##// END OF EJS Templates
debugsetparents: remove redundant invocations of begin/endparentchange...
liscju -
r28654:5474dc73 default
parent child Browse files
Show More
@@ -1,7206 +1,7204 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 __future__ import absolute_import
9 9
10 10 import difflib
11 11 import errno
12 12 import operator
13 13 import os
14 14 import random
15 15 import re
16 16 import shlex
17 17 import socket
18 18 import sys
19 19 import tempfile
20 20 import time
21 21
22 22 from .i18n import _
23 23 from .node import (
24 24 bin,
25 25 hex,
26 26 nullhex,
27 27 nullid,
28 28 nullrev,
29 29 short,
30 30 )
31 31 from . import (
32 32 archival,
33 33 bookmarks,
34 34 bundle2,
35 35 changegroup,
36 36 cmdutil,
37 37 commandserver,
38 38 context,
39 39 copies,
40 40 dagparser,
41 41 dagutil,
42 42 destutil,
43 43 discovery,
44 44 encoding,
45 45 error,
46 46 exchange,
47 47 extensions,
48 48 fileset,
49 49 graphmod,
50 50 hbisect,
51 51 help,
52 52 hg,
53 53 hgweb,
54 54 localrepo,
55 55 lock as lockmod,
56 56 merge as mergemod,
57 57 minirst,
58 58 obsolete,
59 59 patch,
60 60 phases,
61 61 pvec,
62 62 repair,
63 63 revlog,
64 64 revset,
65 65 scmutil,
66 66 setdiscovery,
67 67 simplemerge,
68 68 sshserver,
69 69 streamclone,
70 70 templatekw,
71 71 templater,
72 72 treediscovery,
73 73 ui as uimod,
74 74 util,
75 75 )
76 76
77 77 release = lockmod.release
78 78
79 79 table = {}
80 80
81 81 command = cmdutil.command(table)
82 82
83 83 # label constants
84 84 # until 3.5, bookmarks.current was the advertised name, not
85 85 # bookmarks.active, so we must use both to avoid breaking old
86 86 # custom styles
87 87 activebookmarklabel = 'bookmarks.active bookmarks.current'
88 88
89 89 # common command options
90 90
91 91 globalopts = [
92 92 ('R', 'repository', '',
93 93 _('repository root directory or name of overlay bundle file'),
94 94 _('REPO')),
95 95 ('', 'cwd', '',
96 96 _('change working directory'), _('DIR')),
97 97 ('y', 'noninteractive', None,
98 98 _('do not prompt, automatically pick the first choice for all prompts')),
99 99 ('q', 'quiet', None, _('suppress output')),
100 100 ('v', 'verbose', None, _('enable additional output')),
101 101 ('', 'config', [],
102 102 _('set/override config option (use \'section.name=value\')'),
103 103 _('CONFIG')),
104 104 ('', 'debug', None, _('enable debugging output')),
105 105 ('', 'debugger', None, _('start debugger')),
106 106 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
107 107 _('ENCODE')),
108 108 ('', 'encodingmode', encoding.encodingmode,
109 109 _('set the charset encoding mode'), _('MODE')),
110 110 ('', 'traceback', None, _('always print a traceback on exception')),
111 111 ('', 'time', None, _('time how long the command takes')),
112 112 ('', 'profile', None, _('print command execution profile')),
113 113 ('', 'version', None, _('output version information and exit')),
114 114 ('h', 'help', None, _('display help and exit')),
115 115 ('', 'hidden', False, _('consider hidden changesets')),
116 116 ]
117 117
118 118 dryrunopts = [('n', 'dry-run', None,
119 119 _('do not perform actions, just print output'))]
120 120
121 121 remoteopts = [
122 122 ('e', 'ssh', '',
123 123 _('specify ssh command to use'), _('CMD')),
124 124 ('', 'remotecmd', '',
125 125 _('specify hg command to run on the remote side'), _('CMD')),
126 126 ('', 'insecure', None,
127 127 _('do not verify server certificate (ignoring web.cacerts config)')),
128 128 ]
129 129
130 130 walkopts = [
131 131 ('I', 'include', [],
132 132 _('include names matching the given patterns'), _('PATTERN')),
133 133 ('X', 'exclude', [],
134 134 _('exclude names matching the given patterns'), _('PATTERN')),
135 135 ]
136 136
137 137 commitopts = [
138 138 ('m', 'message', '',
139 139 _('use text as commit message'), _('TEXT')),
140 140 ('l', 'logfile', '',
141 141 _('read commit message from file'), _('FILE')),
142 142 ]
143 143
144 144 commitopts2 = [
145 145 ('d', 'date', '',
146 146 _('record the specified date as commit date'), _('DATE')),
147 147 ('u', 'user', '',
148 148 _('record the specified user as committer'), _('USER')),
149 149 ]
150 150
151 151 # hidden for now
152 152 formatteropts = [
153 153 ('T', 'template', '',
154 154 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
155 155 ]
156 156
157 157 templateopts = [
158 158 ('', 'style', '',
159 159 _('display using template map file (DEPRECATED)'), _('STYLE')),
160 160 ('T', 'template', '',
161 161 _('display with template'), _('TEMPLATE')),
162 162 ]
163 163
164 164 logopts = [
165 165 ('p', 'patch', None, _('show patch')),
166 166 ('g', 'git', None, _('use git extended diff format')),
167 167 ('l', 'limit', '',
168 168 _('limit number of changes displayed'), _('NUM')),
169 169 ('M', 'no-merges', None, _('do not show merges')),
170 170 ('', 'stat', None, _('output diffstat-style summary of changes')),
171 171 ('G', 'graph', None, _("show the revision DAG")),
172 172 ] + templateopts
173 173
174 174 diffopts = [
175 175 ('a', 'text', None, _('treat all files as text')),
176 176 ('g', 'git', None, _('use git extended diff format')),
177 177 ('', 'nodates', None, _('omit dates from diff headers'))
178 178 ]
179 179
180 180 diffwsopts = [
181 181 ('w', 'ignore-all-space', None,
182 182 _('ignore white space when comparing lines')),
183 183 ('b', 'ignore-space-change', None,
184 184 _('ignore changes in the amount of white space')),
185 185 ('B', 'ignore-blank-lines', None,
186 186 _('ignore changes whose lines are all blank')),
187 187 ]
188 188
189 189 diffopts2 = [
190 190 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
191 191 ('p', 'show-function', None, _('show which function each change is in')),
192 192 ('', 'reverse', None, _('produce a diff that undoes the changes')),
193 193 ] + diffwsopts + [
194 194 ('U', 'unified', '',
195 195 _('number of lines of context to show'), _('NUM')),
196 196 ('', 'stat', None, _('output diffstat-style summary of changes')),
197 197 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
198 198 ]
199 199
200 200 mergetoolopts = [
201 201 ('t', 'tool', '', _('specify merge tool')),
202 202 ]
203 203
204 204 similarityopts = [
205 205 ('s', 'similarity', '',
206 206 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
207 207 ]
208 208
209 209 subrepoopts = [
210 210 ('S', 'subrepos', None,
211 211 _('recurse into subrepositories'))
212 212 ]
213 213
214 214 debugrevlogopts = [
215 215 ('c', 'changelog', False, _('open changelog')),
216 216 ('m', 'manifest', False, _('open manifest')),
217 217 ('', 'dir', False, _('open directory manifest')),
218 218 ]
219 219
220 220 # Commands start here, listed alphabetically
221 221
222 222 @command('^add',
223 223 walkopts + subrepoopts + dryrunopts,
224 224 _('[OPTION]... [FILE]...'),
225 225 inferrepo=True)
226 226 def add(ui, repo, *pats, **opts):
227 227 """add the specified files on the next commit
228 228
229 229 Schedule files to be version controlled and added to the
230 230 repository.
231 231
232 232 The files will be added to the repository at the next commit. To
233 233 undo an add before that, see :hg:`forget`.
234 234
235 235 If no names are given, add all files to the repository (except
236 236 files matching ``.hgignore``).
237 237
238 238 .. container:: verbose
239 239
240 240 Examples:
241 241
242 242 - New (unknown) files are added
243 243 automatically by :hg:`add`::
244 244
245 245 $ ls
246 246 foo.c
247 247 $ hg status
248 248 ? foo.c
249 249 $ hg add
250 250 adding foo.c
251 251 $ hg status
252 252 A foo.c
253 253
254 254 - Specific files to be added can be specified::
255 255
256 256 $ ls
257 257 bar.c foo.c
258 258 $ hg status
259 259 ? bar.c
260 260 ? foo.c
261 261 $ hg add bar.c
262 262 $ hg status
263 263 A bar.c
264 264 ? foo.c
265 265
266 266 Returns 0 if all files are successfully added.
267 267 """
268 268
269 269 m = scmutil.match(repo[None], pats, opts)
270 270 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
271 271 return rejected and 1 or 0
272 272
273 273 @command('addremove',
274 274 similarityopts + subrepoopts + walkopts + dryrunopts,
275 275 _('[OPTION]... [FILE]...'),
276 276 inferrepo=True)
277 277 def addremove(ui, repo, *pats, **opts):
278 278 """add all new files, delete all missing files
279 279
280 280 Add all new files and remove all missing files from the
281 281 repository.
282 282
283 283 Unless names are given, new files are ignored if they match any of
284 284 the patterns in ``.hgignore``. As with add, these changes take
285 285 effect at the next commit.
286 286
287 287 Use the -s/--similarity option to detect renamed files. This
288 288 option takes a percentage between 0 (disabled) and 100 (files must
289 289 be identical) as its parameter. With a parameter greater than 0,
290 290 this compares every removed file with every added file and records
291 291 those similar enough as renames. Detecting renamed files this way
292 292 can be expensive. After using this option, :hg:`status -C` can be
293 293 used to check which files were identified as moved or renamed. If
294 294 not specified, -s/--similarity defaults to 100 and only renames of
295 295 identical files are detected.
296 296
297 297 .. container:: verbose
298 298
299 299 Examples:
300 300
301 301 - A number of files (bar.c and foo.c) are new,
302 302 while foobar.c has been removed (without using :hg:`remove`)
303 303 from the repository::
304 304
305 305 $ ls
306 306 bar.c foo.c
307 307 $ hg status
308 308 ! foobar.c
309 309 ? bar.c
310 310 ? foo.c
311 311 $ hg addremove
312 312 adding bar.c
313 313 adding foo.c
314 314 removing foobar.c
315 315 $ hg status
316 316 A bar.c
317 317 A foo.c
318 318 R foobar.c
319 319
320 320 - A file foobar.c was moved to foo.c without using :hg:`rename`.
321 321 Afterwards, it was edited slightly::
322 322
323 323 $ ls
324 324 foo.c
325 325 $ hg status
326 326 ! foobar.c
327 327 ? foo.c
328 328 $ hg addremove --similarity 90
329 329 removing foobar.c
330 330 adding foo.c
331 331 recording removal of foobar.c as rename to foo.c (94% similar)
332 332 $ hg status -C
333 333 A foo.c
334 334 foobar.c
335 335 R foobar.c
336 336
337 337 Returns 0 if all files are successfully added.
338 338 """
339 339 try:
340 340 sim = float(opts.get('similarity') or 100)
341 341 except ValueError:
342 342 raise error.Abort(_('similarity must be a number'))
343 343 if sim < 0 or sim > 100:
344 344 raise error.Abort(_('similarity must be between 0 and 100'))
345 345 matcher = scmutil.match(repo[None], pats, opts)
346 346 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
347 347
348 348 @command('^annotate|blame',
349 349 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
350 350 ('', 'follow', None,
351 351 _('follow copies/renames and list the filename (DEPRECATED)')),
352 352 ('', 'no-follow', None, _("don't follow copies and renames")),
353 353 ('a', 'text', None, _('treat all files as text')),
354 354 ('u', 'user', None, _('list the author (long with -v)')),
355 355 ('f', 'file', None, _('list the filename')),
356 356 ('d', 'date', None, _('list the date (short with -q)')),
357 357 ('n', 'number', None, _('list the revision number (default)')),
358 358 ('c', 'changeset', None, _('list the changeset')),
359 359 ('l', 'line-number', None, _('show line number at the first appearance'))
360 360 ] + diffwsopts + walkopts + formatteropts,
361 361 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
362 362 inferrepo=True)
363 363 def annotate(ui, repo, *pats, **opts):
364 364 """show changeset information by line for each file
365 365
366 366 List changes in files, showing the revision id responsible for
367 367 each line.
368 368
369 369 This command is useful for discovering when a change was made and
370 370 by whom.
371 371
372 372 If you include --file, --user, or --date, the revision number is
373 373 suppressed unless you also include --number.
374 374
375 375 Without the -a/--text option, annotate will avoid processing files
376 376 it detects as binary. With -a, annotate will annotate the file
377 377 anyway, although the results will probably be neither useful
378 378 nor desirable.
379 379
380 380 Returns 0 on success.
381 381 """
382 382 if not pats:
383 383 raise error.Abort(_('at least one filename or pattern is required'))
384 384
385 385 if opts.get('follow'):
386 386 # --follow is deprecated and now just an alias for -f/--file
387 387 # to mimic the behavior of Mercurial before version 1.5
388 388 opts['file'] = True
389 389
390 390 ctx = scmutil.revsingle(repo, opts.get('rev'))
391 391
392 392 fm = ui.formatter('annotate', opts)
393 393 if ui.quiet:
394 394 datefunc = util.shortdate
395 395 else:
396 396 datefunc = util.datestr
397 397 if ctx.rev() is None:
398 398 def hexfn(node):
399 399 if node is None:
400 400 return None
401 401 else:
402 402 return fm.hexfunc(node)
403 403 if opts.get('changeset'):
404 404 # omit "+" suffix which is appended to node hex
405 405 def formatrev(rev):
406 406 if rev is None:
407 407 return '%d' % ctx.p1().rev()
408 408 else:
409 409 return '%d' % rev
410 410 else:
411 411 def formatrev(rev):
412 412 if rev is None:
413 413 return '%d+' % ctx.p1().rev()
414 414 else:
415 415 return '%d ' % rev
416 416 def formathex(hex):
417 417 if hex is None:
418 418 return '%s+' % fm.hexfunc(ctx.p1().node())
419 419 else:
420 420 return '%s ' % hex
421 421 else:
422 422 hexfn = fm.hexfunc
423 423 formatrev = formathex = str
424 424
425 425 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
426 426 ('number', ' ', lambda x: x[0].rev(), formatrev),
427 427 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
428 428 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
429 429 ('file', ' ', lambda x: x[0].path(), str),
430 430 ('line_number', ':', lambda x: x[1], str),
431 431 ]
432 432 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
433 433
434 434 if (not opts.get('user') and not opts.get('changeset')
435 435 and not opts.get('date') and not opts.get('file')):
436 436 opts['number'] = True
437 437
438 438 linenumber = opts.get('line_number') is not None
439 439 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
440 440 raise error.Abort(_('at least one of -n/-c is required for -l'))
441 441
442 442 if fm:
443 443 def makefunc(get, fmt):
444 444 return get
445 445 else:
446 446 def makefunc(get, fmt):
447 447 return lambda x: fmt(get(x))
448 448 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
449 449 if opts.get(op)]
450 450 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
451 451 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
452 452 if opts.get(op))
453 453
454 454 def bad(x, y):
455 455 raise error.Abort("%s: %s" % (x, y))
456 456
457 457 m = scmutil.match(ctx, pats, opts, badfn=bad)
458 458
459 459 follow = not opts.get('no_follow')
460 460 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
461 461 whitespace=True)
462 462 for abs in ctx.walk(m):
463 463 fctx = ctx[abs]
464 464 if not opts.get('text') and util.binary(fctx.data()):
465 465 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
466 466 continue
467 467
468 468 lines = fctx.annotate(follow=follow, linenumber=linenumber,
469 469 diffopts=diffopts)
470 470 formats = []
471 471 pieces = []
472 472
473 473 for f, sep in funcmap:
474 474 l = [f(n) for n, dummy in lines]
475 475 if l:
476 476 if fm:
477 477 formats.append(['%s' for x in l])
478 478 else:
479 479 sizes = [encoding.colwidth(x) for x in l]
480 480 ml = max(sizes)
481 481 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
482 482 pieces.append(l)
483 483
484 484 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
485 485 fm.startitem()
486 486 fm.write(fields, "".join(f), *p)
487 487 fm.write('line', ": %s", l[1])
488 488
489 489 if lines and not lines[-1][1].endswith('\n'):
490 490 fm.plain('\n')
491 491
492 492 fm.end()
493 493
494 494 @command('archive',
495 495 [('', 'no-decode', None, _('do not pass files through decoders')),
496 496 ('p', 'prefix', '', _('directory prefix for files in archive'),
497 497 _('PREFIX')),
498 498 ('r', 'rev', '', _('revision to distribute'), _('REV')),
499 499 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
500 500 ] + subrepoopts + walkopts,
501 501 _('[OPTION]... DEST'))
502 502 def archive(ui, repo, dest, **opts):
503 503 '''create an unversioned archive of a repository revision
504 504
505 505 By default, the revision used is the parent of the working
506 506 directory; use -r/--rev to specify a different revision.
507 507
508 508 The archive type is automatically detected based on file
509 509 extension (to override, use -t/--type).
510 510
511 511 .. container:: verbose
512 512
513 513 Examples:
514 514
515 515 - create a zip file containing the 1.0 release::
516 516
517 517 hg archive -r 1.0 project-1.0.zip
518 518
519 519 - create a tarball excluding .hg files::
520 520
521 521 hg archive project.tar.gz -X ".hg*"
522 522
523 523 Valid types are:
524 524
525 525 :``files``: a directory full of files (default)
526 526 :``tar``: tar archive, uncompressed
527 527 :``tbz2``: tar archive, compressed using bzip2
528 528 :``tgz``: tar archive, compressed using gzip
529 529 :``uzip``: zip archive, uncompressed
530 530 :``zip``: zip archive, compressed using deflate
531 531
532 532 The exact name of the destination archive or directory is given
533 533 using a format string; see :hg:`help export` for details.
534 534
535 535 Each member added to an archive file has a directory prefix
536 536 prepended. Use -p/--prefix to specify a format string for the
537 537 prefix. The default is the basename of the archive, with suffixes
538 538 removed.
539 539
540 540 Returns 0 on success.
541 541 '''
542 542
543 543 ctx = scmutil.revsingle(repo, opts.get('rev'))
544 544 if not ctx:
545 545 raise error.Abort(_('no working directory: please specify a revision'))
546 546 node = ctx.node()
547 547 dest = cmdutil.makefilename(repo, dest, node)
548 548 if os.path.realpath(dest) == repo.root:
549 549 raise error.Abort(_('repository root cannot be destination'))
550 550
551 551 kind = opts.get('type') or archival.guesskind(dest) or 'files'
552 552 prefix = opts.get('prefix')
553 553
554 554 if dest == '-':
555 555 if kind == 'files':
556 556 raise error.Abort(_('cannot archive plain files to stdout'))
557 557 dest = cmdutil.makefileobj(repo, dest)
558 558 if not prefix:
559 559 prefix = os.path.basename(repo.root) + '-%h'
560 560
561 561 prefix = cmdutil.makefilename(repo, prefix, node)
562 562 matchfn = scmutil.match(ctx, [], opts)
563 563 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
564 564 matchfn, prefix, subrepos=opts.get('subrepos'))
565 565
566 566 @command('backout',
567 567 [('', 'merge', None, _('merge with old dirstate parent after backout')),
568 568 ('', 'commit', None,
569 569 _('commit if no conflicts were encountered (DEPRECATED)')),
570 570 ('', 'no-commit', None, _('do not commit')),
571 571 ('', 'parent', '',
572 572 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
573 573 ('r', 'rev', '', _('revision to backout'), _('REV')),
574 574 ('e', 'edit', False, _('invoke editor on commit messages')),
575 575 ] + mergetoolopts + walkopts + commitopts + commitopts2,
576 576 _('[OPTION]... [-r] REV'))
577 577 def backout(ui, repo, node=None, rev=None, **opts):
578 578 '''reverse effect of earlier changeset
579 579
580 580 Prepare a new changeset with the effect of REV undone in the
581 581 current working directory. If no conflicts were encountered,
582 582 it will be committed immediately.
583 583
584 584 If REV is the parent of the working directory, then this new changeset
585 585 is committed automatically (unless --no-commit is specified).
586 586
587 587 .. note::
588 588
589 589 :hg:`backout` cannot be used to fix either an unwanted or
590 590 incorrect merge.
591 591
592 592 .. container:: verbose
593 593
594 594 Examples:
595 595
596 596 - Reverse the effect of the parent of the working directory.
597 597 This backout will be committed immediately::
598 598
599 599 hg backout -r .
600 600
601 601 - Reverse the effect of previous bad revision 23::
602 602
603 603 hg backout -r 23
604 604
605 605 - Reverse the effect of previous bad revision 23 and
606 606 leave changes uncommitted::
607 607
608 608 hg backout -r 23 --no-commit
609 609 hg commit -m "Backout revision 23"
610 610
611 611 By default, the pending changeset will have one parent,
612 612 maintaining a linear history. With --merge, the pending
613 613 changeset will instead have two parents: the old parent of the
614 614 working directory and a new child of REV that simply undoes REV.
615 615
616 616 Before version 1.7, the behavior without --merge was equivalent
617 617 to specifying --merge followed by :hg:`update --clean .` to
618 618 cancel the merge and leave the child of REV as a head to be
619 619 merged separately.
620 620
621 621 See :hg:`help dates` for a list of formats valid for -d/--date.
622 622
623 623 See :hg:`help revert` for a way to restore files to the state
624 624 of another revision.
625 625
626 626 Returns 0 on success, 1 if nothing to backout or there are unresolved
627 627 files.
628 628 '''
629 629 wlock = lock = None
630 630 try:
631 631 wlock = repo.wlock()
632 632 lock = repo.lock()
633 633 return _dobackout(ui, repo, node, rev, **opts)
634 634 finally:
635 635 release(lock, wlock)
636 636
637 637 def _dobackout(ui, repo, node=None, rev=None, **opts):
638 638 if opts.get('commit') and opts.get('no_commit'):
639 639 raise error.Abort(_("cannot use --commit with --no-commit"))
640 640 if opts.get('merge') and opts.get('no_commit'):
641 641 raise error.Abort(_("cannot use --merge with --no-commit"))
642 642
643 643 if rev and node:
644 644 raise error.Abort(_("please specify just one revision"))
645 645
646 646 if not rev:
647 647 rev = node
648 648
649 649 if not rev:
650 650 raise error.Abort(_("please specify a revision to backout"))
651 651
652 652 date = opts.get('date')
653 653 if date:
654 654 opts['date'] = util.parsedate(date)
655 655
656 656 cmdutil.checkunfinished(repo)
657 657 cmdutil.bailifchanged(repo)
658 658 node = scmutil.revsingle(repo, rev).node()
659 659
660 660 op1, op2 = repo.dirstate.parents()
661 661 if not repo.changelog.isancestor(node, op1):
662 662 raise error.Abort(_('cannot backout change that is not an ancestor'))
663 663
664 664 p1, p2 = repo.changelog.parents(node)
665 665 if p1 == nullid:
666 666 raise error.Abort(_('cannot backout a change with no parents'))
667 667 if p2 != nullid:
668 668 if not opts.get('parent'):
669 669 raise error.Abort(_('cannot backout a merge changeset'))
670 670 p = repo.lookup(opts['parent'])
671 671 if p not in (p1, p2):
672 672 raise error.Abort(_('%s is not a parent of %s') %
673 673 (short(p), short(node)))
674 674 parent = p
675 675 else:
676 676 if opts.get('parent'):
677 677 raise error.Abort(_('cannot use --parent on non-merge changeset'))
678 678 parent = p1
679 679
680 680 # the backout should appear on the same branch
681 681 branch = repo.dirstate.branch()
682 682 bheads = repo.branchheads(branch)
683 683 rctx = scmutil.revsingle(repo, hex(parent))
684 684 if not opts.get('merge') and op1 != node:
685 685 dsguard = cmdutil.dirstateguard(repo, 'backout')
686 686 try:
687 687 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
688 688 'backout')
689 689 stats = mergemod.update(repo, parent, True, True, node, False)
690 690 repo.setparents(op1, op2)
691 691 dsguard.close()
692 692 hg._showstats(repo, stats)
693 693 if stats[3]:
694 694 repo.ui.status(_("use 'hg resolve' to retry unresolved "
695 695 "file merges\n"))
696 696 return 1
697 697 finally:
698 698 ui.setconfig('ui', 'forcemerge', '', '')
699 699 lockmod.release(dsguard)
700 700 else:
701 701 hg.clean(repo, node, show_stats=False)
702 702 repo.dirstate.setbranch(branch)
703 703 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
704 704
705 705 if opts.get('no_commit'):
706 706 msg = _("changeset %s backed out, "
707 707 "don't forget to commit.\n")
708 708 ui.status(msg % short(node))
709 709 return 0
710 710
711 711 def commitfunc(ui, repo, message, match, opts):
712 712 editform = 'backout'
713 713 e = cmdutil.getcommiteditor(editform=editform, **opts)
714 714 if not message:
715 715 # we don't translate commit messages
716 716 message = "Backed out changeset %s" % short(node)
717 717 e = cmdutil.getcommiteditor(edit=True, editform=editform)
718 718 return repo.commit(message, opts.get('user'), opts.get('date'),
719 719 match, editor=e)
720 720 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
721 721 if not newnode:
722 722 ui.status(_("nothing changed\n"))
723 723 return 1
724 724 cmdutil.commitstatus(repo, newnode, branch, bheads)
725 725
726 726 def nice(node):
727 727 return '%d:%s' % (repo.changelog.rev(node), short(node))
728 728 ui.status(_('changeset %s backs out changeset %s\n') %
729 729 (nice(repo.changelog.tip()), nice(node)))
730 730 if opts.get('merge') and op1 != node:
731 731 hg.clean(repo, op1, show_stats=False)
732 732 ui.status(_('merging with changeset %s\n')
733 733 % nice(repo.changelog.tip()))
734 734 try:
735 735 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
736 736 'backout')
737 737 return hg.merge(repo, hex(repo.changelog.tip()))
738 738 finally:
739 739 ui.setconfig('ui', 'forcemerge', '', '')
740 740 return 0
741 741
742 742 @command('bisect',
743 743 [('r', 'reset', False, _('reset bisect state')),
744 744 ('g', 'good', False, _('mark changeset good')),
745 745 ('b', 'bad', False, _('mark changeset bad')),
746 746 ('s', 'skip', False, _('skip testing changeset')),
747 747 ('e', 'extend', False, _('extend the bisect range')),
748 748 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
749 749 ('U', 'noupdate', False, _('do not update to target'))],
750 750 _("[-gbsr] [-U] [-c CMD] [REV]"))
751 751 def bisect(ui, repo, rev=None, extra=None, command=None,
752 752 reset=None, good=None, bad=None, skip=None, extend=None,
753 753 noupdate=None):
754 754 """subdivision search of changesets
755 755
756 756 This command helps to find changesets which introduce problems. To
757 757 use, mark the earliest changeset you know exhibits the problem as
758 758 bad, then mark the latest changeset which is free from the problem
759 759 as good. Bisect will update your working directory to a revision
760 760 for testing (unless the -U/--noupdate option is specified). Once
761 761 you have performed tests, mark the working directory as good or
762 762 bad, and bisect will either update to another candidate changeset
763 763 or announce that it has found the bad revision.
764 764
765 765 As a shortcut, you can also use the revision argument to mark a
766 766 revision as good or bad without checking it out first.
767 767
768 768 If you supply a command, it will be used for automatic bisection.
769 769 The environment variable HG_NODE will contain the ID of the
770 770 changeset being tested. The exit status of the command will be
771 771 used to mark revisions as good or bad: status 0 means good, 125
772 772 means to skip the revision, 127 (command not found) will abort the
773 773 bisection, and any other non-zero exit status means the revision
774 774 is bad.
775 775
776 776 .. container:: verbose
777 777
778 778 Some examples:
779 779
780 780 - start a bisection with known bad revision 34, and good revision 12::
781 781
782 782 hg bisect --bad 34
783 783 hg bisect --good 12
784 784
785 785 - advance the current bisection by marking current revision as good or
786 786 bad::
787 787
788 788 hg bisect --good
789 789 hg bisect --bad
790 790
791 791 - mark the current revision, or a known revision, to be skipped (e.g. if
792 792 that revision is not usable because of another issue)::
793 793
794 794 hg bisect --skip
795 795 hg bisect --skip 23
796 796
797 797 - skip all revisions that do not touch directories ``foo`` or ``bar``::
798 798
799 799 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
800 800
801 801 - forget the current bisection::
802 802
803 803 hg bisect --reset
804 804
805 805 - use 'make && make tests' to automatically find the first broken
806 806 revision::
807 807
808 808 hg bisect --reset
809 809 hg bisect --bad 34
810 810 hg bisect --good 12
811 811 hg bisect --command "make && make tests"
812 812
813 813 - see all changesets whose states are already known in the current
814 814 bisection::
815 815
816 816 hg log -r "bisect(pruned)"
817 817
818 818 - see the changeset currently being bisected (especially useful
819 819 if running with -U/--noupdate)::
820 820
821 821 hg log -r "bisect(current)"
822 822
823 823 - see all changesets that took part in the current bisection::
824 824
825 825 hg log -r "bisect(range)"
826 826
827 827 - you can even get a nice graph::
828 828
829 829 hg log --graph -r "bisect(range)"
830 830
831 831 See :hg:`help revsets` for more about the `bisect()` keyword.
832 832
833 833 Returns 0 on success.
834 834 """
835 835 def extendbisectrange(nodes, good):
836 836 # bisect is incomplete when it ends on a merge node and
837 837 # one of the parent was not checked.
838 838 parents = repo[nodes[0]].parents()
839 839 if len(parents) > 1:
840 840 if good:
841 841 side = state['bad']
842 842 else:
843 843 side = state['good']
844 844 num = len(set(i.node() for i in parents) & set(side))
845 845 if num == 1:
846 846 return parents[0].ancestor(parents[1])
847 847 return None
848 848
849 849 def print_result(nodes, good):
850 850 displayer = cmdutil.show_changeset(ui, repo, {})
851 851 if len(nodes) == 1:
852 852 # narrowed it down to a single revision
853 853 if good:
854 854 ui.write(_("The first good revision is:\n"))
855 855 else:
856 856 ui.write(_("The first bad revision is:\n"))
857 857 displayer.show(repo[nodes[0]])
858 858 extendnode = extendbisectrange(nodes, good)
859 859 if extendnode is not None:
860 860 ui.write(_('Not all ancestors of this changeset have been'
861 861 ' checked.\nUse bisect --extend to continue the '
862 862 'bisection from\nthe common ancestor, %s.\n')
863 863 % extendnode)
864 864 else:
865 865 # multiple possible revisions
866 866 if good:
867 867 ui.write(_("Due to skipped revisions, the first "
868 868 "good revision could be any of:\n"))
869 869 else:
870 870 ui.write(_("Due to skipped revisions, the first "
871 871 "bad revision could be any of:\n"))
872 872 for n in nodes:
873 873 displayer.show(repo[n])
874 874 displayer.close()
875 875
876 876 def check_state(state, interactive=True):
877 877 if not state['good'] or not state['bad']:
878 878 if (good or bad or skip or reset) and interactive:
879 879 return
880 880 if not state['good']:
881 881 raise error.Abort(_('cannot bisect (no known good revisions)'))
882 882 else:
883 883 raise error.Abort(_('cannot bisect (no known bad revisions)'))
884 884 return True
885 885
886 886 # backward compatibility
887 887 if rev in "good bad reset init".split():
888 888 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
889 889 cmd, rev, extra = rev, extra, None
890 890 if cmd == "good":
891 891 good = True
892 892 elif cmd == "bad":
893 893 bad = True
894 894 else:
895 895 reset = True
896 896 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
897 897 raise error.Abort(_('incompatible arguments'))
898 898
899 899 cmdutil.checkunfinished(repo)
900 900
901 901 if reset:
902 902 p = repo.join("bisect.state")
903 903 if os.path.exists(p):
904 904 os.unlink(p)
905 905 return
906 906
907 907 state = hbisect.load_state(repo)
908 908
909 909 if command:
910 910 changesets = 1
911 911 if noupdate:
912 912 try:
913 913 node = state['current'][0]
914 914 except LookupError:
915 915 raise error.Abort(_('current bisect revision is unknown - '
916 916 'start a new bisect to fix'))
917 917 else:
918 918 node, p2 = repo.dirstate.parents()
919 919 if p2 != nullid:
920 920 raise error.Abort(_('current bisect revision is a merge'))
921 921 try:
922 922 while changesets:
923 923 # update state
924 924 state['current'] = [node]
925 925 hbisect.save_state(repo, state)
926 926 status = ui.system(command, environ={'HG_NODE': hex(node)})
927 927 if status == 125:
928 928 transition = "skip"
929 929 elif status == 0:
930 930 transition = "good"
931 931 # status < 0 means process was killed
932 932 elif status == 127:
933 933 raise error.Abort(_("failed to execute %s") % command)
934 934 elif status < 0:
935 935 raise error.Abort(_("%s killed") % command)
936 936 else:
937 937 transition = "bad"
938 938 ctx = scmutil.revsingle(repo, rev, node)
939 939 rev = None # clear for future iterations
940 940 state[transition].append(ctx.node())
941 941 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
942 942 check_state(state, interactive=False)
943 943 # bisect
944 944 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
945 945 # update to next check
946 946 node = nodes[0]
947 947 if not noupdate:
948 948 cmdutil.bailifchanged(repo)
949 949 hg.clean(repo, node, show_stats=False)
950 950 finally:
951 951 state['current'] = [node]
952 952 hbisect.save_state(repo, state)
953 953 print_result(nodes, bgood)
954 954 return
955 955
956 956 # update state
957 957
958 958 if rev:
959 959 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
960 960 else:
961 961 nodes = [repo.lookup('.')]
962 962
963 963 if good or bad or skip:
964 964 if good:
965 965 state['good'] += nodes
966 966 elif bad:
967 967 state['bad'] += nodes
968 968 elif skip:
969 969 state['skip'] += nodes
970 970 hbisect.save_state(repo, state)
971 971
972 972 if not check_state(state):
973 973 return
974 974
975 975 # actually bisect
976 976 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
977 977 if extend:
978 978 if not changesets:
979 979 extendnode = extendbisectrange(nodes, good)
980 980 if extendnode is not None:
981 981 ui.write(_("Extending search to changeset %d:%s\n")
982 982 % (extendnode.rev(), extendnode))
983 983 state['current'] = [extendnode.node()]
984 984 hbisect.save_state(repo, state)
985 985 if noupdate:
986 986 return
987 987 cmdutil.bailifchanged(repo)
988 988 return hg.clean(repo, extendnode.node())
989 989 raise error.Abort(_("nothing to extend"))
990 990
991 991 if changesets == 0:
992 992 print_result(nodes, good)
993 993 else:
994 994 assert len(nodes) == 1 # only a single node can be tested next
995 995 node = nodes[0]
996 996 # compute the approximate number of remaining tests
997 997 tests, size = 0, 2
998 998 while size <= changesets:
999 999 tests, size = tests + 1, size * 2
1000 1000 rev = repo.changelog.rev(node)
1001 1001 ui.write(_("Testing changeset %d:%s "
1002 1002 "(%d changesets remaining, ~%d tests)\n")
1003 1003 % (rev, short(node), changesets, tests))
1004 1004 state['current'] = [node]
1005 1005 hbisect.save_state(repo, state)
1006 1006 if not noupdate:
1007 1007 cmdutil.bailifchanged(repo)
1008 1008 return hg.clean(repo, node)
1009 1009
1010 1010 @command('bookmarks|bookmark',
1011 1011 [('f', 'force', False, _('force')),
1012 1012 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
1013 1013 ('d', 'delete', False, _('delete a given bookmark')),
1014 1014 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
1015 1015 ('i', 'inactive', False, _('mark a bookmark inactive')),
1016 1016 ] + formatteropts,
1017 1017 _('hg bookmarks [OPTIONS]... [NAME]...'))
1018 1018 def bookmark(ui, repo, *names, **opts):
1019 1019 '''create a new bookmark or list existing bookmarks
1020 1020
1021 1021 Bookmarks are labels on changesets to help track lines of development.
1022 1022 Bookmarks are unversioned and can be moved, renamed and deleted.
1023 1023 Deleting or moving a bookmark has no effect on the associated changesets.
1024 1024
1025 1025 Creating or updating to a bookmark causes it to be marked as 'active'.
1026 1026 The active bookmark is indicated with a '*'.
1027 1027 When a commit is made, the active bookmark will advance to the new commit.
1028 1028 A plain :hg:`update` will also advance an active bookmark, if possible.
1029 1029 Updating away from a bookmark will cause it to be deactivated.
1030 1030
1031 1031 Bookmarks can be pushed and pulled between repositories (see
1032 1032 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1033 1033 diverged, a new 'divergent bookmark' of the form 'name@path' will
1034 1034 be created. Using :hg:`merge` will resolve the divergence.
1035 1035
1036 1036 A bookmark named '@' has the special property that :hg:`clone` will
1037 1037 check it out by default if it exists.
1038 1038
1039 1039 .. container:: verbose
1040 1040
1041 1041 Examples:
1042 1042
1043 1043 - create an active bookmark for a new line of development::
1044 1044
1045 1045 hg book new-feature
1046 1046
1047 1047 - create an inactive bookmark as a place marker::
1048 1048
1049 1049 hg book -i reviewed
1050 1050
1051 1051 - create an inactive bookmark on another changeset::
1052 1052
1053 1053 hg book -r .^ tested
1054 1054
1055 1055 - rename bookmark turkey to dinner::
1056 1056
1057 1057 hg book -m turkey dinner
1058 1058
1059 1059 - move the '@' bookmark from another branch::
1060 1060
1061 1061 hg book -f @
1062 1062 '''
1063 1063 force = opts.get('force')
1064 1064 rev = opts.get('rev')
1065 1065 delete = opts.get('delete')
1066 1066 rename = opts.get('rename')
1067 1067 inactive = opts.get('inactive')
1068 1068
1069 1069 def checkformat(mark):
1070 1070 mark = mark.strip()
1071 1071 if not mark:
1072 1072 raise error.Abort(_("bookmark names cannot consist entirely of "
1073 1073 "whitespace"))
1074 1074 scmutil.checknewlabel(repo, mark, 'bookmark')
1075 1075 return mark
1076 1076
1077 1077 def checkconflict(repo, mark, cur, force=False, target=None):
1078 1078 if mark in marks and not force:
1079 1079 if target:
1080 1080 if marks[mark] == target and target == cur:
1081 1081 # re-activating a bookmark
1082 1082 return
1083 1083 anc = repo.changelog.ancestors([repo[target].rev()])
1084 1084 bmctx = repo[marks[mark]]
1085 1085 divs = [repo[b].node() for b in marks
1086 1086 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
1087 1087
1088 1088 # allow resolving a single divergent bookmark even if moving
1089 1089 # the bookmark across branches when a revision is specified
1090 1090 # that contains a divergent bookmark
1091 1091 if bmctx.rev() not in anc and target in divs:
1092 1092 bookmarks.deletedivergent(repo, [target], mark)
1093 1093 return
1094 1094
1095 1095 deletefrom = [b for b in divs
1096 1096 if repo[b].rev() in anc or b == target]
1097 1097 bookmarks.deletedivergent(repo, deletefrom, mark)
1098 1098 if bookmarks.validdest(repo, bmctx, repo[target]):
1099 1099 ui.status(_("moving bookmark '%s' forward from %s\n") %
1100 1100 (mark, short(bmctx.node())))
1101 1101 return
1102 1102 raise error.Abort(_("bookmark '%s' already exists "
1103 1103 "(use -f to force)") % mark)
1104 1104 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
1105 1105 and not force):
1106 1106 raise error.Abort(
1107 1107 _("a bookmark cannot have the name of an existing branch"))
1108 1108
1109 1109 if delete and rename:
1110 1110 raise error.Abort(_("--delete and --rename are incompatible"))
1111 1111 if delete and rev:
1112 1112 raise error.Abort(_("--rev is incompatible with --delete"))
1113 1113 if rename and rev:
1114 1114 raise error.Abort(_("--rev is incompatible with --rename"))
1115 1115 if not names and (delete or rev):
1116 1116 raise error.Abort(_("bookmark name required"))
1117 1117
1118 1118 if delete or rename or names or inactive:
1119 1119 wlock = lock = tr = None
1120 1120 try:
1121 1121 wlock = repo.wlock()
1122 1122 lock = repo.lock()
1123 1123 cur = repo.changectx('.').node()
1124 1124 marks = repo._bookmarks
1125 1125 if delete:
1126 1126 tr = repo.transaction('bookmark')
1127 1127 for mark in names:
1128 1128 if mark not in marks:
1129 1129 raise error.Abort(_("bookmark '%s' does not exist") %
1130 1130 mark)
1131 1131 if mark == repo._activebookmark:
1132 1132 bookmarks.deactivate(repo)
1133 1133 del marks[mark]
1134 1134
1135 1135 elif rename:
1136 1136 tr = repo.transaction('bookmark')
1137 1137 if not names:
1138 1138 raise error.Abort(_("new bookmark name required"))
1139 1139 elif len(names) > 1:
1140 1140 raise error.Abort(_("only one new bookmark name allowed"))
1141 1141 mark = checkformat(names[0])
1142 1142 if rename not in marks:
1143 1143 raise error.Abort(_("bookmark '%s' does not exist")
1144 1144 % rename)
1145 1145 checkconflict(repo, mark, cur, force)
1146 1146 marks[mark] = marks[rename]
1147 1147 if repo._activebookmark == rename and not inactive:
1148 1148 bookmarks.activate(repo, mark)
1149 1149 del marks[rename]
1150 1150 elif names:
1151 1151 tr = repo.transaction('bookmark')
1152 1152 newact = None
1153 1153 for mark in names:
1154 1154 mark = checkformat(mark)
1155 1155 if newact is None:
1156 1156 newact = mark
1157 1157 if inactive and mark == repo._activebookmark:
1158 1158 bookmarks.deactivate(repo)
1159 1159 return
1160 1160 tgt = cur
1161 1161 if rev:
1162 1162 tgt = scmutil.revsingle(repo, rev).node()
1163 1163 checkconflict(repo, mark, cur, force, tgt)
1164 1164 marks[mark] = tgt
1165 1165 if not inactive and cur == marks[newact] and not rev:
1166 1166 bookmarks.activate(repo, newact)
1167 1167 elif cur != tgt and newact == repo._activebookmark:
1168 1168 bookmarks.deactivate(repo)
1169 1169 elif inactive:
1170 1170 if len(marks) == 0:
1171 1171 ui.status(_("no bookmarks set\n"))
1172 1172 elif not repo._activebookmark:
1173 1173 ui.status(_("no active bookmark\n"))
1174 1174 else:
1175 1175 bookmarks.deactivate(repo)
1176 1176 if tr is not None:
1177 1177 marks.recordchange(tr)
1178 1178 tr.close()
1179 1179 finally:
1180 1180 lockmod.release(tr, lock, wlock)
1181 1181 else: # show bookmarks
1182 1182 fm = ui.formatter('bookmarks', opts)
1183 1183 hexfn = fm.hexfunc
1184 1184 marks = repo._bookmarks
1185 1185 if len(marks) == 0 and not fm:
1186 1186 ui.status(_("no bookmarks set\n"))
1187 1187 for bmark, n in sorted(marks.iteritems()):
1188 1188 active = repo._activebookmark
1189 1189 if bmark == active:
1190 1190 prefix, label = '*', activebookmarklabel
1191 1191 else:
1192 1192 prefix, label = ' ', ''
1193 1193
1194 1194 fm.startitem()
1195 1195 if not ui.quiet:
1196 1196 fm.plain(' %s ' % prefix, label=label)
1197 1197 fm.write('bookmark', '%s', bmark, label=label)
1198 1198 pad = " " * (25 - encoding.colwidth(bmark))
1199 1199 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1200 1200 repo.changelog.rev(n), hexfn(n), label=label)
1201 1201 fm.data(active=(bmark == active))
1202 1202 fm.plain('\n')
1203 1203 fm.end()
1204 1204
1205 1205 @command('branch',
1206 1206 [('f', 'force', None,
1207 1207 _('set branch name even if it shadows an existing branch')),
1208 1208 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1209 1209 _('[-fC] [NAME]'))
1210 1210 def branch(ui, repo, label=None, **opts):
1211 1211 """set or show the current branch name
1212 1212
1213 1213 .. note::
1214 1214
1215 1215 Branch names are permanent and global. Use :hg:`bookmark` to create a
1216 1216 light-weight bookmark instead. See :hg:`help glossary` for more
1217 1217 information about named branches and bookmarks.
1218 1218
1219 1219 With no argument, show the current branch name. With one argument,
1220 1220 set the working directory branch name (the branch will not exist
1221 1221 in the repository until the next commit). Standard practice
1222 1222 recommends that primary development take place on the 'default'
1223 1223 branch.
1224 1224
1225 1225 Unless -f/--force is specified, branch will not let you set a
1226 1226 branch name that already exists.
1227 1227
1228 1228 Use -C/--clean to reset the working directory branch to that of
1229 1229 the parent of the working directory, negating a previous branch
1230 1230 change.
1231 1231
1232 1232 Use the command :hg:`update` to switch to an existing branch. Use
1233 1233 :hg:`commit --close-branch` to mark this branch head as closed.
1234 1234 When all heads of a branch are closed, the branch will be
1235 1235 considered closed.
1236 1236
1237 1237 Returns 0 on success.
1238 1238 """
1239 1239 if label:
1240 1240 label = label.strip()
1241 1241
1242 1242 if not opts.get('clean') and not label:
1243 1243 ui.write("%s\n" % repo.dirstate.branch())
1244 1244 return
1245 1245
1246 1246 with repo.wlock():
1247 1247 if opts.get('clean'):
1248 1248 label = repo[None].p1().branch()
1249 1249 repo.dirstate.setbranch(label)
1250 1250 ui.status(_('reset working directory to branch %s\n') % label)
1251 1251 elif label:
1252 1252 if not opts.get('force') and label in repo.branchmap():
1253 1253 if label not in [p.branch() for p in repo[None].parents()]:
1254 1254 raise error.Abort(_('a branch of the same name already'
1255 1255 ' exists'),
1256 1256 # i18n: "it" refers to an existing branch
1257 1257 hint=_("use 'hg update' to switch to it"))
1258 1258 scmutil.checknewlabel(repo, label, 'branch')
1259 1259 repo.dirstate.setbranch(label)
1260 1260 ui.status(_('marked working directory as branch %s\n') % label)
1261 1261
1262 1262 # find any open named branches aside from default
1263 1263 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1264 1264 if n != "default" and not c]
1265 1265 if not others:
1266 1266 ui.status(_('(branches are permanent and global, '
1267 1267 'did you want a bookmark?)\n'))
1268 1268
1269 1269 @command('branches',
1270 1270 [('a', 'active', False,
1271 1271 _('show only branches that have unmerged heads (DEPRECATED)')),
1272 1272 ('c', 'closed', False, _('show normal and closed branches')),
1273 1273 ] + formatteropts,
1274 1274 _('[-c]'))
1275 1275 def branches(ui, repo, active=False, closed=False, **opts):
1276 1276 """list repository named branches
1277 1277
1278 1278 List the repository's named branches, indicating which ones are
1279 1279 inactive. If -c/--closed is specified, also list branches which have
1280 1280 been marked closed (see :hg:`commit --close-branch`).
1281 1281
1282 1282 Use the command :hg:`update` to switch to an existing branch.
1283 1283
1284 1284 Returns 0.
1285 1285 """
1286 1286
1287 1287 fm = ui.formatter('branches', opts)
1288 1288 hexfunc = fm.hexfunc
1289 1289
1290 1290 allheads = set(repo.heads())
1291 1291 branches = []
1292 1292 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1293 1293 isactive = not isclosed and bool(set(heads) & allheads)
1294 1294 branches.append((tag, repo[tip], isactive, not isclosed))
1295 1295 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1296 1296 reverse=True)
1297 1297
1298 1298 for tag, ctx, isactive, isopen in branches:
1299 1299 if active and not isactive:
1300 1300 continue
1301 1301 if isactive:
1302 1302 label = 'branches.active'
1303 1303 notice = ''
1304 1304 elif not isopen:
1305 1305 if not closed:
1306 1306 continue
1307 1307 label = 'branches.closed'
1308 1308 notice = _(' (closed)')
1309 1309 else:
1310 1310 label = 'branches.inactive'
1311 1311 notice = _(' (inactive)')
1312 1312 current = (tag == repo.dirstate.branch())
1313 1313 if current:
1314 1314 label = 'branches.current'
1315 1315
1316 1316 fm.startitem()
1317 1317 fm.write('branch', '%s', tag, label=label)
1318 1318 rev = ctx.rev()
1319 1319 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1320 1320 fmt = ' ' * padsize + ' %d:%s'
1321 1321 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1322 1322 label='log.changeset changeset.%s' % ctx.phasestr())
1323 1323 fm.data(active=isactive, closed=not isopen, current=current)
1324 1324 if not ui.quiet:
1325 1325 fm.plain(notice)
1326 1326 fm.plain('\n')
1327 1327 fm.end()
1328 1328
1329 1329 @command('bundle',
1330 1330 [('f', 'force', None, _('run even when the destination is unrelated')),
1331 1331 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1332 1332 _('REV')),
1333 1333 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1334 1334 _('BRANCH')),
1335 1335 ('', 'base', [],
1336 1336 _('a base changeset assumed to be available at the destination'),
1337 1337 _('REV')),
1338 1338 ('a', 'all', None, _('bundle all changesets in the repository')),
1339 1339 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1340 1340 ] + remoteopts,
1341 1341 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1342 1342 def bundle(ui, repo, fname, dest=None, **opts):
1343 1343 """create a changegroup file
1344 1344
1345 1345 Generate a changegroup file collecting changesets to be added
1346 1346 to a repository.
1347 1347
1348 1348 To create a bundle containing all changesets, use -a/--all
1349 1349 (or --base null). Otherwise, hg assumes the destination will have
1350 1350 all the nodes you specify with --base parameters. Otherwise, hg
1351 1351 will assume the repository has all the nodes in destination, or
1352 1352 default-push/default if no destination is specified.
1353 1353
1354 1354 You can change bundle format with the -t/--type option. You can
1355 1355 specify a compression, a bundle version or both using a dash
1356 1356 (comp-version). The available compression methods are: none, bzip2,
1357 1357 and gzip (by default, bundles are compressed using bzip2). The
1358 1358 available formats are: v1, v2 (default to most suitable).
1359 1359
1360 1360 The bundle file can then be transferred using conventional means
1361 1361 and applied to another repository with the unbundle or pull
1362 1362 command. This is useful when direct push and pull are not
1363 1363 available or when exporting an entire repository is undesirable.
1364 1364
1365 1365 Applying bundles preserves all changeset contents including
1366 1366 permissions, copy/rename information, and revision history.
1367 1367
1368 1368 Returns 0 on success, 1 if no changes found.
1369 1369 """
1370 1370 revs = None
1371 1371 if 'rev' in opts:
1372 1372 revstrings = opts['rev']
1373 1373 revs = scmutil.revrange(repo, revstrings)
1374 1374 if revstrings and not revs:
1375 1375 raise error.Abort(_('no commits to bundle'))
1376 1376
1377 1377 bundletype = opts.get('type', 'bzip2').lower()
1378 1378 try:
1379 1379 bcompression, cgversion, params = exchange.parsebundlespec(
1380 1380 repo, bundletype, strict=False)
1381 1381 except error.UnsupportedBundleSpecification as e:
1382 1382 raise error.Abort(str(e),
1383 1383 hint=_('see "hg help bundle" for supported '
1384 1384 'values for --type'))
1385 1385
1386 1386 # Packed bundles are a pseudo bundle format for now.
1387 1387 if cgversion == 's1':
1388 1388 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1389 1389 hint=_('use "hg debugcreatestreamclonebundle"'))
1390 1390
1391 1391 if opts.get('all'):
1392 1392 if dest:
1393 1393 raise error.Abort(_("--all is incompatible with specifying "
1394 1394 "a destination"))
1395 1395 if opts.get('base'):
1396 1396 ui.warn(_("ignoring --base because --all was specified\n"))
1397 1397 base = ['null']
1398 1398 else:
1399 1399 base = scmutil.revrange(repo, opts.get('base'))
1400 1400 # TODO: get desired bundlecaps from command line.
1401 1401 bundlecaps = None
1402 1402 if base:
1403 1403 if dest:
1404 1404 raise error.Abort(_("--base is incompatible with specifying "
1405 1405 "a destination"))
1406 1406 common = [repo.lookup(rev) for rev in base]
1407 1407 heads = revs and map(repo.lookup, revs) or revs
1408 1408 cg = changegroup.getchangegroup(repo, 'bundle', heads=heads,
1409 1409 common=common, bundlecaps=bundlecaps,
1410 1410 version=cgversion)
1411 1411 outgoing = None
1412 1412 else:
1413 1413 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1414 1414 dest, branches = hg.parseurl(dest, opts.get('branch'))
1415 1415 other = hg.peer(repo, opts, dest)
1416 1416 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1417 1417 heads = revs and map(repo.lookup, revs) or revs
1418 1418 outgoing = discovery.findcommonoutgoing(repo, other,
1419 1419 onlyheads=heads,
1420 1420 force=opts.get('force'),
1421 1421 portable=True)
1422 1422 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1423 1423 bundlecaps, version=cgversion)
1424 1424 if not cg:
1425 1425 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1426 1426 return 1
1427 1427
1428 1428 if cgversion == '01': #bundle1
1429 1429 if bcompression is None:
1430 1430 bcompression = 'UN'
1431 1431 bversion = 'HG10' + bcompression
1432 1432 bcompression = None
1433 1433 else:
1434 1434 assert cgversion == '02'
1435 1435 bversion = 'HG20'
1436 1436
1437 1437
1438 1438 changegroup.writebundle(ui, cg, fname, bversion, compression=bcompression)
1439 1439
1440 1440 @command('cat',
1441 1441 [('o', 'output', '',
1442 1442 _('print output to file with formatted name'), _('FORMAT')),
1443 1443 ('r', 'rev', '', _('print the given revision'), _('REV')),
1444 1444 ('', 'decode', None, _('apply any matching decode filter')),
1445 1445 ] + walkopts,
1446 1446 _('[OPTION]... FILE...'),
1447 1447 inferrepo=True)
1448 1448 def cat(ui, repo, file1, *pats, **opts):
1449 1449 """output the current or given revision of files
1450 1450
1451 1451 Print the specified files as they were at the given revision. If
1452 1452 no revision is given, the parent of the working directory is used.
1453 1453
1454 1454 Output may be to a file, in which case the name of the file is
1455 1455 given using a format string. The formatting rules as follows:
1456 1456
1457 1457 :``%%``: literal "%" character
1458 1458 :``%s``: basename of file being printed
1459 1459 :``%d``: dirname of file being printed, or '.' if in repository root
1460 1460 :``%p``: root-relative path name of file being printed
1461 1461 :``%H``: changeset hash (40 hexadecimal digits)
1462 1462 :``%R``: changeset revision number
1463 1463 :``%h``: short-form changeset hash (12 hexadecimal digits)
1464 1464 :``%r``: zero-padded changeset revision number
1465 1465 :``%b``: basename of the exporting repository
1466 1466
1467 1467 Returns 0 on success.
1468 1468 """
1469 1469 ctx = scmutil.revsingle(repo, opts.get('rev'))
1470 1470 m = scmutil.match(ctx, (file1,) + pats, opts)
1471 1471
1472 1472 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1473 1473
1474 1474 @command('^clone',
1475 1475 [('U', 'noupdate', None, _('the clone will include an empty working '
1476 1476 'directory (only a repository)')),
1477 1477 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1478 1478 _('REV')),
1479 1479 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1480 1480 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1481 1481 ('', 'pull', None, _('use pull protocol to copy metadata')),
1482 1482 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1483 1483 ] + remoteopts,
1484 1484 _('[OPTION]... SOURCE [DEST]'),
1485 1485 norepo=True)
1486 1486 def clone(ui, source, dest=None, **opts):
1487 1487 """make a copy of an existing repository
1488 1488
1489 1489 Create a copy of an existing repository in a new directory.
1490 1490
1491 1491 If no destination directory name is specified, it defaults to the
1492 1492 basename of the source.
1493 1493
1494 1494 The location of the source is added to the new repository's
1495 1495 ``.hg/hgrc`` file, as the default to be used for future pulls.
1496 1496
1497 1497 Only local paths and ``ssh://`` URLs are supported as
1498 1498 destinations. For ``ssh://`` destinations, no working directory or
1499 1499 ``.hg/hgrc`` will be created on the remote side.
1500 1500
1501 1501 If the source repository has a bookmark called '@' set, that
1502 1502 revision will be checked out in the new repository by default.
1503 1503
1504 1504 To check out a particular version, use -u/--update, or
1505 1505 -U/--noupdate to create a clone with no working directory.
1506 1506
1507 1507 To pull only a subset of changesets, specify one or more revisions
1508 1508 identifiers with -r/--rev or branches with -b/--branch. The
1509 1509 resulting clone will contain only the specified changesets and
1510 1510 their ancestors. These options (or 'clone src#rev dest') imply
1511 1511 --pull, even for local source repositories.
1512 1512
1513 1513 .. note::
1514 1514
1515 1515 Specifying a tag will include the tagged changeset but not the
1516 1516 changeset containing the tag.
1517 1517
1518 1518 .. container:: verbose
1519 1519
1520 1520 For efficiency, hardlinks are used for cloning whenever the
1521 1521 source and destination are on the same filesystem (note this
1522 1522 applies only to the repository data, not to the working
1523 1523 directory). Some filesystems, such as AFS, implement hardlinking
1524 1524 incorrectly, but do not report errors. In these cases, use the
1525 1525 --pull option to avoid hardlinking.
1526 1526
1527 1527 In some cases, you can clone repositories and the working
1528 1528 directory using full hardlinks with ::
1529 1529
1530 1530 $ cp -al REPO REPOCLONE
1531 1531
1532 1532 This is the fastest way to clone, but it is not always safe. The
1533 1533 operation is not atomic (making sure REPO is not modified during
1534 1534 the operation is up to you) and you have to make sure your
1535 1535 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1536 1536 so). Also, this is not compatible with certain extensions that
1537 1537 place their metadata under the .hg directory, such as mq.
1538 1538
1539 1539 Mercurial will update the working directory to the first applicable
1540 1540 revision from this list:
1541 1541
1542 1542 a) null if -U or the source repository has no changesets
1543 1543 b) if -u . and the source repository is local, the first parent of
1544 1544 the source repository's working directory
1545 1545 c) the changeset specified with -u (if a branch name, this means the
1546 1546 latest head of that branch)
1547 1547 d) the changeset specified with -r
1548 1548 e) the tipmost head specified with -b
1549 1549 f) the tipmost head specified with the url#branch source syntax
1550 1550 g) the revision marked with the '@' bookmark, if present
1551 1551 h) the tipmost head of the default branch
1552 1552 i) tip
1553 1553
1554 1554 When cloning from servers that support it, Mercurial may fetch
1555 1555 pre-generated data from a server-advertised URL. When this is done,
1556 1556 hooks operating on incoming changesets and changegroups may fire twice,
1557 1557 once for the bundle fetched from the URL and another for any additional
1558 1558 data not fetched from this URL. In addition, if an error occurs, the
1559 1559 repository may be rolled back to a partial clone. This behavior may
1560 1560 change in future releases. See :hg:`help -e clonebundles` for more.
1561 1561
1562 1562 Examples:
1563 1563
1564 1564 - clone a remote repository to a new directory named hg/::
1565 1565
1566 1566 hg clone http://selenic.com/hg
1567 1567
1568 1568 - create a lightweight local clone::
1569 1569
1570 1570 hg clone project/ project-feature/
1571 1571
1572 1572 - clone from an absolute path on an ssh server (note double-slash)::
1573 1573
1574 1574 hg clone ssh://user@server//home/projects/alpha/
1575 1575
1576 1576 - do a high-speed clone over a LAN while checking out a
1577 1577 specified version::
1578 1578
1579 1579 hg clone --uncompressed http://server/repo -u 1.5
1580 1580
1581 1581 - create a repository without changesets after a particular revision::
1582 1582
1583 1583 hg clone -r 04e544 experimental/ good/
1584 1584
1585 1585 - clone (and track) a particular named branch::
1586 1586
1587 1587 hg clone http://selenic.com/hg#stable
1588 1588
1589 1589 See :hg:`help urls` for details on specifying URLs.
1590 1590
1591 1591 Returns 0 on success.
1592 1592 """
1593 1593 if opts.get('noupdate') and opts.get('updaterev'):
1594 1594 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1595 1595
1596 1596 r = hg.clone(ui, opts, source, dest,
1597 1597 pull=opts.get('pull'),
1598 1598 stream=opts.get('uncompressed'),
1599 1599 rev=opts.get('rev'),
1600 1600 update=opts.get('updaterev') or not opts.get('noupdate'),
1601 1601 branch=opts.get('branch'),
1602 1602 shareopts=opts.get('shareopts'))
1603 1603
1604 1604 return r is None
1605 1605
1606 1606 @command('^commit|ci',
1607 1607 [('A', 'addremove', None,
1608 1608 _('mark new/missing files as added/removed before committing')),
1609 1609 ('', 'close-branch', None,
1610 1610 _('mark a branch head as closed')),
1611 1611 ('', 'amend', None, _('amend the parent of the working directory')),
1612 1612 ('s', 'secret', None, _('use the secret phase for committing')),
1613 1613 ('e', 'edit', None, _('invoke editor on commit messages')),
1614 1614 ('i', 'interactive', None, _('use interactive mode')),
1615 1615 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1616 1616 _('[OPTION]... [FILE]...'),
1617 1617 inferrepo=True)
1618 1618 def commit(ui, repo, *pats, **opts):
1619 1619 """commit the specified files or all outstanding changes
1620 1620
1621 1621 Commit changes to the given files into the repository. Unlike a
1622 1622 centralized SCM, this operation is a local operation. See
1623 1623 :hg:`push` for a way to actively distribute your changes.
1624 1624
1625 1625 If a list of files is omitted, all changes reported by :hg:`status`
1626 1626 will be committed.
1627 1627
1628 1628 If you are committing the result of a merge, do not provide any
1629 1629 filenames or -I/-X filters.
1630 1630
1631 1631 If no commit message is specified, Mercurial starts your
1632 1632 configured editor where you can enter a message. In case your
1633 1633 commit fails, you will find a backup of your message in
1634 1634 ``.hg/last-message.txt``.
1635 1635
1636 1636 The --close-branch flag can be used to mark the current branch
1637 1637 head closed. When all heads of a branch are closed, the branch
1638 1638 will be considered closed and no longer listed.
1639 1639
1640 1640 The --amend flag can be used to amend the parent of the
1641 1641 working directory with a new commit that contains the changes
1642 1642 in the parent in addition to those currently reported by :hg:`status`,
1643 1643 if there are any. The old commit is stored in a backup bundle in
1644 1644 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1645 1645 on how to restore it).
1646 1646
1647 1647 Message, user and date are taken from the amended commit unless
1648 1648 specified. When a message isn't specified on the command line,
1649 1649 the editor will open with the message of the amended commit.
1650 1650
1651 1651 It is not possible to amend public changesets (see :hg:`help phases`)
1652 1652 or changesets that have children.
1653 1653
1654 1654 See :hg:`help dates` for a list of formats valid for -d/--date.
1655 1655
1656 1656 Returns 0 on success, 1 if nothing changed.
1657 1657
1658 1658 .. container:: verbose
1659 1659
1660 1660 Examples:
1661 1661
1662 1662 - commit all files ending in .py::
1663 1663
1664 1664 hg commit --include "set:**.py"
1665 1665
1666 1666 - commit all non-binary files::
1667 1667
1668 1668 hg commit --exclude "set:binary()"
1669 1669
1670 1670 - amend the current commit and set the date to now::
1671 1671
1672 1672 hg commit --amend --date now
1673 1673 """
1674 1674 wlock = lock = None
1675 1675 try:
1676 1676 wlock = repo.wlock()
1677 1677 lock = repo.lock()
1678 1678 return _docommit(ui, repo, *pats, **opts)
1679 1679 finally:
1680 1680 release(lock, wlock)
1681 1681
1682 1682 def _docommit(ui, repo, *pats, **opts):
1683 1683 if opts.get('interactive'):
1684 1684 opts.pop('interactive')
1685 1685 cmdutil.dorecord(ui, repo, commit, None, False,
1686 1686 cmdutil.recordfilter, *pats, **opts)
1687 1687 return
1688 1688
1689 1689 if opts.get('subrepos'):
1690 1690 if opts.get('amend'):
1691 1691 raise error.Abort(_('cannot amend with --subrepos'))
1692 1692 # Let --subrepos on the command line override config setting.
1693 1693 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1694 1694
1695 1695 cmdutil.checkunfinished(repo, commit=True)
1696 1696
1697 1697 branch = repo[None].branch()
1698 1698 bheads = repo.branchheads(branch)
1699 1699
1700 1700 extra = {}
1701 1701 if opts.get('close_branch'):
1702 1702 extra['close'] = 1
1703 1703
1704 1704 if not bheads:
1705 1705 raise error.Abort(_('can only close branch heads'))
1706 1706 elif opts.get('amend'):
1707 1707 if repo[None].parents()[0].p1().branch() != branch and \
1708 1708 repo[None].parents()[0].p2().branch() != branch:
1709 1709 raise error.Abort(_('can only close branch heads'))
1710 1710
1711 1711 if opts.get('amend'):
1712 1712 if ui.configbool('ui', 'commitsubrepos'):
1713 1713 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1714 1714
1715 1715 old = repo['.']
1716 1716 if not old.mutable():
1717 1717 raise error.Abort(_('cannot amend public changesets'))
1718 1718 if len(repo[None].parents()) > 1:
1719 1719 raise error.Abort(_('cannot amend while merging'))
1720 1720 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1721 1721 if not allowunstable and old.children():
1722 1722 raise error.Abort(_('cannot amend changeset with children'))
1723 1723
1724 1724 # Currently histedit gets confused if an amend happens while histedit
1725 1725 # is in progress. Since we have a checkunfinished command, we are
1726 1726 # temporarily honoring it.
1727 1727 #
1728 1728 # Note: eventually this guard will be removed. Please do not expect
1729 1729 # this behavior to remain.
1730 1730 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1731 1731 cmdutil.checkunfinished(repo)
1732 1732
1733 1733 # commitfunc is used only for temporary amend commit by cmdutil.amend
1734 1734 def commitfunc(ui, repo, message, match, opts):
1735 1735 return repo.commit(message,
1736 1736 opts.get('user') or old.user(),
1737 1737 opts.get('date') or old.date(),
1738 1738 match,
1739 1739 extra=extra)
1740 1740
1741 1741 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1742 1742 if node == old.node():
1743 1743 ui.status(_("nothing changed\n"))
1744 1744 return 1
1745 1745 else:
1746 1746 def commitfunc(ui, repo, message, match, opts):
1747 1747 backup = ui.backupconfig('phases', 'new-commit')
1748 1748 baseui = repo.baseui
1749 1749 basebackup = baseui.backupconfig('phases', 'new-commit')
1750 1750 try:
1751 1751 if opts.get('secret'):
1752 1752 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1753 1753 # Propagate to subrepos
1754 1754 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1755 1755
1756 1756 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1757 1757 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1758 1758 return repo.commit(message, opts.get('user'), opts.get('date'),
1759 1759 match,
1760 1760 editor=editor,
1761 1761 extra=extra)
1762 1762 finally:
1763 1763 ui.restoreconfig(backup)
1764 1764 repo.baseui.restoreconfig(basebackup)
1765 1765
1766 1766
1767 1767 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1768 1768
1769 1769 if not node:
1770 1770 stat = cmdutil.postcommitstatus(repo, pats, opts)
1771 1771 if stat[3]:
1772 1772 ui.status(_("nothing changed (%d missing files, see "
1773 1773 "'hg status')\n") % len(stat[3]))
1774 1774 else:
1775 1775 ui.status(_("nothing changed\n"))
1776 1776 return 1
1777 1777
1778 1778 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1779 1779
1780 1780 @command('config|showconfig|debugconfig',
1781 1781 [('u', 'untrusted', None, _('show untrusted configuration options')),
1782 1782 ('e', 'edit', None, _('edit user config')),
1783 1783 ('l', 'local', None, _('edit repository config')),
1784 1784 ('g', 'global', None, _('edit global config'))],
1785 1785 _('[-u] [NAME]...'),
1786 1786 optionalrepo=True)
1787 1787 def config(ui, repo, *values, **opts):
1788 1788 """show combined config settings from all hgrc files
1789 1789
1790 1790 With no arguments, print names and values of all config items.
1791 1791
1792 1792 With one argument of the form section.name, print just the value
1793 1793 of that config item.
1794 1794
1795 1795 With multiple arguments, print names and values of all config
1796 1796 items with matching section names.
1797 1797
1798 1798 With --edit, start an editor on the user-level config file. With
1799 1799 --global, edit the system-wide config file. With --local, edit the
1800 1800 repository-level config file.
1801 1801
1802 1802 With --debug, the source (filename and line number) is printed
1803 1803 for each config item.
1804 1804
1805 1805 See :hg:`help config` for more information about config files.
1806 1806
1807 1807 Returns 0 on success, 1 if NAME does not exist.
1808 1808
1809 1809 """
1810 1810
1811 1811 if opts.get('edit') or opts.get('local') or opts.get('global'):
1812 1812 if opts.get('local') and opts.get('global'):
1813 1813 raise error.Abort(_("can't use --local and --global together"))
1814 1814
1815 1815 if opts.get('local'):
1816 1816 if not repo:
1817 1817 raise error.Abort(_("can't use --local outside a repository"))
1818 1818 paths = [repo.join('hgrc')]
1819 1819 elif opts.get('global'):
1820 1820 paths = scmutil.systemrcpath()
1821 1821 else:
1822 1822 paths = scmutil.userrcpath()
1823 1823
1824 1824 for f in paths:
1825 1825 if os.path.exists(f):
1826 1826 break
1827 1827 else:
1828 1828 if opts.get('global'):
1829 1829 samplehgrc = uimod.samplehgrcs['global']
1830 1830 elif opts.get('local'):
1831 1831 samplehgrc = uimod.samplehgrcs['local']
1832 1832 else:
1833 1833 samplehgrc = uimod.samplehgrcs['user']
1834 1834
1835 1835 f = paths[0]
1836 1836 fp = open(f, "w")
1837 1837 fp.write(samplehgrc)
1838 1838 fp.close()
1839 1839
1840 1840 editor = ui.geteditor()
1841 1841 ui.system("%s \"%s\"" % (editor, f),
1842 1842 onerr=error.Abort, errprefix=_("edit failed"))
1843 1843 return
1844 1844
1845 1845 for f in scmutil.rcpath():
1846 1846 ui.debug('read config from: %s\n' % f)
1847 1847 untrusted = bool(opts.get('untrusted'))
1848 1848 if values:
1849 1849 sections = [v for v in values if '.' not in v]
1850 1850 items = [v for v in values if '.' in v]
1851 1851 if len(items) > 1 or items and sections:
1852 1852 raise error.Abort(_('only one config item permitted'))
1853 1853 matched = False
1854 1854 for section, name, value in ui.walkconfig(untrusted=untrusted):
1855 1855 value = str(value).replace('\n', '\\n')
1856 1856 sectname = section + '.' + name
1857 1857 if values:
1858 1858 for v in values:
1859 1859 if v == section:
1860 1860 ui.debug('%s: ' %
1861 1861 ui.configsource(section, name, untrusted))
1862 1862 ui.write('%s=%s\n' % (sectname, value))
1863 1863 matched = True
1864 1864 elif v == sectname:
1865 1865 ui.debug('%s: ' %
1866 1866 ui.configsource(section, name, untrusted))
1867 1867 ui.write(value, '\n')
1868 1868 matched = True
1869 1869 else:
1870 1870 ui.debug('%s: ' %
1871 1871 ui.configsource(section, name, untrusted))
1872 1872 ui.write('%s=%s\n' % (sectname, value))
1873 1873 matched = True
1874 1874 if matched:
1875 1875 return 0
1876 1876 return 1
1877 1877
1878 1878 @command('copy|cp',
1879 1879 [('A', 'after', None, _('record a copy that has already occurred')),
1880 1880 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1881 1881 ] + walkopts + dryrunopts,
1882 1882 _('[OPTION]... [SOURCE]... DEST'))
1883 1883 def copy(ui, repo, *pats, **opts):
1884 1884 """mark files as copied for the next commit
1885 1885
1886 1886 Mark dest as having copies of source files. If dest is a
1887 1887 directory, copies are put in that directory. If dest is a file,
1888 1888 the source must be a single file.
1889 1889
1890 1890 By default, this command copies the contents of files as they
1891 1891 exist in the working directory. If invoked with -A/--after, the
1892 1892 operation is recorded, but no copying is performed.
1893 1893
1894 1894 This command takes effect with the next commit. To undo a copy
1895 1895 before that, see :hg:`revert`.
1896 1896
1897 1897 Returns 0 on success, 1 if errors are encountered.
1898 1898 """
1899 1899 with repo.wlock(False):
1900 1900 return cmdutil.copy(ui, repo, pats, opts)
1901 1901
1902 1902 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1903 1903 def debugancestor(ui, repo, *args):
1904 1904 """find the ancestor revision of two revisions in a given index"""
1905 1905 if len(args) == 3:
1906 1906 index, rev1, rev2 = args
1907 1907 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1908 1908 lookup = r.lookup
1909 1909 elif len(args) == 2:
1910 1910 if not repo:
1911 1911 raise error.Abort(_("there is no Mercurial repository here "
1912 1912 "(.hg not found)"))
1913 1913 rev1, rev2 = args
1914 1914 r = repo.changelog
1915 1915 lookup = repo.lookup
1916 1916 else:
1917 1917 raise error.Abort(_('either two or three arguments required'))
1918 1918 a = r.ancestor(lookup(rev1), lookup(rev2))
1919 1919 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1920 1920
1921 1921 @command('debugbuilddag',
1922 1922 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1923 1923 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1924 1924 ('n', 'new-file', None, _('add new file at each rev'))],
1925 1925 _('[OPTION]... [TEXT]'))
1926 1926 def debugbuilddag(ui, repo, text=None,
1927 1927 mergeable_file=False,
1928 1928 overwritten_file=False,
1929 1929 new_file=False):
1930 1930 """builds a repo with a given DAG from scratch in the current empty repo
1931 1931
1932 1932 The description of the DAG is read from stdin if not given on the
1933 1933 command line.
1934 1934
1935 1935 Elements:
1936 1936
1937 1937 - "+n" is a linear run of n nodes based on the current default parent
1938 1938 - "." is a single node based on the current default parent
1939 1939 - "$" resets the default parent to null (implied at the start);
1940 1940 otherwise the default parent is always the last node created
1941 1941 - "<p" sets the default parent to the backref p
1942 1942 - "*p" is a fork at parent p, which is a backref
1943 1943 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1944 1944 - "/p2" is a merge of the preceding node and p2
1945 1945 - ":tag" defines a local tag for the preceding node
1946 1946 - "@branch" sets the named branch for subsequent nodes
1947 1947 - "#...\\n" is a comment up to the end of the line
1948 1948
1949 1949 Whitespace between the above elements is ignored.
1950 1950
1951 1951 A backref is either
1952 1952
1953 1953 - a number n, which references the node curr-n, where curr is the current
1954 1954 node, or
1955 1955 - the name of a local tag you placed earlier using ":tag", or
1956 1956 - empty to denote the default parent.
1957 1957
1958 1958 All string valued-elements are either strictly alphanumeric, or must
1959 1959 be enclosed in double quotes ("..."), with "\\" as escape character.
1960 1960 """
1961 1961
1962 1962 if text is None:
1963 1963 ui.status(_("reading DAG from stdin\n"))
1964 1964 text = ui.fin.read()
1965 1965
1966 1966 cl = repo.changelog
1967 1967 if len(cl) > 0:
1968 1968 raise error.Abort(_('repository is not empty'))
1969 1969
1970 1970 # determine number of revs in DAG
1971 1971 total = 0
1972 1972 for type, data in dagparser.parsedag(text):
1973 1973 if type == 'n':
1974 1974 total += 1
1975 1975
1976 1976 if mergeable_file:
1977 1977 linesperrev = 2
1978 1978 # make a file with k lines per rev
1979 1979 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1980 1980 initialmergedlines.append("")
1981 1981
1982 1982 tags = []
1983 1983
1984 1984 lock = tr = None
1985 1985 try:
1986 1986 lock = repo.lock()
1987 1987 tr = repo.transaction("builddag")
1988 1988
1989 1989 at = -1
1990 1990 atbranch = 'default'
1991 1991 nodeids = []
1992 1992 id = 0
1993 1993 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1994 1994 for type, data in dagparser.parsedag(text):
1995 1995 if type == 'n':
1996 1996 ui.note(('node %s\n' % str(data)))
1997 1997 id, ps = data
1998 1998
1999 1999 files = []
2000 2000 fctxs = {}
2001 2001
2002 2002 p2 = None
2003 2003 if mergeable_file:
2004 2004 fn = "mf"
2005 2005 p1 = repo[ps[0]]
2006 2006 if len(ps) > 1:
2007 2007 p2 = repo[ps[1]]
2008 2008 pa = p1.ancestor(p2)
2009 2009 base, local, other = [x[fn].data() for x in (pa, p1,
2010 2010 p2)]
2011 2011 m3 = simplemerge.Merge3Text(base, local, other)
2012 2012 ml = [l.strip() for l in m3.merge_lines()]
2013 2013 ml.append("")
2014 2014 elif at > 0:
2015 2015 ml = p1[fn].data().split("\n")
2016 2016 else:
2017 2017 ml = initialmergedlines
2018 2018 ml[id * linesperrev] += " r%i" % id
2019 2019 mergedtext = "\n".join(ml)
2020 2020 files.append(fn)
2021 2021 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
2022 2022
2023 2023 if overwritten_file:
2024 2024 fn = "of"
2025 2025 files.append(fn)
2026 2026 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
2027 2027
2028 2028 if new_file:
2029 2029 fn = "nf%i" % id
2030 2030 files.append(fn)
2031 2031 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
2032 2032 if len(ps) > 1:
2033 2033 if not p2:
2034 2034 p2 = repo[ps[1]]
2035 2035 for fn in p2:
2036 2036 if fn.startswith("nf"):
2037 2037 files.append(fn)
2038 2038 fctxs[fn] = p2[fn]
2039 2039
2040 2040 def fctxfn(repo, cx, path):
2041 2041 return fctxs.get(path)
2042 2042
2043 2043 if len(ps) == 0 or ps[0] < 0:
2044 2044 pars = [None, None]
2045 2045 elif len(ps) == 1:
2046 2046 pars = [nodeids[ps[0]], None]
2047 2047 else:
2048 2048 pars = [nodeids[p] for p in ps]
2049 2049 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
2050 2050 date=(id, 0),
2051 2051 user="debugbuilddag",
2052 2052 extra={'branch': atbranch})
2053 2053 nodeid = repo.commitctx(cx)
2054 2054 nodeids.append(nodeid)
2055 2055 at = id
2056 2056 elif type == 'l':
2057 2057 id, name = data
2058 2058 ui.note(('tag %s\n' % name))
2059 2059 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
2060 2060 elif type == 'a':
2061 2061 ui.note(('branch %s\n' % data))
2062 2062 atbranch = data
2063 2063 ui.progress(_('building'), id, unit=_('revisions'), total=total)
2064 2064 tr.close()
2065 2065
2066 2066 if tags:
2067 2067 repo.vfs.write("localtags", "".join(tags))
2068 2068 finally:
2069 2069 ui.progress(_('building'), None)
2070 2070 release(tr, lock)
2071 2071
2072 2072 @command('debugbundle',
2073 2073 [('a', 'all', None, _('show all details')),
2074 2074 ('', 'spec', None, _('print the bundlespec of the bundle'))],
2075 2075 _('FILE'),
2076 2076 norepo=True)
2077 2077 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
2078 2078 """lists the contents of a bundle"""
2079 2079 with hg.openpath(ui, bundlepath) as f:
2080 2080 if spec:
2081 2081 spec = exchange.getbundlespec(ui, f)
2082 2082 ui.write('%s\n' % spec)
2083 2083 return
2084 2084
2085 2085 gen = exchange.readbundle(ui, f, bundlepath)
2086 2086 if isinstance(gen, bundle2.unbundle20):
2087 2087 return _debugbundle2(ui, gen, all=all, **opts)
2088 2088 if all:
2089 2089 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
2090 2090
2091 2091 def showchunks(named):
2092 2092 ui.write("\n%s\n" % named)
2093 2093 chain = None
2094 2094 while True:
2095 2095 chunkdata = gen.deltachunk(chain)
2096 2096 if not chunkdata:
2097 2097 break
2098 2098 node = chunkdata['node']
2099 2099 p1 = chunkdata['p1']
2100 2100 p2 = chunkdata['p2']
2101 2101 cs = chunkdata['cs']
2102 2102 deltabase = chunkdata['deltabase']
2103 2103 delta = chunkdata['delta']
2104 2104 ui.write("%s %s %s %s %s %s\n" %
2105 2105 (hex(node), hex(p1), hex(p2),
2106 2106 hex(cs), hex(deltabase), len(delta)))
2107 2107 chain = node
2108 2108
2109 2109 chunkdata = gen.changelogheader()
2110 2110 showchunks("changelog")
2111 2111 chunkdata = gen.manifestheader()
2112 2112 showchunks("manifest")
2113 2113 while True:
2114 2114 chunkdata = gen.filelogheader()
2115 2115 if not chunkdata:
2116 2116 break
2117 2117 fname = chunkdata['filename']
2118 2118 showchunks(fname)
2119 2119 else:
2120 2120 if isinstance(gen, bundle2.unbundle20):
2121 2121 raise error.Abort(_('use debugbundle2 for this file'))
2122 2122 chunkdata = gen.changelogheader()
2123 2123 chain = None
2124 2124 while True:
2125 2125 chunkdata = gen.deltachunk(chain)
2126 2126 if not chunkdata:
2127 2127 break
2128 2128 node = chunkdata['node']
2129 2129 ui.write("%s\n" % hex(node))
2130 2130 chain = node
2131 2131
2132 2132 def _debugbundle2(ui, gen, **opts):
2133 2133 """lists the contents of a bundle2"""
2134 2134 if not isinstance(gen, bundle2.unbundle20):
2135 2135 raise error.Abort(_('not a bundle2 file'))
2136 2136 ui.write(('Stream params: %s\n' % repr(gen.params)))
2137 2137 for part in gen.iterparts():
2138 2138 ui.write('%s -- %r\n' % (part.type, repr(part.params)))
2139 2139 if part.type == 'changegroup':
2140 2140 version = part.params.get('version', '01')
2141 2141 cg = changegroup.getunbundler(version, part, 'UN')
2142 2142 chunkdata = cg.changelogheader()
2143 2143 chain = None
2144 2144 while True:
2145 2145 chunkdata = cg.deltachunk(chain)
2146 2146 if not chunkdata:
2147 2147 break
2148 2148 node = chunkdata['node']
2149 2149 ui.write(" %s\n" % hex(node))
2150 2150 chain = node
2151 2151
2152 2152 @command('debugcreatestreamclonebundle', [], 'FILE')
2153 2153 def debugcreatestreamclonebundle(ui, repo, fname):
2154 2154 """create a stream clone bundle file
2155 2155
2156 2156 Stream bundles are special bundles that are essentially archives of
2157 2157 revlog files. They are commonly used for cloning very quickly.
2158 2158 """
2159 2159 requirements, gen = streamclone.generatebundlev1(repo)
2160 2160 changegroup.writechunks(ui, gen, fname)
2161 2161
2162 2162 ui.write(_('bundle requirements: %s\n') % ', '.join(sorted(requirements)))
2163 2163
2164 2164 @command('debugapplystreamclonebundle', [], 'FILE')
2165 2165 def debugapplystreamclonebundle(ui, repo, fname):
2166 2166 """apply a stream clone bundle file"""
2167 2167 f = hg.openpath(ui, fname)
2168 2168 gen = exchange.readbundle(ui, f, fname)
2169 2169 gen.apply(repo)
2170 2170
2171 2171 @command('debugcheckstate', [], '')
2172 2172 def debugcheckstate(ui, repo):
2173 2173 """validate the correctness of the current dirstate"""
2174 2174 parent1, parent2 = repo.dirstate.parents()
2175 2175 m1 = repo[parent1].manifest()
2176 2176 m2 = repo[parent2].manifest()
2177 2177 errors = 0
2178 2178 for f in repo.dirstate:
2179 2179 state = repo.dirstate[f]
2180 2180 if state in "nr" and f not in m1:
2181 2181 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
2182 2182 errors += 1
2183 2183 if state in "a" and f in m1:
2184 2184 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
2185 2185 errors += 1
2186 2186 if state in "m" and f not in m1 and f not in m2:
2187 2187 ui.warn(_("%s in state %s, but not in either manifest\n") %
2188 2188 (f, state))
2189 2189 errors += 1
2190 2190 for f in m1:
2191 2191 state = repo.dirstate[f]
2192 2192 if state not in "nrm":
2193 2193 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
2194 2194 errors += 1
2195 2195 if errors:
2196 2196 error = _(".hg/dirstate inconsistent with current parent's manifest")
2197 2197 raise error.Abort(error)
2198 2198
2199 2199 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
2200 2200 def debugcommands(ui, cmd='', *args):
2201 2201 """list all available commands and options"""
2202 2202 for cmd, vals in sorted(table.iteritems()):
2203 2203 cmd = cmd.split('|')[0].strip('^')
2204 2204 opts = ', '.join([i[1] for i in vals[1]])
2205 2205 ui.write('%s: %s\n' % (cmd, opts))
2206 2206
2207 2207 @command('debugcomplete',
2208 2208 [('o', 'options', None, _('show the command options'))],
2209 2209 _('[-o] CMD'),
2210 2210 norepo=True)
2211 2211 def debugcomplete(ui, cmd='', **opts):
2212 2212 """returns the completion list associated with the given command"""
2213 2213
2214 2214 if opts.get('options'):
2215 2215 options = []
2216 2216 otables = [globalopts]
2217 2217 if cmd:
2218 2218 aliases, entry = cmdutil.findcmd(cmd, table, False)
2219 2219 otables.append(entry[1])
2220 2220 for t in otables:
2221 2221 for o in t:
2222 2222 if "(DEPRECATED)" in o[3]:
2223 2223 continue
2224 2224 if o[0]:
2225 2225 options.append('-%s' % o[0])
2226 2226 options.append('--%s' % o[1])
2227 2227 ui.write("%s\n" % "\n".join(options))
2228 2228 return
2229 2229
2230 2230 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2231 2231 if ui.verbose:
2232 2232 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
2233 2233 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
2234 2234
2235 2235 @command('debugdag',
2236 2236 [('t', 'tags', None, _('use tags as labels')),
2237 2237 ('b', 'branches', None, _('annotate with branch names')),
2238 2238 ('', 'dots', None, _('use dots for runs')),
2239 2239 ('s', 'spaces', None, _('separate elements by spaces'))],
2240 2240 _('[OPTION]... [FILE [REV]...]'),
2241 2241 optionalrepo=True)
2242 2242 def debugdag(ui, repo, file_=None, *revs, **opts):
2243 2243 """format the changelog or an index DAG as a concise textual description
2244 2244
2245 2245 If you pass a revlog index, the revlog's DAG is emitted. If you list
2246 2246 revision numbers, they get labeled in the output as rN.
2247 2247
2248 2248 Otherwise, the changelog DAG of the current repo is emitted.
2249 2249 """
2250 2250 spaces = opts.get('spaces')
2251 2251 dots = opts.get('dots')
2252 2252 if file_:
2253 2253 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2254 2254 revs = set((int(r) for r in revs))
2255 2255 def events():
2256 2256 for r in rlog:
2257 2257 yield 'n', (r, list(p for p in rlog.parentrevs(r)
2258 2258 if p != -1))
2259 2259 if r in revs:
2260 2260 yield 'l', (r, "r%i" % r)
2261 2261 elif repo:
2262 2262 cl = repo.changelog
2263 2263 tags = opts.get('tags')
2264 2264 branches = opts.get('branches')
2265 2265 if tags:
2266 2266 labels = {}
2267 2267 for l, n in repo.tags().items():
2268 2268 labels.setdefault(cl.rev(n), []).append(l)
2269 2269 def events():
2270 2270 b = "default"
2271 2271 for r in cl:
2272 2272 if branches:
2273 2273 newb = cl.read(cl.node(r))[5]['branch']
2274 2274 if newb != b:
2275 2275 yield 'a', newb
2276 2276 b = newb
2277 2277 yield 'n', (r, list(p for p in cl.parentrevs(r)
2278 2278 if p != -1))
2279 2279 if tags:
2280 2280 ls = labels.get(r)
2281 2281 if ls:
2282 2282 for l in ls:
2283 2283 yield 'l', (r, l)
2284 2284 else:
2285 2285 raise error.Abort(_('need repo for changelog dag'))
2286 2286
2287 2287 for line in dagparser.dagtextlines(events(),
2288 2288 addspaces=spaces,
2289 2289 wraplabels=True,
2290 2290 wrapannotations=True,
2291 2291 wrapnonlinear=dots,
2292 2292 usedots=dots,
2293 2293 maxlinewidth=70):
2294 2294 ui.write(line)
2295 2295 ui.write("\n")
2296 2296
2297 2297 @command('debugdata', debugrevlogopts, _('-c|-m|FILE REV'))
2298 2298 def debugdata(ui, repo, file_, rev=None, **opts):
2299 2299 """dump the contents of a data file revision"""
2300 2300 if opts.get('changelog') or opts.get('manifest'):
2301 2301 file_, rev = None, file_
2302 2302 elif rev is None:
2303 2303 raise error.CommandError('debugdata', _('invalid arguments'))
2304 2304 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
2305 2305 try:
2306 2306 ui.write(r.revision(r.lookup(rev)))
2307 2307 except KeyError:
2308 2308 raise error.Abort(_('invalid revision identifier %s') % rev)
2309 2309
2310 2310 @command('debugdate',
2311 2311 [('e', 'extended', None, _('try extended date formats'))],
2312 2312 _('[-e] DATE [RANGE]'),
2313 2313 norepo=True, optionalrepo=True)
2314 2314 def debugdate(ui, date, range=None, **opts):
2315 2315 """parse and display a date"""
2316 2316 if opts["extended"]:
2317 2317 d = util.parsedate(date, util.extendeddateformats)
2318 2318 else:
2319 2319 d = util.parsedate(date)
2320 2320 ui.write(("internal: %s %s\n") % d)
2321 2321 ui.write(("standard: %s\n") % util.datestr(d))
2322 2322 if range:
2323 2323 m = util.matchdate(range)
2324 2324 ui.write(("match: %s\n") % m(d[0]))
2325 2325
2326 2326 @command('debugdiscovery',
2327 2327 [('', 'old', None, _('use old-style discovery')),
2328 2328 ('', 'nonheads', None,
2329 2329 _('use old-style discovery with non-heads included')),
2330 2330 ] + remoteopts,
2331 2331 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
2332 2332 def debugdiscovery(ui, repo, remoteurl="default", **opts):
2333 2333 """runs the changeset discovery protocol in isolation"""
2334 2334 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
2335 2335 opts.get('branch'))
2336 2336 remote = hg.peer(repo, opts, remoteurl)
2337 2337 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
2338 2338
2339 2339 # make sure tests are repeatable
2340 2340 random.seed(12323)
2341 2341
2342 2342 def doit(localheads, remoteheads, remote=remote):
2343 2343 if opts.get('old'):
2344 2344 if localheads:
2345 2345 raise error.Abort('cannot use localheads with old style '
2346 2346 'discovery')
2347 2347 if not util.safehasattr(remote, 'branches'):
2348 2348 # enable in-client legacy support
2349 2349 remote = localrepo.locallegacypeer(remote.local())
2350 2350 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2351 2351 force=True)
2352 2352 common = set(common)
2353 2353 if not opts.get('nonheads'):
2354 2354 ui.write(("unpruned common: %s\n") %
2355 2355 " ".join(sorted(short(n) for n in common)))
2356 2356 dag = dagutil.revlogdag(repo.changelog)
2357 2357 all = dag.ancestorset(dag.internalizeall(common))
2358 2358 common = dag.externalizeall(dag.headsetofconnecteds(all))
2359 2359 else:
2360 2360 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2361 2361 common = set(common)
2362 2362 rheads = set(hds)
2363 2363 lheads = set(repo.heads())
2364 2364 ui.write(("common heads: %s\n") %
2365 2365 " ".join(sorted(short(n) for n in common)))
2366 2366 if lheads <= common:
2367 2367 ui.write(("local is subset\n"))
2368 2368 elif rheads <= common:
2369 2369 ui.write(("remote is subset\n"))
2370 2370
2371 2371 serverlogs = opts.get('serverlog')
2372 2372 if serverlogs:
2373 2373 for filename in serverlogs:
2374 2374 with open(filename, 'r') as logfile:
2375 2375 line = logfile.readline()
2376 2376 while line:
2377 2377 parts = line.strip().split(';')
2378 2378 op = parts[1]
2379 2379 if op == 'cg':
2380 2380 pass
2381 2381 elif op == 'cgss':
2382 2382 doit(parts[2].split(' '), parts[3].split(' '))
2383 2383 elif op == 'unb':
2384 2384 doit(parts[3].split(' '), parts[2].split(' '))
2385 2385 line = logfile.readline()
2386 2386 else:
2387 2387 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2388 2388 opts.get('remote_head'))
2389 2389 localrevs = opts.get('local_head')
2390 2390 doit(localrevs, remoterevs)
2391 2391
2392 2392 @command('debugextensions', formatteropts, [], norepo=True)
2393 2393 def debugextensions(ui, **opts):
2394 2394 '''show information about active extensions'''
2395 2395 exts = extensions.extensions(ui)
2396 2396 fm = ui.formatter('debugextensions', opts)
2397 2397 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
2398 2398 extsource = extmod.__file__
2399 2399 exttestedwith = getattr(extmod, 'testedwith', None)
2400 2400 if exttestedwith is not None:
2401 2401 exttestedwith = exttestedwith.split()
2402 2402 extbuglink = getattr(extmod, 'buglink', None)
2403 2403
2404 2404 fm.startitem()
2405 2405
2406 2406 if ui.quiet or ui.verbose:
2407 2407 fm.write('name', '%s\n', extname)
2408 2408 else:
2409 2409 fm.write('name', '%s', extname)
2410 2410 if not exttestedwith:
2411 2411 fm.plain(_(' (untested!)\n'))
2412 2412 else:
2413 2413 if exttestedwith == ['internal'] or \
2414 2414 util.version() in exttestedwith:
2415 2415 fm.plain('\n')
2416 2416 else:
2417 2417 lasttestedversion = exttestedwith[-1]
2418 2418 fm.plain(' (%s!)\n' % lasttestedversion)
2419 2419
2420 2420 fm.condwrite(ui.verbose and extsource, 'source',
2421 2421 _(' location: %s\n'), extsource or "")
2422 2422
2423 2423 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
2424 2424 _(' tested with: %s\n'), ' '.join(exttestedwith or []))
2425 2425
2426 2426 fm.condwrite(ui.verbose and extbuglink, 'buglink',
2427 2427 _(' bug reporting: %s\n'), extbuglink or "")
2428 2428
2429 2429 fm.end()
2430 2430
2431 2431 @command('debugfileset',
2432 2432 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2433 2433 _('[-r REV] FILESPEC'))
2434 2434 def debugfileset(ui, repo, expr, **opts):
2435 2435 '''parse and apply a fileset specification'''
2436 2436 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2437 2437 if ui.verbose:
2438 2438 tree = fileset.parse(expr)
2439 2439 ui.note(fileset.prettyformat(tree), "\n")
2440 2440
2441 2441 for f in ctx.getfileset(expr):
2442 2442 ui.write("%s\n" % f)
2443 2443
2444 2444 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2445 2445 def debugfsinfo(ui, path="."):
2446 2446 """show information detected about current filesystem"""
2447 2447 util.writefile('.debugfsinfo', '')
2448 2448 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2449 2449 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2450 2450 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2451 2451 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2452 2452 and 'yes' or 'no'))
2453 2453 os.unlink('.debugfsinfo')
2454 2454
2455 2455 @command('debuggetbundle',
2456 2456 [('H', 'head', [], _('id of head node'), _('ID')),
2457 2457 ('C', 'common', [], _('id of common node'), _('ID')),
2458 2458 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2459 2459 _('REPO FILE [-H|-C ID]...'),
2460 2460 norepo=True)
2461 2461 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2462 2462 """retrieves a bundle from a repo
2463 2463
2464 2464 Every ID must be a full-length hex node id string. Saves the bundle to the
2465 2465 given file.
2466 2466 """
2467 2467 repo = hg.peer(ui, opts, repopath)
2468 2468 if not repo.capable('getbundle'):
2469 2469 raise error.Abort("getbundle() not supported by target repository")
2470 2470 args = {}
2471 2471 if common:
2472 2472 args['common'] = [bin(s) for s in common]
2473 2473 if head:
2474 2474 args['heads'] = [bin(s) for s in head]
2475 2475 # TODO: get desired bundlecaps from command line.
2476 2476 args['bundlecaps'] = None
2477 2477 bundle = repo.getbundle('debug', **args)
2478 2478
2479 2479 bundletype = opts.get('type', 'bzip2').lower()
2480 2480 btypes = {'none': 'HG10UN',
2481 2481 'bzip2': 'HG10BZ',
2482 2482 'gzip': 'HG10GZ',
2483 2483 'bundle2': 'HG20'}
2484 2484 bundletype = btypes.get(bundletype)
2485 2485 if bundletype not in changegroup.bundletypes:
2486 2486 raise error.Abort(_('unknown bundle type specified with --type'))
2487 2487 changegroup.writebundle(ui, bundle, bundlepath, bundletype)
2488 2488
2489 2489 @command('debugignore', [], '[FILE]')
2490 2490 def debugignore(ui, repo, *files, **opts):
2491 2491 """display the combined ignore pattern and information about ignored files
2492 2492
2493 2493 With no argument display the combined ignore pattern.
2494 2494
2495 2495 Given space separated file names, shows if the given file is ignored and
2496 2496 if so, show the ignore rule (file and line number) that matched it.
2497 2497 """
2498 2498 ignore = repo.dirstate._ignore
2499 2499 if not files:
2500 2500 # Show all the patterns
2501 2501 includepat = getattr(ignore, 'includepat', None)
2502 2502 if includepat is not None:
2503 2503 ui.write("%s\n" % includepat)
2504 2504 else:
2505 2505 raise error.Abort(_("no ignore patterns found"))
2506 2506 else:
2507 2507 for f in files:
2508 2508 nf = util.normpath(f)
2509 2509 ignored = None
2510 2510 ignoredata = None
2511 2511 if nf != '.':
2512 2512 if ignore(nf):
2513 2513 ignored = nf
2514 2514 ignoredata = repo.dirstate._ignorefileandline(nf)
2515 2515 else:
2516 2516 for p in util.finddirs(nf):
2517 2517 if ignore(p):
2518 2518 ignored = p
2519 2519 ignoredata = repo.dirstate._ignorefileandline(p)
2520 2520 break
2521 2521 if ignored:
2522 2522 if ignored == nf:
2523 2523 ui.write("%s is ignored\n" % f)
2524 2524 else:
2525 2525 ui.write("%s is ignored because of containing folder %s\n"
2526 2526 % (f, ignored))
2527 2527 ignorefile, lineno, line = ignoredata
2528 2528 ui.write("(ignore rule in %s, line %d: '%s')\n"
2529 2529 % (ignorefile, lineno, line))
2530 2530 else:
2531 2531 ui.write("%s is not ignored\n" % f)
2532 2532
2533 2533 @command('debugindex', debugrevlogopts +
2534 2534 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2535 2535 _('[-f FORMAT] -c|-m|FILE'),
2536 2536 optionalrepo=True)
2537 2537 def debugindex(ui, repo, file_=None, **opts):
2538 2538 """dump the contents of an index file"""
2539 2539 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2540 2540 format = opts.get('format', 0)
2541 2541 if format not in (0, 1):
2542 2542 raise error.Abort(_("unknown format %d") % format)
2543 2543
2544 2544 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2545 2545 if generaldelta:
2546 2546 basehdr = ' delta'
2547 2547 else:
2548 2548 basehdr = ' base'
2549 2549
2550 2550 if ui.debugflag:
2551 2551 shortfn = hex
2552 2552 else:
2553 2553 shortfn = short
2554 2554
2555 2555 # There might not be anything in r, so have a sane default
2556 2556 idlen = 12
2557 2557 for i in r:
2558 2558 idlen = len(shortfn(r.node(i)))
2559 2559 break
2560 2560
2561 2561 if format == 0:
2562 2562 ui.write(" rev offset length " + basehdr + " linkrev"
2563 2563 " %s %s p2\n" % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2564 2564 elif format == 1:
2565 2565 ui.write(" rev flag offset length"
2566 2566 " size " + basehdr + " link p1 p2"
2567 2567 " %s\n" % "nodeid".rjust(idlen))
2568 2568
2569 2569 for i in r:
2570 2570 node = r.node(i)
2571 2571 if generaldelta:
2572 2572 base = r.deltaparent(i)
2573 2573 else:
2574 2574 base = r.chainbase(i)
2575 2575 if format == 0:
2576 2576 try:
2577 2577 pp = r.parents(node)
2578 2578 except Exception:
2579 2579 pp = [nullid, nullid]
2580 2580 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2581 2581 i, r.start(i), r.length(i), base, r.linkrev(i),
2582 2582 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2583 2583 elif format == 1:
2584 2584 pr = r.parentrevs(i)
2585 2585 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2586 2586 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2587 2587 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2588 2588
2589 2589 @command('debugindexdot', debugrevlogopts,
2590 2590 _('-c|-m|FILE'), optionalrepo=True)
2591 2591 def debugindexdot(ui, repo, file_=None, **opts):
2592 2592 """dump an index DAG as a graphviz dot file"""
2593 2593 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
2594 2594 ui.write(("digraph G {\n"))
2595 2595 for i in r:
2596 2596 node = r.node(i)
2597 2597 pp = r.parents(node)
2598 2598 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2599 2599 if pp[1] != nullid:
2600 2600 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2601 2601 ui.write("}\n")
2602 2602
2603 2603 @command('debugdeltachain',
2604 2604 debugrevlogopts + formatteropts,
2605 2605 _('-c|-m|FILE'),
2606 2606 optionalrepo=True)
2607 2607 def debugdeltachain(ui, repo, file_=None, **opts):
2608 2608 """dump information about delta chains in a revlog
2609 2609
2610 2610 Output can be templatized. Available template keywords are:
2611 2611
2612 2612 rev revision number
2613 2613 chainid delta chain identifier (numbered by unique base)
2614 2614 chainlen delta chain length to this revision
2615 2615 prevrev previous revision in delta chain
2616 2616 deltatype role of delta / how it was computed
2617 2617 compsize compressed size of revision
2618 2618 uncompsize uncompressed size of revision
2619 2619 chainsize total size of compressed revisions in chain
2620 2620 chainratio total chain size divided by uncompressed revision size
2621 2621 (new delta chains typically start at ratio 2.00)
2622 2622 lindist linear distance from base revision in delta chain to end
2623 2623 of this revision
2624 2624 extradist total size of revisions not part of this delta chain from
2625 2625 base of delta chain to end of this revision; a measurement
2626 2626 of how much extra data we need to read/seek across to read
2627 2627 the delta chain for this revision
2628 2628 extraratio extradist divided by chainsize; another representation of
2629 2629 how much unrelated data is needed to load this delta chain
2630 2630 """
2631 2631 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
2632 2632 index = r.index
2633 2633 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2634 2634
2635 2635 def revinfo(rev):
2636 2636 e = index[rev]
2637 2637 compsize = e[1]
2638 2638 uncompsize = e[2]
2639 2639 chainsize = 0
2640 2640
2641 2641 if generaldelta:
2642 2642 if e[3] == e[5]:
2643 2643 deltatype = 'p1'
2644 2644 elif e[3] == e[6]:
2645 2645 deltatype = 'p2'
2646 2646 elif e[3] == rev - 1:
2647 2647 deltatype = 'prev'
2648 2648 elif e[3] == rev:
2649 2649 deltatype = 'base'
2650 2650 else:
2651 2651 deltatype = 'other'
2652 2652 else:
2653 2653 if e[3] == rev:
2654 2654 deltatype = 'base'
2655 2655 else:
2656 2656 deltatype = 'prev'
2657 2657
2658 2658 chain = r._deltachain(rev)[0]
2659 2659 for iterrev in chain:
2660 2660 e = index[iterrev]
2661 2661 chainsize += e[1]
2662 2662
2663 2663 return compsize, uncompsize, deltatype, chain, chainsize
2664 2664
2665 2665 fm = ui.formatter('debugdeltachain', opts)
2666 2666
2667 2667 fm.plain(' rev chain# chainlen prev delta '
2668 2668 'size rawsize chainsize ratio lindist extradist '
2669 2669 'extraratio\n')
2670 2670
2671 2671 chainbases = {}
2672 2672 for rev in r:
2673 2673 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
2674 2674 chainbase = chain[0]
2675 2675 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
2676 2676 basestart = r.start(chainbase)
2677 2677 revstart = r.start(rev)
2678 2678 lineardist = revstart + comp - basestart
2679 2679 extradist = lineardist - chainsize
2680 2680 try:
2681 2681 prevrev = chain[-2]
2682 2682 except IndexError:
2683 2683 prevrev = -1
2684 2684
2685 2685 chainratio = float(chainsize) / float(uncomp)
2686 2686 extraratio = float(extradist) / float(chainsize)
2687 2687
2688 2688 fm.startitem()
2689 2689 fm.write('rev chainid chainlen prevrev deltatype compsize '
2690 2690 'uncompsize chainsize chainratio lindist extradist '
2691 2691 'extraratio',
2692 2692 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f\n',
2693 2693 rev, chainid, len(chain), prevrev, deltatype, comp,
2694 2694 uncomp, chainsize, chainratio, lineardist, extradist,
2695 2695 extraratio,
2696 2696 rev=rev, chainid=chainid, chainlen=len(chain),
2697 2697 prevrev=prevrev, deltatype=deltatype, compsize=comp,
2698 2698 uncompsize=uncomp, chainsize=chainsize,
2699 2699 chainratio=chainratio, lindist=lineardist,
2700 2700 extradist=extradist, extraratio=extraratio)
2701 2701
2702 2702 fm.end()
2703 2703
2704 2704 @command('debuginstall', [] + formatteropts, '', norepo=True)
2705 2705 def debuginstall(ui, **opts):
2706 2706 '''test Mercurial installation
2707 2707
2708 2708 Returns 0 on success.
2709 2709 '''
2710 2710
2711 2711 def writetemp(contents):
2712 2712 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2713 2713 f = os.fdopen(fd, "wb")
2714 2714 f.write(contents)
2715 2715 f.close()
2716 2716 return name
2717 2717
2718 2718 problems = 0
2719 2719
2720 2720 fm = ui.formatter('debuginstall', opts)
2721 2721 fm.startitem()
2722 2722
2723 2723 # encoding
2724 2724 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
2725 2725 err = None
2726 2726 try:
2727 2727 encoding.fromlocal("test")
2728 2728 except error.Abort as inst:
2729 2729 err = inst
2730 2730 problems += 1
2731 2731 fm.condwrite(err, 'encodingerror', _(" %s\n"
2732 2732 " (check that your locale is properly set)\n"), err)
2733 2733
2734 2734 # Python
2735 2735 fm.write('pythonexe', _("checking Python executable (%s)\n"),
2736 2736 sys.executable)
2737 2737 fm.write('pythonver', _("checking Python version (%s)\n"),
2738 2738 ("%s.%s.%s" % sys.version_info[:3]))
2739 2739 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
2740 2740 os.path.dirname(os.__file__))
2741 2741
2742 2742 # compiled modules
2743 2743 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
2744 2744 os.path.dirname(__file__))
2745 2745
2746 2746 err = None
2747 2747 try:
2748 2748 from . import (
2749 2749 base85,
2750 2750 bdiff,
2751 2751 mpatch,
2752 2752 osutil,
2753 2753 )
2754 2754 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2755 2755 except Exception as inst:
2756 2756 err = inst
2757 2757 problems += 1
2758 2758 fm.condwrite(err, 'extensionserror', " %s\n", err)
2759 2759
2760 2760 # templates
2761 2761 p = templater.templatepaths()
2762 2762 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
2763 2763 fm.condwrite(not p, '', _(" no template directories found\n"))
2764 2764 if p:
2765 2765 m = templater.templatepath("map-cmdline.default")
2766 2766 if m:
2767 2767 # template found, check if it is working
2768 2768 err = None
2769 2769 try:
2770 2770 templater.templater(m)
2771 2771 except Exception as inst:
2772 2772 err = inst
2773 2773 p = None
2774 2774 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
2775 2775 else:
2776 2776 p = None
2777 2777 fm.condwrite(p, 'defaulttemplate',
2778 2778 _("checking default template (%s)\n"), m)
2779 2779 fm.condwrite(not m, 'defaulttemplatenotfound',
2780 2780 _(" template '%s' not found\n"), "default")
2781 2781 if not p:
2782 2782 problems += 1
2783 2783 fm.condwrite(not p, '',
2784 2784 _(" (templates seem to have been installed incorrectly)\n"))
2785 2785
2786 2786 # editor
2787 2787 editor = ui.geteditor()
2788 2788 editor = util.expandpath(editor)
2789 2789 fm.write('editor', _("checking commit editor... (%s)\n"), editor)
2790 2790 cmdpath = util.findexe(shlex.split(editor)[0])
2791 2791 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
2792 2792 _(" No commit editor set and can't find %s in PATH\n"
2793 2793 " (specify a commit editor in your configuration"
2794 2794 " file)\n"), not cmdpath and editor == 'vi' and editor)
2795 2795 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
2796 2796 _(" Can't find editor '%s' in PATH\n"
2797 2797 " (specify a commit editor in your configuration"
2798 2798 " file)\n"), not cmdpath and editor)
2799 2799 if not cmdpath and editor != 'vi':
2800 2800 problems += 1
2801 2801
2802 2802 # check username
2803 2803 username = None
2804 2804 err = None
2805 2805 try:
2806 2806 username = ui.username()
2807 2807 except error.Abort as e:
2808 2808 err = e
2809 2809 problems += 1
2810 2810
2811 2811 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
2812 2812 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
2813 2813 " (specify a username in your configuration file)\n"), err)
2814 2814
2815 2815 fm.condwrite(not problems, '',
2816 2816 _("no problems detected\n"))
2817 2817 if not problems:
2818 2818 fm.data(problems=problems)
2819 2819 fm.condwrite(problems, 'problems',
2820 2820 _("%s problems detected,"
2821 2821 " please check your install!\n"), problems)
2822 2822 fm.end()
2823 2823
2824 2824 return problems
2825 2825
2826 2826 @command('debugknown', [], _('REPO ID...'), norepo=True)
2827 2827 def debugknown(ui, repopath, *ids, **opts):
2828 2828 """test whether node ids are known to a repo
2829 2829
2830 2830 Every ID must be a full-length hex node id string. Returns a list of 0s
2831 2831 and 1s indicating unknown/known.
2832 2832 """
2833 2833 repo = hg.peer(ui, opts, repopath)
2834 2834 if not repo.capable('known'):
2835 2835 raise error.Abort("known() not supported by target repository")
2836 2836 flags = repo.known([bin(s) for s in ids])
2837 2837 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2838 2838
2839 2839 @command('debuglabelcomplete', [], _('LABEL...'))
2840 2840 def debuglabelcomplete(ui, repo, *args):
2841 2841 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2842 2842 debugnamecomplete(ui, repo, *args)
2843 2843
2844 2844 @command('debugmergestate', [], '')
2845 2845 def debugmergestate(ui, repo, *args):
2846 2846 """print merge state
2847 2847
2848 2848 Use --verbose to print out information about whether v1 or v2 merge state
2849 2849 was chosen."""
2850 2850 def _hashornull(h):
2851 2851 if h == nullhex:
2852 2852 return 'null'
2853 2853 else:
2854 2854 return h
2855 2855
2856 2856 def printrecords(version):
2857 2857 ui.write(('* version %s records\n') % version)
2858 2858 if version == 1:
2859 2859 records = v1records
2860 2860 else:
2861 2861 records = v2records
2862 2862
2863 2863 for rtype, record in records:
2864 2864 # pretty print some record types
2865 2865 if rtype == 'L':
2866 2866 ui.write(('local: %s\n') % record)
2867 2867 elif rtype == 'O':
2868 2868 ui.write(('other: %s\n') % record)
2869 2869 elif rtype == 'm':
2870 2870 driver, mdstate = record.split('\0', 1)
2871 2871 ui.write(('merge driver: %s (state "%s")\n')
2872 2872 % (driver, mdstate))
2873 2873 elif rtype in 'FDC':
2874 2874 r = record.split('\0')
2875 2875 f, state, hash, lfile, afile, anode, ofile = r[0:7]
2876 2876 if version == 1:
2877 2877 onode = 'not stored in v1 format'
2878 2878 flags = r[7]
2879 2879 else:
2880 2880 onode, flags = r[7:9]
2881 2881 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
2882 2882 % (f, rtype, state, _hashornull(hash)))
2883 2883 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
2884 2884 ui.write((' ancestor path: %s (node %s)\n')
2885 2885 % (afile, _hashornull(anode)))
2886 2886 ui.write((' other path: %s (node %s)\n')
2887 2887 % (ofile, _hashornull(onode)))
2888 2888 elif rtype == 'f':
2889 2889 filename, rawextras = record.split('\0', 1)
2890 2890 extras = rawextras.split('\0')
2891 2891 i = 0
2892 2892 extrastrings = []
2893 2893 while i < len(extras):
2894 2894 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
2895 2895 i += 2
2896 2896
2897 2897 ui.write(('file extras: %s (%s)\n')
2898 2898 % (filename, ', '.join(extrastrings)))
2899 2899 elif rtype == 'l':
2900 2900 labels = record.split('\0', 2)
2901 2901 labels = [l for l in labels if len(l) > 0]
2902 2902 ui.write(('labels:\n'))
2903 2903 ui.write((' local: %s\n' % labels[0]))
2904 2904 ui.write((' other: %s\n' % labels[1]))
2905 2905 if len(labels) > 2:
2906 2906 ui.write((' base: %s\n' % labels[2]))
2907 2907 else:
2908 2908 ui.write(('unrecognized entry: %s\t%s\n')
2909 2909 % (rtype, record.replace('\0', '\t')))
2910 2910
2911 2911 # Avoid mergestate.read() since it may raise an exception for unsupported
2912 2912 # merge state records. We shouldn't be doing this, but this is OK since this
2913 2913 # command is pretty low-level.
2914 2914 ms = mergemod.mergestate(repo)
2915 2915
2916 2916 # sort so that reasonable information is on top
2917 2917 v1records = ms._readrecordsv1()
2918 2918 v2records = ms._readrecordsv2()
2919 2919 order = 'LOml'
2920 2920 def key(r):
2921 2921 idx = order.find(r[0])
2922 2922 if idx == -1:
2923 2923 return (1, r[1])
2924 2924 else:
2925 2925 return (0, idx)
2926 2926 v1records.sort(key=key)
2927 2927 v2records.sort(key=key)
2928 2928
2929 2929 if not v1records and not v2records:
2930 2930 ui.write(('no merge state found\n'))
2931 2931 elif not v2records:
2932 2932 ui.note(('no version 2 merge state\n'))
2933 2933 printrecords(1)
2934 2934 elif ms._v1v2match(v1records, v2records):
2935 2935 ui.note(('v1 and v2 states match: using v2\n'))
2936 2936 printrecords(2)
2937 2937 else:
2938 2938 ui.note(('v1 and v2 states mismatch: using v1\n'))
2939 2939 printrecords(1)
2940 2940 if ui.verbose:
2941 2941 printrecords(2)
2942 2942
2943 2943 @command('debugnamecomplete', [], _('NAME...'))
2944 2944 def debugnamecomplete(ui, repo, *args):
2945 2945 '''complete "names" - tags, open branch names, bookmark names'''
2946 2946
2947 2947 names = set()
2948 2948 # since we previously only listed open branches, we will handle that
2949 2949 # specially (after this for loop)
2950 2950 for name, ns in repo.names.iteritems():
2951 2951 if name != 'branches':
2952 2952 names.update(ns.listnames(repo))
2953 2953 names.update(tag for (tag, heads, tip, closed)
2954 2954 in repo.branchmap().iterbranches() if not closed)
2955 2955 completions = set()
2956 2956 if not args:
2957 2957 args = ['']
2958 2958 for a in args:
2959 2959 completions.update(n for n in names if n.startswith(a))
2960 2960 ui.write('\n'.join(sorted(completions)))
2961 2961 ui.write('\n')
2962 2962
2963 2963 @command('debuglocks',
2964 2964 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2965 2965 ('W', 'force-wlock', None,
2966 2966 _('free the working state lock (DANGEROUS)'))],
2967 2967 _('[OPTION]...'))
2968 2968 def debuglocks(ui, repo, **opts):
2969 2969 """show or modify state of locks
2970 2970
2971 2971 By default, this command will show which locks are held. This
2972 2972 includes the user and process holding the lock, the amount of time
2973 2973 the lock has been held, and the machine name where the process is
2974 2974 running if it's not local.
2975 2975
2976 2976 Locks protect the integrity of Mercurial's data, so should be
2977 2977 treated with care. System crashes or other interruptions may cause
2978 2978 locks to not be properly released, though Mercurial will usually
2979 2979 detect and remove such stale locks automatically.
2980 2980
2981 2981 However, detecting stale locks may not always be possible (for
2982 2982 instance, on a shared filesystem). Removing locks may also be
2983 2983 blocked by filesystem permissions.
2984 2984
2985 2985 Returns 0 if no locks are held.
2986 2986
2987 2987 """
2988 2988
2989 2989 if opts.get('force_lock'):
2990 2990 repo.svfs.unlink('lock')
2991 2991 if opts.get('force_wlock'):
2992 2992 repo.vfs.unlink('wlock')
2993 2993 if opts.get('force_lock') or opts.get('force_lock'):
2994 2994 return 0
2995 2995
2996 2996 now = time.time()
2997 2997 held = 0
2998 2998
2999 2999 def report(vfs, name, method):
3000 3000 # this causes stale locks to get reaped for more accurate reporting
3001 3001 try:
3002 3002 l = method(False)
3003 3003 except error.LockHeld:
3004 3004 l = None
3005 3005
3006 3006 if l:
3007 3007 l.release()
3008 3008 else:
3009 3009 try:
3010 3010 stat = vfs.lstat(name)
3011 3011 age = now - stat.st_mtime
3012 3012 user = util.username(stat.st_uid)
3013 3013 locker = vfs.readlock(name)
3014 3014 if ":" in locker:
3015 3015 host, pid = locker.split(':')
3016 3016 if host == socket.gethostname():
3017 3017 locker = 'user %s, process %s' % (user, pid)
3018 3018 else:
3019 3019 locker = 'user %s, process %s, host %s' \
3020 3020 % (user, pid, host)
3021 3021 ui.write("%-6s %s (%ds)\n" % (name + ":", locker, age))
3022 3022 return 1
3023 3023 except OSError as e:
3024 3024 if e.errno != errno.ENOENT:
3025 3025 raise
3026 3026
3027 3027 ui.write("%-6s free\n" % (name + ":"))
3028 3028 return 0
3029 3029
3030 3030 held += report(repo.svfs, "lock", repo.lock)
3031 3031 held += report(repo.vfs, "wlock", repo.wlock)
3032 3032
3033 3033 return held
3034 3034
3035 3035 @command('debugobsolete',
3036 3036 [('', 'flags', 0, _('markers flag')),
3037 3037 ('', 'record-parents', False,
3038 3038 _('record parent information for the precursor')),
3039 3039 ('r', 'rev', [], _('display markers relevant to REV')),
3040 3040 ('', 'index', False, _('display index of the marker')),
3041 3041 ] + commitopts2,
3042 3042 _('[OBSOLETED [REPLACEMENT ...]]'))
3043 3043 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
3044 3044 """create arbitrary obsolete marker
3045 3045
3046 3046 With no arguments, displays the list of obsolescence markers."""
3047 3047
3048 3048 def parsenodeid(s):
3049 3049 try:
3050 3050 # We do not use revsingle/revrange functions here to accept
3051 3051 # arbitrary node identifiers, possibly not present in the
3052 3052 # local repository.
3053 3053 n = bin(s)
3054 3054 if len(n) != len(nullid):
3055 3055 raise TypeError()
3056 3056 return n
3057 3057 except TypeError:
3058 3058 raise error.Abort('changeset references must be full hexadecimal '
3059 3059 'node identifiers')
3060 3060
3061 3061 if precursor is not None:
3062 3062 if opts['rev']:
3063 3063 raise error.Abort('cannot select revision when creating marker')
3064 3064 metadata = {}
3065 3065 metadata['user'] = opts['user'] or ui.username()
3066 3066 succs = tuple(parsenodeid(succ) for succ in successors)
3067 3067 l = repo.lock()
3068 3068 try:
3069 3069 tr = repo.transaction('debugobsolete')
3070 3070 try:
3071 3071 date = opts.get('date')
3072 3072 if date:
3073 3073 date = util.parsedate(date)
3074 3074 else:
3075 3075 date = None
3076 3076 prec = parsenodeid(precursor)
3077 3077 parents = None
3078 3078 if opts['record_parents']:
3079 3079 if prec not in repo.unfiltered():
3080 3080 raise error.Abort('cannot used --record-parents on '
3081 3081 'unknown changesets')
3082 3082 parents = repo.unfiltered()[prec].parents()
3083 3083 parents = tuple(p.node() for p in parents)
3084 3084 repo.obsstore.create(tr, prec, succs, opts['flags'],
3085 3085 parents=parents, date=date,
3086 3086 metadata=metadata)
3087 3087 tr.close()
3088 3088 except ValueError as exc:
3089 3089 raise error.Abort(_('bad obsmarker input: %s') % exc)
3090 3090 finally:
3091 3091 tr.release()
3092 3092 finally:
3093 3093 l.release()
3094 3094 else:
3095 3095 if opts['rev']:
3096 3096 revs = scmutil.revrange(repo, opts['rev'])
3097 3097 nodes = [repo[r].node() for r in revs]
3098 3098 markers = list(obsolete.getmarkers(repo, nodes=nodes))
3099 3099 markers.sort(key=lambda x: x._data)
3100 3100 else:
3101 3101 markers = obsolete.getmarkers(repo)
3102 3102
3103 3103 for i, m in enumerate(markers):
3104 3104 ind = i if opts.get('index') else None
3105 3105 cmdutil.showmarker(ui, m, index=ind)
3106 3106
3107 3107 @command('debugpathcomplete',
3108 3108 [('f', 'full', None, _('complete an entire path')),
3109 3109 ('n', 'normal', None, _('show only normal files')),
3110 3110 ('a', 'added', None, _('show only added files')),
3111 3111 ('r', 'removed', None, _('show only removed files'))],
3112 3112 _('FILESPEC...'))
3113 3113 def debugpathcomplete(ui, repo, *specs, **opts):
3114 3114 '''complete part or all of a tracked path
3115 3115
3116 3116 This command supports shells that offer path name completion. It
3117 3117 currently completes only files already known to the dirstate.
3118 3118
3119 3119 Completion extends only to the next path segment unless
3120 3120 --full is specified, in which case entire paths are used.'''
3121 3121
3122 3122 def complete(path, acceptable):
3123 3123 dirstate = repo.dirstate
3124 3124 spec = os.path.normpath(os.path.join(os.getcwd(), path))
3125 3125 rootdir = repo.root + os.sep
3126 3126 if spec != repo.root and not spec.startswith(rootdir):
3127 3127 return [], []
3128 3128 if os.path.isdir(spec):
3129 3129 spec += '/'
3130 3130 spec = spec[len(rootdir):]
3131 3131 fixpaths = os.sep != '/'
3132 3132 if fixpaths:
3133 3133 spec = spec.replace(os.sep, '/')
3134 3134 speclen = len(spec)
3135 3135 fullpaths = opts['full']
3136 3136 files, dirs = set(), set()
3137 3137 adddir, addfile = dirs.add, files.add
3138 3138 for f, st in dirstate.iteritems():
3139 3139 if f.startswith(spec) and st[0] in acceptable:
3140 3140 if fixpaths:
3141 3141 f = f.replace('/', os.sep)
3142 3142 if fullpaths:
3143 3143 addfile(f)
3144 3144 continue
3145 3145 s = f.find(os.sep, speclen)
3146 3146 if s >= 0:
3147 3147 adddir(f[:s])
3148 3148 else:
3149 3149 addfile(f)
3150 3150 return files, dirs
3151 3151
3152 3152 acceptable = ''
3153 3153 if opts['normal']:
3154 3154 acceptable += 'nm'
3155 3155 if opts['added']:
3156 3156 acceptable += 'a'
3157 3157 if opts['removed']:
3158 3158 acceptable += 'r'
3159 3159 cwd = repo.getcwd()
3160 3160 if not specs:
3161 3161 specs = ['.']
3162 3162
3163 3163 files, dirs = set(), set()
3164 3164 for spec in specs:
3165 3165 f, d = complete(spec, acceptable or 'nmar')
3166 3166 files.update(f)
3167 3167 dirs.update(d)
3168 3168 files.update(dirs)
3169 3169 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
3170 3170 ui.write('\n')
3171 3171
3172 3172 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
3173 3173 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
3174 3174 '''access the pushkey key/value protocol
3175 3175
3176 3176 With two args, list the keys in the given namespace.
3177 3177
3178 3178 With five args, set a key to new if it currently is set to old.
3179 3179 Reports success or failure.
3180 3180 '''
3181 3181
3182 3182 target = hg.peer(ui, {}, repopath)
3183 3183 if keyinfo:
3184 3184 key, old, new = keyinfo
3185 3185 r = target.pushkey(namespace, key, old, new)
3186 3186 ui.status(str(r) + '\n')
3187 3187 return not r
3188 3188 else:
3189 3189 for k, v in sorted(target.listkeys(namespace).iteritems()):
3190 3190 ui.write("%s\t%s\n" % (k.encode('string-escape'),
3191 3191 v.encode('string-escape')))
3192 3192
3193 3193 @command('debugpvec', [], _('A B'))
3194 3194 def debugpvec(ui, repo, a, b=None):
3195 3195 ca = scmutil.revsingle(repo, a)
3196 3196 cb = scmutil.revsingle(repo, b)
3197 3197 pa = pvec.ctxpvec(ca)
3198 3198 pb = pvec.ctxpvec(cb)
3199 3199 if pa == pb:
3200 3200 rel = "="
3201 3201 elif pa > pb:
3202 3202 rel = ">"
3203 3203 elif pa < pb:
3204 3204 rel = "<"
3205 3205 elif pa | pb:
3206 3206 rel = "|"
3207 3207 ui.write(_("a: %s\n") % pa)
3208 3208 ui.write(_("b: %s\n") % pb)
3209 3209 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
3210 3210 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
3211 3211 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
3212 3212 pa.distance(pb), rel))
3213 3213
3214 3214 @command('debugrebuilddirstate|debugrebuildstate',
3215 3215 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
3216 3216 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
3217 3217 'the working copy parent')),
3218 3218 ],
3219 3219 _('[-r REV]'))
3220 3220 def debugrebuilddirstate(ui, repo, rev, **opts):
3221 3221 """rebuild the dirstate as it would look like for the given revision
3222 3222
3223 3223 If no revision is specified the first current parent will be used.
3224 3224
3225 3225 The dirstate will be set to the files of the given revision.
3226 3226 The actual working directory content or existing dirstate
3227 3227 information such as adds or removes is not considered.
3228 3228
3229 3229 ``minimal`` will only rebuild the dirstate status for files that claim to be
3230 3230 tracked but are not in the parent manifest, or that exist in the parent
3231 3231 manifest but are not in the dirstate. It will not change adds, removes, or
3232 3232 modified files that are in the working copy parent.
3233 3233
3234 3234 One use of this command is to make the next :hg:`status` invocation
3235 3235 check the actual file content.
3236 3236 """
3237 3237 ctx = scmutil.revsingle(repo, rev)
3238 3238 with repo.wlock():
3239 3239 dirstate = repo.dirstate
3240 3240 changedfiles = None
3241 3241 # See command doc for what minimal does.
3242 3242 if opts.get('minimal'):
3243 3243 manifestfiles = set(ctx.manifest().keys())
3244 3244 dirstatefiles = set(dirstate)
3245 3245 manifestonly = manifestfiles - dirstatefiles
3246 3246 dsonly = dirstatefiles - manifestfiles
3247 3247 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
3248 3248 changedfiles = manifestonly | dsnotadded
3249 3249
3250 3250 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
3251 3251
3252 3252 @command('debugrebuildfncache', [], '')
3253 3253 def debugrebuildfncache(ui, repo):
3254 3254 """rebuild the fncache file"""
3255 3255 repair.rebuildfncache(ui, repo)
3256 3256
3257 3257 @command('debugrename',
3258 3258 [('r', 'rev', '', _('revision to debug'), _('REV'))],
3259 3259 _('[-r REV] FILE'))
3260 3260 def debugrename(ui, repo, file1, *pats, **opts):
3261 3261 """dump rename information"""
3262 3262
3263 3263 ctx = scmutil.revsingle(repo, opts.get('rev'))
3264 3264 m = scmutil.match(ctx, (file1,) + pats, opts)
3265 3265 for abs in ctx.walk(m):
3266 3266 fctx = ctx[abs]
3267 3267 o = fctx.filelog().renamed(fctx.filenode())
3268 3268 rel = m.rel(abs)
3269 3269 if o:
3270 3270 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
3271 3271 else:
3272 3272 ui.write(_("%s not renamed\n") % rel)
3273 3273
3274 3274 @command('debugrevlog', debugrevlogopts +
3275 3275 [('d', 'dump', False, _('dump index data'))],
3276 3276 _('-c|-m|FILE'),
3277 3277 optionalrepo=True)
3278 3278 def debugrevlog(ui, repo, file_=None, **opts):
3279 3279 """show data and statistics about a revlog"""
3280 3280 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
3281 3281
3282 3282 if opts.get("dump"):
3283 3283 numrevs = len(r)
3284 3284 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
3285 3285 " rawsize totalsize compression heads chainlen\n")
3286 3286 ts = 0
3287 3287 heads = set()
3288 3288
3289 3289 for rev in xrange(numrevs):
3290 3290 dbase = r.deltaparent(rev)
3291 3291 if dbase == -1:
3292 3292 dbase = rev
3293 3293 cbase = r.chainbase(rev)
3294 3294 clen = r.chainlen(rev)
3295 3295 p1, p2 = r.parentrevs(rev)
3296 3296 rs = r.rawsize(rev)
3297 3297 ts = ts + rs
3298 3298 heads -= set(r.parentrevs(rev))
3299 3299 heads.add(rev)
3300 3300 try:
3301 3301 compression = ts / r.end(rev)
3302 3302 except ZeroDivisionError:
3303 3303 compression = 0
3304 3304 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
3305 3305 "%11d %5d %8d\n" %
3306 3306 (rev, p1, p2, r.start(rev), r.end(rev),
3307 3307 r.start(dbase), r.start(cbase),
3308 3308 r.start(p1), r.start(p2),
3309 3309 rs, ts, compression, len(heads), clen))
3310 3310 return 0
3311 3311
3312 3312 v = r.version
3313 3313 format = v & 0xFFFF
3314 3314 flags = []
3315 3315 gdelta = False
3316 3316 if v & revlog.REVLOGNGINLINEDATA:
3317 3317 flags.append('inline')
3318 3318 if v & revlog.REVLOGGENERALDELTA:
3319 3319 gdelta = True
3320 3320 flags.append('generaldelta')
3321 3321 if not flags:
3322 3322 flags = ['(none)']
3323 3323
3324 3324 nummerges = 0
3325 3325 numfull = 0
3326 3326 numprev = 0
3327 3327 nump1 = 0
3328 3328 nump2 = 0
3329 3329 numother = 0
3330 3330 nump1prev = 0
3331 3331 nump2prev = 0
3332 3332 chainlengths = []
3333 3333
3334 3334 datasize = [None, 0, 0L]
3335 3335 fullsize = [None, 0, 0L]
3336 3336 deltasize = [None, 0, 0L]
3337 3337
3338 3338 def addsize(size, l):
3339 3339 if l[0] is None or size < l[0]:
3340 3340 l[0] = size
3341 3341 if size > l[1]:
3342 3342 l[1] = size
3343 3343 l[2] += size
3344 3344
3345 3345 numrevs = len(r)
3346 3346 for rev in xrange(numrevs):
3347 3347 p1, p2 = r.parentrevs(rev)
3348 3348 delta = r.deltaparent(rev)
3349 3349 if format > 0:
3350 3350 addsize(r.rawsize(rev), datasize)
3351 3351 if p2 != nullrev:
3352 3352 nummerges += 1
3353 3353 size = r.length(rev)
3354 3354 if delta == nullrev:
3355 3355 chainlengths.append(0)
3356 3356 numfull += 1
3357 3357 addsize(size, fullsize)
3358 3358 else:
3359 3359 chainlengths.append(chainlengths[delta] + 1)
3360 3360 addsize(size, deltasize)
3361 3361 if delta == rev - 1:
3362 3362 numprev += 1
3363 3363 if delta == p1:
3364 3364 nump1prev += 1
3365 3365 elif delta == p2:
3366 3366 nump2prev += 1
3367 3367 elif delta == p1:
3368 3368 nump1 += 1
3369 3369 elif delta == p2:
3370 3370 nump2 += 1
3371 3371 elif delta != nullrev:
3372 3372 numother += 1
3373 3373
3374 3374 # Adjust size min value for empty cases
3375 3375 for size in (datasize, fullsize, deltasize):
3376 3376 if size[0] is None:
3377 3377 size[0] = 0
3378 3378
3379 3379 numdeltas = numrevs - numfull
3380 3380 numoprev = numprev - nump1prev - nump2prev
3381 3381 totalrawsize = datasize[2]
3382 3382 datasize[2] /= numrevs
3383 3383 fulltotal = fullsize[2]
3384 3384 fullsize[2] /= numfull
3385 3385 deltatotal = deltasize[2]
3386 3386 if numrevs - numfull > 0:
3387 3387 deltasize[2] /= numrevs - numfull
3388 3388 totalsize = fulltotal + deltatotal
3389 3389 avgchainlen = sum(chainlengths) / numrevs
3390 3390 maxchainlen = max(chainlengths)
3391 3391 compratio = 1
3392 3392 if totalsize:
3393 3393 compratio = totalrawsize / totalsize
3394 3394
3395 3395 basedfmtstr = '%%%dd\n'
3396 3396 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
3397 3397
3398 3398 def dfmtstr(max):
3399 3399 return basedfmtstr % len(str(max))
3400 3400 def pcfmtstr(max, padding=0):
3401 3401 return basepcfmtstr % (len(str(max)), ' ' * padding)
3402 3402
3403 3403 def pcfmt(value, total):
3404 3404 if total:
3405 3405 return (value, 100 * float(value) / total)
3406 3406 else:
3407 3407 return value, 100.0
3408 3408
3409 3409 ui.write(('format : %d\n') % format)
3410 3410 ui.write(('flags : %s\n') % ', '.join(flags))
3411 3411
3412 3412 ui.write('\n')
3413 3413 fmt = pcfmtstr(totalsize)
3414 3414 fmt2 = dfmtstr(totalsize)
3415 3415 ui.write(('revisions : ') + fmt2 % numrevs)
3416 3416 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
3417 3417 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
3418 3418 ui.write(('revisions : ') + fmt2 % numrevs)
3419 3419 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
3420 3420 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
3421 3421 ui.write(('revision size : ') + fmt2 % totalsize)
3422 3422 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
3423 3423 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
3424 3424
3425 3425 ui.write('\n')
3426 3426 fmt = dfmtstr(max(avgchainlen, compratio))
3427 3427 ui.write(('avg chain length : ') + fmt % avgchainlen)
3428 3428 ui.write(('max chain length : ') + fmt % maxchainlen)
3429 3429 ui.write(('compression ratio : ') + fmt % compratio)
3430 3430
3431 3431 if format > 0:
3432 3432 ui.write('\n')
3433 3433 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
3434 3434 % tuple(datasize))
3435 3435 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
3436 3436 % tuple(fullsize))
3437 3437 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
3438 3438 % tuple(deltasize))
3439 3439
3440 3440 if numdeltas > 0:
3441 3441 ui.write('\n')
3442 3442 fmt = pcfmtstr(numdeltas)
3443 3443 fmt2 = pcfmtstr(numdeltas, 4)
3444 3444 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
3445 3445 if numprev > 0:
3446 3446 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
3447 3447 numprev))
3448 3448 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
3449 3449 numprev))
3450 3450 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
3451 3451 numprev))
3452 3452 if gdelta:
3453 3453 ui.write(('deltas against p1 : ')
3454 3454 + fmt % pcfmt(nump1, numdeltas))
3455 3455 ui.write(('deltas against p2 : ')
3456 3456 + fmt % pcfmt(nump2, numdeltas))
3457 3457 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
3458 3458 numdeltas))
3459 3459
3460 3460 @command('debugrevspec',
3461 3461 [('', 'optimize', None, _('print parsed tree after optimizing'))],
3462 3462 ('REVSPEC'))
3463 3463 def debugrevspec(ui, repo, expr, **opts):
3464 3464 """parse and apply a revision specification
3465 3465
3466 3466 Use --verbose to print the parsed tree before and after aliases
3467 3467 expansion.
3468 3468 """
3469 3469 if ui.verbose:
3470 3470 tree = revset.parse(expr, lookup=repo.__contains__)
3471 3471 ui.note(revset.prettyformat(tree), "\n")
3472 3472 newtree = revset.findaliases(ui, tree)
3473 3473 if newtree != tree:
3474 3474 ui.note("* expanded:\n", revset.prettyformat(newtree), "\n")
3475 3475 tree = newtree
3476 3476 newtree = revset.foldconcat(tree)
3477 3477 if newtree != tree:
3478 3478 ui.note("* concatenated:\n", revset.prettyformat(newtree), "\n")
3479 3479 if opts["optimize"]:
3480 3480 weight, optimizedtree = revset.optimize(newtree, True)
3481 3481 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
3482 3482 func = revset.match(ui, expr, repo)
3483 3483 revs = func(repo)
3484 3484 if ui.verbose:
3485 3485 ui.note("* set:\n", revset.prettyformatset(revs), "\n")
3486 3486 for c in revs:
3487 3487 ui.write("%s\n" % c)
3488 3488
3489 3489 @command('debugsetparents', [], _('REV1 [REV2]'))
3490 3490 def debugsetparents(ui, repo, rev1, rev2=None):
3491 3491 """manually set the parents of the current working directory
3492 3492
3493 3493 This is useful for writing repository conversion tools, but should
3494 3494 be used with care. For example, neither the working directory nor the
3495 3495 dirstate is updated, so file status may be incorrect after running this
3496 3496 command.
3497 3497
3498 3498 Returns 0 on success.
3499 3499 """
3500 3500
3501 3501 r1 = scmutil.revsingle(repo, rev1).node()
3502 3502 r2 = scmutil.revsingle(repo, rev2, 'null').node()
3503 3503
3504 3504 with repo.wlock():
3505 repo.dirstate.beginparentchange()
3506 3505 repo.setparents(r1, r2)
3507 repo.dirstate.endparentchange()
3508 3506
3509 3507 @command('debugdirstate|debugstate',
3510 3508 [('', 'nodates', None, _('do not display the saved mtime')),
3511 3509 ('', 'datesort', None, _('sort by saved mtime'))],
3512 3510 _('[OPTION]...'))
3513 3511 def debugstate(ui, repo, **opts):
3514 3512 """show the contents of the current dirstate"""
3515 3513
3516 3514 nodates = opts.get('nodates')
3517 3515 datesort = opts.get('datesort')
3518 3516
3519 3517 timestr = ""
3520 3518 if datesort:
3521 3519 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
3522 3520 else:
3523 3521 keyfunc = None # sort by filename
3524 3522 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
3525 3523 if ent[3] == -1:
3526 3524 timestr = 'unset '
3527 3525 elif nodates:
3528 3526 timestr = 'set '
3529 3527 else:
3530 3528 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
3531 3529 time.localtime(ent[3]))
3532 3530 if ent[1] & 0o20000:
3533 3531 mode = 'lnk'
3534 3532 else:
3535 3533 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
3536 3534 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
3537 3535 for f in repo.dirstate.copies():
3538 3536 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
3539 3537
3540 3538 @command('debugsub',
3541 3539 [('r', 'rev', '',
3542 3540 _('revision to check'), _('REV'))],
3543 3541 _('[-r REV] [REV]'))
3544 3542 def debugsub(ui, repo, rev=None):
3545 3543 ctx = scmutil.revsingle(repo, rev, None)
3546 3544 for k, v in sorted(ctx.substate.items()):
3547 3545 ui.write(('path %s\n') % k)
3548 3546 ui.write((' source %s\n') % v[0])
3549 3547 ui.write((' revision %s\n') % v[1])
3550 3548
3551 3549 @command('debugsuccessorssets',
3552 3550 [],
3553 3551 _('[REV]'))
3554 3552 def debugsuccessorssets(ui, repo, *revs):
3555 3553 """show set of successors for revision
3556 3554
3557 3555 A successors set of changeset A is a consistent group of revisions that
3558 3556 succeed A. It contains non-obsolete changesets only.
3559 3557
3560 3558 In most cases a changeset A has a single successors set containing a single
3561 3559 successor (changeset A replaced by A').
3562 3560
3563 3561 A changeset that is made obsolete with no successors are called "pruned".
3564 3562 Such changesets have no successors sets at all.
3565 3563
3566 3564 A changeset that has been "split" will have a successors set containing
3567 3565 more than one successor.
3568 3566
3569 3567 A changeset that has been rewritten in multiple different ways is called
3570 3568 "divergent". Such changesets have multiple successor sets (each of which
3571 3569 may also be split, i.e. have multiple successors).
3572 3570
3573 3571 Results are displayed as follows::
3574 3572
3575 3573 <rev1>
3576 3574 <successors-1A>
3577 3575 <rev2>
3578 3576 <successors-2A>
3579 3577 <successors-2B1> <successors-2B2> <successors-2B3>
3580 3578
3581 3579 Here rev2 has two possible (i.e. divergent) successors sets. The first
3582 3580 holds one element, whereas the second holds three (i.e. the changeset has
3583 3581 been split).
3584 3582 """
3585 3583 # passed to successorssets caching computation from one call to another
3586 3584 cache = {}
3587 3585 ctx2str = str
3588 3586 node2str = short
3589 3587 if ui.debug():
3590 3588 def ctx2str(ctx):
3591 3589 return ctx.hex()
3592 3590 node2str = hex
3593 3591 for rev in scmutil.revrange(repo, revs):
3594 3592 ctx = repo[rev]
3595 3593 ui.write('%s\n'% ctx2str(ctx))
3596 3594 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
3597 3595 if succsset:
3598 3596 ui.write(' ')
3599 3597 ui.write(node2str(succsset[0]))
3600 3598 for node in succsset[1:]:
3601 3599 ui.write(' ')
3602 3600 ui.write(node2str(node))
3603 3601 ui.write('\n')
3604 3602
3605 3603 @command('debugtemplate',
3606 3604 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
3607 3605 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
3608 3606 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
3609 3607 optionalrepo=True)
3610 3608 def debugtemplate(ui, repo, tmpl, **opts):
3611 3609 """parse and apply a template
3612 3610
3613 3611 If -r/--rev is given, the template is processed as a log template and
3614 3612 applied to the given changesets. Otherwise, it is processed as a generic
3615 3613 template.
3616 3614
3617 3615 Use --verbose to print the parsed tree.
3618 3616 """
3619 3617 revs = None
3620 3618 if opts['rev']:
3621 3619 if repo is None:
3622 3620 raise error.RepoError(_('there is no Mercurial repository here '
3623 3621 '(.hg not found)'))
3624 3622 revs = scmutil.revrange(repo, opts['rev'])
3625 3623
3626 3624 props = {}
3627 3625 for d in opts['define']:
3628 3626 try:
3629 3627 k, v = (e.strip() for e in d.split('=', 1))
3630 3628 if not k:
3631 3629 raise ValueError
3632 3630 props[k] = v
3633 3631 except ValueError:
3634 3632 raise error.Abort(_('malformed keyword definition: %s') % d)
3635 3633
3636 3634 if ui.verbose:
3637 3635 tree = templater.parse(tmpl)
3638 3636 ui.note(templater.prettyformat(tree), '\n')
3639 3637
3640 3638 mapfile = None
3641 3639 if revs is None:
3642 3640 k = 'debugtemplate'
3643 3641 t = templater.templater(mapfile)
3644 3642 t.cache[k] = tmpl
3645 3643 ui.write(templater.stringify(t(k, **props)))
3646 3644 else:
3647 3645 displayer = cmdutil.changeset_templater(ui, repo, None, opts, tmpl,
3648 3646 mapfile, buffered=False)
3649 3647 for r in revs:
3650 3648 displayer.show(repo[r], **props)
3651 3649 displayer.close()
3652 3650
3653 3651 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
3654 3652 def debugwalk(ui, repo, *pats, **opts):
3655 3653 """show how files match on given patterns"""
3656 3654 m = scmutil.match(repo[None], pats, opts)
3657 3655 items = list(repo.walk(m))
3658 3656 if not items:
3659 3657 return
3660 3658 f = lambda fn: fn
3661 3659 if ui.configbool('ui', 'slash') and os.sep != '/':
3662 3660 f = lambda fn: util.normpath(fn)
3663 3661 fmt = 'f %%-%ds %%-%ds %%s' % (
3664 3662 max([len(abs) for abs in items]),
3665 3663 max([len(m.rel(abs)) for abs in items]))
3666 3664 for abs in items:
3667 3665 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3668 3666 ui.write("%s\n" % line.rstrip())
3669 3667
3670 3668 @command('debugwireargs',
3671 3669 [('', 'three', '', 'three'),
3672 3670 ('', 'four', '', 'four'),
3673 3671 ('', 'five', '', 'five'),
3674 3672 ] + remoteopts,
3675 3673 _('REPO [OPTIONS]... [ONE [TWO]]'),
3676 3674 norepo=True)
3677 3675 def debugwireargs(ui, repopath, *vals, **opts):
3678 3676 repo = hg.peer(ui, opts, repopath)
3679 3677 for opt in remoteopts:
3680 3678 del opts[opt[1]]
3681 3679 args = {}
3682 3680 for k, v in opts.iteritems():
3683 3681 if v:
3684 3682 args[k] = v
3685 3683 # run twice to check that we don't mess up the stream for the next command
3686 3684 res1 = repo.debugwireargs(*vals, **args)
3687 3685 res2 = repo.debugwireargs(*vals, **args)
3688 3686 ui.write("%s\n" % res1)
3689 3687 if res1 != res2:
3690 3688 ui.warn("%s\n" % res2)
3691 3689
3692 3690 @command('^diff',
3693 3691 [('r', 'rev', [], _('revision'), _('REV')),
3694 3692 ('c', 'change', '', _('change made by revision'), _('REV'))
3695 3693 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3696 3694 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3697 3695 inferrepo=True)
3698 3696 def diff(ui, repo, *pats, **opts):
3699 3697 """diff repository (or selected files)
3700 3698
3701 3699 Show differences between revisions for the specified files.
3702 3700
3703 3701 Differences between files are shown using the unified diff format.
3704 3702
3705 3703 .. note::
3706 3704
3707 3705 :hg:`diff` may generate unexpected results for merges, as it will
3708 3706 default to comparing against the working directory's first
3709 3707 parent changeset if no revisions are specified.
3710 3708
3711 3709 When two revision arguments are given, then changes are shown
3712 3710 between those revisions. If only one revision is specified then
3713 3711 that revision is compared to the working directory, and, when no
3714 3712 revisions are specified, the working directory files are compared
3715 3713 to its first parent.
3716 3714
3717 3715 Alternatively you can specify -c/--change with a revision to see
3718 3716 the changes in that changeset relative to its first parent.
3719 3717
3720 3718 Without the -a/--text option, diff will avoid generating diffs of
3721 3719 files it detects as binary. With -a, diff will generate a diff
3722 3720 anyway, probably with undesirable results.
3723 3721
3724 3722 Use the -g/--git option to generate diffs in the git extended diff
3725 3723 format. For more information, read :hg:`help diffs`.
3726 3724
3727 3725 .. container:: verbose
3728 3726
3729 3727 Examples:
3730 3728
3731 3729 - compare a file in the current working directory to its parent::
3732 3730
3733 3731 hg diff foo.c
3734 3732
3735 3733 - compare two historical versions of a directory, with rename info::
3736 3734
3737 3735 hg diff --git -r 1.0:1.2 lib/
3738 3736
3739 3737 - get change stats relative to the last change on some date::
3740 3738
3741 3739 hg diff --stat -r "date('may 2')"
3742 3740
3743 3741 - diff all newly-added files that contain a keyword::
3744 3742
3745 3743 hg diff "set:added() and grep(GNU)"
3746 3744
3747 3745 - compare a revision and its parents::
3748 3746
3749 3747 hg diff -c 9353 # compare against first parent
3750 3748 hg diff -r 9353^:9353 # same using revset syntax
3751 3749 hg diff -r 9353^2:9353 # compare against the second parent
3752 3750
3753 3751 Returns 0 on success.
3754 3752 """
3755 3753
3756 3754 revs = opts.get('rev')
3757 3755 change = opts.get('change')
3758 3756 stat = opts.get('stat')
3759 3757 reverse = opts.get('reverse')
3760 3758
3761 3759 if revs and change:
3762 3760 msg = _('cannot specify --rev and --change at the same time')
3763 3761 raise error.Abort(msg)
3764 3762 elif change:
3765 3763 node2 = scmutil.revsingle(repo, change, None).node()
3766 3764 node1 = repo[node2].p1().node()
3767 3765 else:
3768 3766 node1, node2 = scmutil.revpair(repo, revs)
3769 3767
3770 3768 if reverse:
3771 3769 node1, node2 = node2, node1
3772 3770
3773 3771 diffopts = patch.diffallopts(ui, opts)
3774 3772 m = scmutil.match(repo[node2], pats, opts)
3775 3773 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3776 3774 listsubrepos=opts.get('subrepos'),
3777 3775 root=opts.get('root'))
3778 3776
3779 3777 @command('^export',
3780 3778 [('o', 'output', '',
3781 3779 _('print output to file with formatted name'), _('FORMAT')),
3782 3780 ('', 'switch-parent', None, _('diff against the second parent')),
3783 3781 ('r', 'rev', [], _('revisions to export'), _('REV')),
3784 3782 ] + diffopts,
3785 3783 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3786 3784 def export(ui, repo, *changesets, **opts):
3787 3785 """dump the header and diffs for one or more changesets
3788 3786
3789 3787 Print the changeset header and diffs for one or more revisions.
3790 3788 If no revision is given, the parent of the working directory is used.
3791 3789
3792 3790 The information shown in the changeset header is: author, date,
3793 3791 branch name (if non-default), changeset hash, parent(s) and commit
3794 3792 comment.
3795 3793
3796 3794 .. note::
3797 3795
3798 3796 :hg:`export` may generate unexpected diff output for merge
3799 3797 changesets, as it will compare the merge changeset against its
3800 3798 first parent only.
3801 3799
3802 3800 Output may be to a file, in which case the name of the file is
3803 3801 given using a format string. The formatting rules are as follows:
3804 3802
3805 3803 :``%%``: literal "%" character
3806 3804 :``%H``: changeset hash (40 hexadecimal digits)
3807 3805 :``%N``: number of patches being generated
3808 3806 :``%R``: changeset revision number
3809 3807 :``%b``: basename of the exporting repository
3810 3808 :``%h``: short-form changeset hash (12 hexadecimal digits)
3811 3809 :``%m``: first line of the commit message (only alphanumeric characters)
3812 3810 :``%n``: zero-padded sequence number, starting at 1
3813 3811 :``%r``: zero-padded changeset revision number
3814 3812
3815 3813 Without the -a/--text option, export will avoid generating diffs
3816 3814 of files it detects as binary. With -a, export will generate a
3817 3815 diff anyway, probably with undesirable results.
3818 3816
3819 3817 Use the -g/--git option to generate diffs in the git extended diff
3820 3818 format. See :hg:`help diffs` for more information.
3821 3819
3822 3820 With the --switch-parent option, the diff will be against the
3823 3821 second parent. It can be useful to review a merge.
3824 3822
3825 3823 .. container:: verbose
3826 3824
3827 3825 Examples:
3828 3826
3829 3827 - use export and import to transplant a bugfix to the current
3830 3828 branch::
3831 3829
3832 3830 hg export -r 9353 | hg import -
3833 3831
3834 3832 - export all the changesets between two revisions to a file with
3835 3833 rename information::
3836 3834
3837 3835 hg export --git -r 123:150 > changes.txt
3838 3836
3839 3837 - split outgoing changes into a series of patches with
3840 3838 descriptive names::
3841 3839
3842 3840 hg export -r "outgoing()" -o "%n-%m.patch"
3843 3841
3844 3842 Returns 0 on success.
3845 3843 """
3846 3844 changesets += tuple(opts.get('rev', []))
3847 3845 if not changesets:
3848 3846 changesets = ['.']
3849 3847 revs = scmutil.revrange(repo, changesets)
3850 3848 if not revs:
3851 3849 raise error.Abort(_("export requires at least one changeset"))
3852 3850 if len(revs) > 1:
3853 3851 ui.note(_('exporting patches:\n'))
3854 3852 else:
3855 3853 ui.note(_('exporting patch:\n'))
3856 3854 cmdutil.export(repo, revs, template=opts.get('output'),
3857 3855 switch_parent=opts.get('switch_parent'),
3858 3856 opts=patch.diffallopts(ui, opts))
3859 3857
3860 3858 @command('files',
3861 3859 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3862 3860 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3863 3861 ] + walkopts + formatteropts + subrepoopts,
3864 3862 _('[OPTION]... [PATTERN]...'))
3865 3863 def files(ui, repo, *pats, **opts):
3866 3864 """list tracked files
3867 3865
3868 3866 Print files under Mercurial control in the working directory or
3869 3867 specified revision whose names match the given patterns (excluding
3870 3868 removed files).
3871 3869
3872 3870 If no patterns are given to match, this command prints the names
3873 3871 of all files under Mercurial control in the working directory.
3874 3872
3875 3873 .. container:: verbose
3876 3874
3877 3875 Examples:
3878 3876
3879 3877 - list all files under the current directory::
3880 3878
3881 3879 hg files .
3882 3880
3883 3881 - shows sizes and flags for current revision::
3884 3882
3885 3883 hg files -vr .
3886 3884
3887 3885 - list all files named README::
3888 3886
3889 3887 hg files -I "**/README"
3890 3888
3891 3889 - list all binary files::
3892 3890
3893 3891 hg files "set:binary()"
3894 3892
3895 3893 - find files containing a regular expression::
3896 3894
3897 3895 hg files "set:grep('bob')"
3898 3896
3899 3897 - search tracked file contents with xargs and grep::
3900 3898
3901 3899 hg files -0 | xargs -0 grep foo
3902 3900
3903 3901 See :hg:`help patterns` and :hg:`help filesets` for more information
3904 3902 on specifying file patterns.
3905 3903
3906 3904 Returns 0 if a match is found, 1 otherwise.
3907 3905
3908 3906 """
3909 3907 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3910 3908
3911 3909 end = '\n'
3912 3910 if opts.get('print0'):
3913 3911 end = '\0'
3914 3912 fm = ui.formatter('files', opts)
3915 3913 fmt = '%s' + end
3916 3914
3917 3915 m = scmutil.match(ctx, pats, opts)
3918 3916 ret = cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
3919 3917
3920 3918 fm.end()
3921 3919
3922 3920 return ret
3923 3921
3924 3922 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3925 3923 def forget(ui, repo, *pats, **opts):
3926 3924 """forget the specified files on the next commit
3927 3925
3928 3926 Mark the specified files so they will no longer be tracked
3929 3927 after the next commit.
3930 3928
3931 3929 This only removes files from the current branch, not from the
3932 3930 entire project history, and it does not delete them from the
3933 3931 working directory.
3934 3932
3935 3933 To delete the file from the working directory, see :hg:`remove`.
3936 3934
3937 3935 To undo a forget before the next commit, see :hg:`add`.
3938 3936
3939 3937 .. container:: verbose
3940 3938
3941 3939 Examples:
3942 3940
3943 3941 - forget newly-added binary files::
3944 3942
3945 3943 hg forget "set:added() and binary()"
3946 3944
3947 3945 - forget files that would be excluded by .hgignore::
3948 3946
3949 3947 hg forget "set:hgignore()"
3950 3948
3951 3949 Returns 0 on success.
3952 3950 """
3953 3951
3954 3952 if not pats:
3955 3953 raise error.Abort(_('no files specified'))
3956 3954
3957 3955 m = scmutil.match(repo[None], pats, opts)
3958 3956 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3959 3957 return rejected and 1 or 0
3960 3958
3961 3959 @command(
3962 3960 'graft',
3963 3961 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3964 3962 ('c', 'continue', False, _('resume interrupted graft')),
3965 3963 ('e', 'edit', False, _('invoke editor on commit messages')),
3966 3964 ('', 'log', None, _('append graft info to log message')),
3967 3965 ('f', 'force', False, _('force graft')),
3968 3966 ('D', 'currentdate', False,
3969 3967 _('record the current date as commit date')),
3970 3968 ('U', 'currentuser', False,
3971 3969 _('record the current user as committer'), _('DATE'))]
3972 3970 + commitopts2 + mergetoolopts + dryrunopts,
3973 3971 _('[OPTION]... [-r REV]... REV...'))
3974 3972 def graft(ui, repo, *revs, **opts):
3975 3973 '''copy changes from other branches onto the current branch
3976 3974
3977 3975 This command uses Mercurial's merge logic to copy individual
3978 3976 changes from other branches without merging branches in the
3979 3977 history graph. This is sometimes known as 'backporting' or
3980 3978 'cherry-picking'. By default, graft will copy user, date, and
3981 3979 description from the source changesets.
3982 3980
3983 3981 Changesets that are ancestors of the current revision, that have
3984 3982 already been grafted, or that are merges will be skipped.
3985 3983
3986 3984 If --log is specified, log messages will have a comment appended
3987 3985 of the form::
3988 3986
3989 3987 (grafted from CHANGESETHASH)
3990 3988
3991 3989 If --force is specified, revisions will be grafted even if they
3992 3990 are already ancestors of or have been grafted to the destination.
3993 3991 This is useful when the revisions have since been backed out.
3994 3992
3995 3993 If a graft merge results in conflicts, the graft process is
3996 3994 interrupted so that the current merge can be manually resolved.
3997 3995 Once all conflicts are addressed, the graft process can be
3998 3996 continued with the -c/--continue option.
3999 3997
4000 3998 .. note::
4001 3999
4002 4000 The -c/--continue option does not reapply earlier options, except
4003 4001 for --force.
4004 4002
4005 4003 .. container:: verbose
4006 4004
4007 4005 Examples:
4008 4006
4009 4007 - copy a single change to the stable branch and edit its description::
4010 4008
4011 4009 hg update stable
4012 4010 hg graft --edit 9393
4013 4011
4014 4012 - graft a range of changesets with one exception, updating dates::
4015 4013
4016 4014 hg graft -D "2085::2093 and not 2091"
4017 4015
4018 4016 - continue a graft after resolving conflicts::
4019 4017
4020 4018 hg graft -c
4021 4019
4022 4020 - show the source of a grafted changeset::
4023 4021
4024 4022 hg log --debug -r .
4025 4023
4026 4024 - show revisions sorted by date::
4027 4025
4028 4026 hg log -r 'sort(all(), date)'
4029 4027
4030 4028 See :hg:`help revisions` and :hg:`help revsets` for more about
4031 4029 specifying revisions.
4032 4030
4033 4031 Returns 0 on successful completion.
4034 4032 '''
4035 4033 with repo.wlock():
4036 4034 return _dograft(ui, repo, *revs, **opts)
4037 4035
4038 4036 def _dograft(ui, repo, *revs, **opts):
4039 4037 if revs and opts['rev']:
4040 4038 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
4041 4039 'revision ordering!\n'))
4042 4040
4043 4041 revs = list(revs)
4044 4042 revs.extend(opts['rev'])
4045 4043
4046 4044 if not opts.get('user') and opts.get('currentuser'):
4047 4045 opts['user'] = ui.username()
4048 4046 if not opts.get('date') and opts.get('currentdate'):
4049 4047 opts['date'] = "%d %d" % util.makedate()
4050 4048
4051 4049 editor = cmdutil.getcommiteditor(editform='graft', **opts)
4052 4050
4053 4051 cont = False
4054 4052 if opts['continue']:
4055 4053 cont = True
4056 4054 if revs:
4057 4055 raise error.Abort(_("can't specify --continue and revisions"))
4058 4056 # read in unfinished revisions
4059 4057 try:
4060 4058 nodes = repo.vfs.read('graftstate').splitlines()
4061 4059 revs = [repo[node].rev() for node in nodes]
4062 4060 except IOError as inst:
4063 4061 if inst.errno != errno.ENOENT:
4064 4062 raise
4065 4063 cmdutil.wrongtooltocontinue(repo, _('graft'))
4066 4064 else:
4067 4065 cmdutil.checkunfinished(repo)
4068 4066 cmdutil.bailifchanged(repo)
4069 4067 if not revs:
4070 4068 raise error.Abort(_('no revisions specified'))
4071 4069 revs = scmutil.revrange(repo, revs)
4072 4070
4073 4071 skipped = set()
4074 4072 # check for merges
4075 4073 for rev in repo.revs('%ld and merge()', revs):
4076 4074 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
4077 4075 skipped.add(rev)
4078 4076 revs = [r for r in revs if r not in skipped]
4079 4077 if not revs:
4080 4078 return -1
4081 4079
4082 4080 # Don't check in the --continue case, in effect retaining --force across
4083 4081 # --continues. That's because without --force, any revisions we decided to
4084 4082 # skip would have been filtered out here, so they wouldn't have made their
4085 4083 # way to the graftstate. With --force, any revisions we would have otherwise
4086 4084 # skipped would not have been filtered out, and if they hadn't been applied
4087 4085 # already, they'd have been in the graftstate.
4088 4086 if not (cont or opts.get('force')):
4089 4087 # check for ancestors of dest branch
4090 4088 crev = repo['.'].rev()
4091 4089 ancestors = repo.changelog.ancestors([crev], inclusive=True)
4092 4090 # Cannot use x.remove(y) on smart set, this has to be a list.
4093 4091 # XXX make this lazy in the future
4094 4092 revs = list(revs)
4095 4093 # don't mutate while iterating, create a copy
4096 4094 for rev in list(revs):
4097 4095 if rev in ancestors:
4098 4096 ui.warn(_('skipping ancestor revision %d:%s\n') %
4099 4097 (rev, repo[rev]))
4100 4098 # XXX remove on list is slow
4101 4099 revs.remove(rev)
4102 4100 if not revs:
4103 4101 return -1
4104 4102
4105 4103 # analyze revs for earlier grafts
4106 4104 ids = {}
4107 4105 for ctx in repo.set("%ld", revs):
4108 4106 ids[ctx.hex()] = ctx.rev()
4109 4107 n = ctx.extra().get('source')
4110 4108 if n:
4111 4109 ids[n] = ctx.rev()
4112 4110
4113 4111 # check ancestors for earlier grafts
4114 4112 ui.debug('scanning for duplicate grafts\n')
4115 4113
4116 4114 for rev in repo.changelog.findmissingrevs(revs, [crev]):
4117 4115 ctx = repo[rev]
4118 4116 n = ctx.extra().get('source')
4119 4117 if n in ids:
4120 4118 try:
4121 4119 r = repo[n].rev()
4122 4120 except error.RepoLookupError:
4123 4121 r = None
4124 4122 if r in revs:
4125 4123 ui.warn(_('skipping revision %d:%s '
4126 4124 '(already grafted to %d:%s)\n')
4127 4125 % (r, repo[r], rev, ctx))
4128 4126 revs.remove(r)
4129 4127 elif ids[n] in revs:
4130 4128 if r is None:
4131 4129 ui.warn(_('skipping already grafted revision %d:%s '
4132 4130 '(%d:%s also has unknown origin %s)\n')
4133 4131 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
4134 4132 else:
4135 4133 ui.warn(_('skipping already grafted revision %d:%s '
4136 4134 '(%d:%s also has origin %d:%s)\n')
4137 4135 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
4138 4136 revs.remove(ids[n])
4139 4137 elif ctx.hex() in ids:
4140 4138 r = ids[ctx.hex()]
4141 4139 ui.warn(_('skipping already grafted revision %d:%s '
4142 4140 '(was grafted from %d:%s)\n') %
4143 4141 (r, repo[r], rev, ctx))
4144 4142 revs.remove(r)
4145 4143 if not revs:
4146 4144 return -1
4147 4145
4148 4146 for pos, ctx in enumerate(repo.set("%ld", revs)):
4149 4147 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
4150 4148 ctx.description().split('\n', 1)[0])
4151 4149 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
4152 4150 if names:
4153 4151 desc += ' (%s)' % ' '.join(names)
4154 4152 ui.status(_('grafting %s\n') % desc)
4155 4153 if opts.get('dry_run'):
4156 4154 continue
4157 4155
4158 4156 source = ctx.extra().get('source')
4159 4157 extra = {}
4160 4158 if source:
4161 4159 extra['source'] = source
4162 4160 extra['intermediate-source'] = ctx.hex()
4163 4161 else:
4164 4162 extra['source'] = ctx.hex()
4165 4163 user = ctx.user()
4166 4164 if opts.get('user'):
4167 4165 user = opts['user']
4168 4166 date = ctx.date()
4169 4167 if opts.get('date'):
4170 4168 date = opts['date']
4171 4169 message = ctx.description()
4172 4170 if opts.get('log'):
4173 4171 message += '\n(grafted from %s)' % ctx.hex()
4174 4172
4175 4173 # we don't merge the first commit when continuing
4176 4174 if not cont:
4177 4175 # perform the graft merge with p1(rev) as 'ancestor'
4178 4176 try:
4179 4177 # ui.forcemerge is an internal variable, do not document
4180 4178 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4181 4179 'graft')
4182 4180 stats = mergemod.graft(repo, ctx, ctx.p1(),
4183 4181 ['local', 'graft'])
4184 4182 finally:
4185 4183 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
4186 4184 # report any conflicts
4187 4185 if stats and stats[3] > 0:
4188 4186 # write out state for --continue
4189 4187 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
4190 4188 repo.vfs.write('graftstate', ''.join(nodelines))
4191 4189 extra = ''
4192 4190 if opts.get('user'):
4193 4191 extra += ' --user %s' % opts['user']
4194 4192 if opts.get('date'):
4195 4193 extra += ' --date %s' % opts['date']
4196 4194 if opts.get('log'):
4197 4195 extra += ' --log'
4198 4196 hint=_('use hg resolve and hg graft --continue%s') % extra
4199 4197 raise error.Abort(
4200 4198 _("unresolved conflicts, can't continue"),
4201 4199 hint=hint)
4202 4200 else:
4203 4201 cont = False
4204 4202
4205 4203 # commit
4206 4204 node = repo.commit(text=message, user=user,
4207 4205 date=date, extra=extra, editor=editor)
4208 4206 if node is None:
4209 4207 ui.warn(
4210 4208 _('note: graft of %d:%s created no changes to commit\n') %
4211 4209 (ctx.rev(), ctx))
4212 4210
4213 4211 # remove state when we complete successfully
4214 4212 if not opts.get('dry_run'):
4215 4213 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
4216 4214
4217 4215 return 0
4218 4216
4219 4217 @command('grep',
4220 4218 [('0', 'print0', None, _('end fields with NUL')),
4221 4219 ('', 'all', None, _('print all revisions that match')),
4222 4220 ('a', 'text', None, _('treat all files as text')),
4223 4221 ('f', 'follow', None,
4224 4222 _('follow changeset history,'
4225 4223 ' or file history across copies and renames')),
4226 4224 ('i', 'ignore-case', None, _('ignore case when matching')),
4227 4225 ('l', 'files-with-matches', None,
4228 4226 _('print only filenames and revisions that match')),
4229 4227 ('n', 'line-number', None, _('print matching line numbers')),
4230 4228 ('r', 'rev', [],
4231 4229 _('only search files changed within revision range'), _('REV')),
4232 4230 ('u', 'user', None, _('list the author (long with -v)')),
4233 4231 ('d', 'date', None, _('list the date (short with -q)')),
4234 4232 ] + walkopts,
4235 4233 _('[OPTION]... PATTERN [FILE]...'),
4236 4234 inferrepo=True)
4237 4235 def grep(ui, repo, pattern, *pats, **opts):
4238 4236 """search for a pattern in specified files and revisions
4239 4237
4240 4238 Search revisions of files for a regular expression.
4241 4239
4242 4240 This command behaves differently than Unix grep. It only accepts
4243 4241 Python/Perl regexps. It searches repository history, not the
4244 4242 working directory. It always prints the revision number in which a
4245 4243 match appears.
4246 4244
4247 4245 By default, grep only prints output for the first revision of a
4248 4246 file in which it finds a match. To get it to print every revision
4249 4247 that contains a change in match status ("-" for a match that
4250 4248 becomes a non-match, or "+" for a non-match that becomes a match),
4251 4249 use the --all flag.
4252 4250
4253 4251 Returns 0 if a match is found, 1 otherwise.
4254 4252 """
4255 4253 reflags = re.M
4256 4254 if opts.get('ignore_case'):
4257 4255 reflags |= re.I
4258 4256 try:
4259 4257 regexp = util.re.compile(pattern, reflags)
4260 4258 except re.error as inst:
4261 4259 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
4262 4260 return 1
4263 4261 sep, eol = ':', '\n'
4264 4262 if opts.get('print0'):
4265 4263 sep = eol = '\0'
4266 4264
4267 4265 getfile = util.lrucachefunc(repo.file)
4268 4266
4269 4267 def matchlines(body):
4270 4268 begin = 0
4271 4269 linenum = 0
4272 4270 while begin < len(body):
4273 4271 match = regexp.search(body, begin)
4274 4272 if not match:
4275 4273 break
4276 4274 mstart, mend = match.span()
4277 4275 linenum += body.count('\n', begin, mstart) + 1
4278 4276 lstart = body.rfind('\n', begin, mstart) + 1 or begin
4279 4277 begin = body.find('\n', mend) + 1 or len(body) + 1
4280 4278 lend = begin - 1
4281 4279 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
4282 4280
4283 4281 class linestate(object):
4284 4282 def __init__(self, line, linenum, colstart, colend):
4285 4283 self.line = line
4286 4284 self.linenum = linenum
4287 4285 self.colstart = colstart
4288 4286 self.colend = colend
4289 4287
4290 4288 def __hash__(self):
4291 4289 return hash((self.linenum, self.line))
4292 4290
4293 4291 def __eq__(self, other):
4294 4292 return self.line == other.line
4295 4293
4296 4294 def __iter__(self):
4297 4295 yield (self.line[:self.colstart], '')
4298 4296 yield (self.line[self.colstart:self.colend], 'grep.match')
4299 4297 rest = self.line[self.colend:]
4300 4298 while rest != '':
4301 4299 match = regexp.search(rest)
4302 4300 if not match:
4303 4301 yield (rest, '')
4304 4302 break
4305 4303 mstart, mend = match.span()
4306 4304 yield (rest[:mstart], '')
4307 4305 yield (rest[mstart:mend], 'grep.match')
4308 4306 rest = rest[mend:]
4309 4307
4310 4308 matches = {}
4311 4309 copies = {}
4312 4310 def grepbody(fn, rev, body):
4313 4311 matches[rev].setdefault(fn, [])
4314 4312 m = matches[rev][fn]
4315 4313 for lnum, cstart, cend, line in matchlines(body):
4316 4314 s = linestate(line, lnum, cstart, cend)
4317 4315 m.append(s)
4318 4316
4319 4317 def difflinestates(a, b):
4320 4318 sm = difflib.SequenceMatcher(None, a, b)
4321 4319 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
4322 4320 if tag == 'insert':
4323 4321 for i in xrange(blo, bhi):
4324 4322 yield ('+', b[i])
4325 4323 elif tag == 'delete':
4326 4324 for i in xrange(alo, ahi):
4327 4325 yield ('-', a[i])
4328 4326 elif tag == 'replace':
4329 4327 for i in xrange(alo, ahi):
4330 4328 yield ('-', a[i])
4331 4329 for i in xrange(blo, bhi):
4332 4330 yield ('+', b[i])
4333 4331
4334 4332 def display(fn, ctx, pstates, states):
4335 4333 rev = ctx.rev()
4336 4334 if ui.quiet:
4337 4335 datefunc = util.shortdate
4338 4336 else:
4339 4337 datefunc = util.datestr
4340 4338 found = False
4341 4339 @util.cachefunc
4342 4340 def binary():
4343 4341 flog = getfile(fn)
4344 4342 return util.binary(flog.read(ctx.filenode(fn)))
4345 4343
4346 4344 if opts.get('all'):
4347 4345 iter = difflinestates(pstates, states)
4348 4346 else:
4349 4347 iter = [('', l) for l in states]
4350 4348 for change, l in iter:
4351 4349 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
4352 4350
4353 4351 if opts.get('line_number'):
4354 4352 cols.append((str(l.linenum), 'grep.linenumber'))
4355 4353 if opts.get('all'):
4356 4354 cols.append((change, 'grep.change'))
4357 4355 if opts.get('user'):
4358 4356 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
4359 4357 if opts.get('date'):
4360 4358 cols.append((datefunc(ctx.date()), 'grep.date'))
4361 4359 for col, label in cols[:-1]:
4362 4360 ui.write(col, label=label)
4363 4361 ui.write(sep, label='grep.sep')
4364 4362 ui.write(cols[-1][0], label=cols[-1][1])
4365 4363 if not opts.get('files_with_matches'):
4366 4364 ui.write(sep, label='grep.sep')
4367 4365 if not opts.get('text') and binary():
4368 4366 ui.write(" Binary file matches")
4369 4367 else:
4370 4368 for s, label in l:
4371 4369 ui.write(s, label=label)
4372 4370 ui.write(eol)
4373 4371 found = True
4374 4372 if opts.get('files_with_matches'):
4375 4373 break
4376 4374 return found
4377 4375
4378 4376 skip = {}
4379 4377 revfiles = {}
4380 4378 matchfn = scmutil.match(repo[None], pats, opts)
4381 4379 found = False
4382 4380 follow = opts.get('follow')
4383 4381
4384 4382 def prep(ctx, fns):
4385 4383 rev = ctx.rev()
4386 4384 pctx = ctx.p1()
4387 4385 parent = pctx.rev()
4388 4386 matches.setdefault(rev, {})
4389 4387 matches.setdefault(parent, {})
4390 4388 files = revfiles.setdefault(rev, [])
4391 4389 for fn in fns:
4392 4390 flog = getfile(fn)
4393 4391 try:
4394 4392 fnode = ctx.filenode(fn)
4395 4393 except error.LookupError:
4396 4394 continue
4397 4395
4398 4396 copied = flog.renamed(fnode)
4399 4397 copy = follow and copied and copied[0]
4400 4398 if copy:
4401 4399 copies.setdefault(rev, {})[fn] = copy
4402 4400 if fn in skip:
4403 4401 if copy:
4404 4402 skip[copy] = True
4405 4403 continue
4406 4404 files.append(fn)
4407 4405
4408 4406 if fn not in matches[rev]:
4409 4407 grepbody(fn, rev, flog.read(fnode))
4410 4408
4411 4409 pfn = copy or fn
4412 4410 if pfn not in matches[parent]:
4413 4411 try:
4414 4412 fnode = pctx.filenode(pfn)
4415 4413 grepbody(pfn, parent, flog.read(fnode))
4416 4414 except error.LookupError:
4417 4415 pass
4418 4416
4419 4417 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
4420 4418 rev = ctx.rev()
4421 4419 parent = ctx.p1().rev()
4422 4420 for fn in sorted(revfiles.get(rev, [])):
4423 4421 states = matches[rev][fn]
4424 4422 copy = copies.get(rev, {}).get(fn)
4425 4423 if fn in skip:
4426 4424 if copy:
4427 4425 skip[copy] = True
4428 4426 continue
4429 4427 pstates = matches.get(parent, {}).get(copy or fn, [])
4430 4428 if pstates or states:
4431 4429 r = display(fn, ctx, pstates, states)
4432 4430 found = found or r
4433 4431 if r and not opts.get('all'):
4434 4432 skip[fn] = True
4435 4433 if copy:
4436 4434 skip[copy] = True
4437 4435 del matches[rev]
4438 4436 del revfiles[rev]
4439 4437
4440 4438 return not found
4441 4439
4442 4440 @command('heads',
4443 4441 [('r', 'rev', '',
4444 4442 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
4445 4443 ('t', 'topo', False, _('show topological heads only')),
4446 4444 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
4447 4445 ('c', 'closed', False, _('show normal and closed branch heads')),
4448 4446 ] + templateopts,
4449 4447 _('[-ct] [-r STARTREV] [REV]...'))
4450 4448 def heads(ui, repo, *branchrevs, **opts):
4451 4449 """show branch heads
4452 4450
4453 4451 With no arguments, show all open branch heads in the repository.
4454 4452 Branch heads are changesets that have no descendants on the
4455 4453 same branch. They are where development generally takes place and
4456 4454 are the usual targets for update and merge operations.
4457 4455
4458 4456 If one or more REVs are given, only open branch heads on the
4459 4457 branches associated with the specified changesets are shown. This
4460 4458 means that you can use :hg:`heads .` to see the heads on the
4461 4459 currently checked-out branch.
4462 4460
4463 4461 If -c/--closed is specified, also show branch heads marked closed
4464 4462 (see :hg:`commit --close-branch`).
4465 4463
4466 4464 If STARTREV is specified, only those heads that are descendants of
4467 4465 STARTREV will be displayed.
4468 4466
4469 4467 If -t/--topo is specified, named branch mechanics will be ignored and only
4470 4468 topological heads (changesets with no children) will be shown.
4471 4469
4472 4470 Returns 0 if matching heads are found, 1 if not.
4473 4471 """
4474 4472
4475 4473 start = None
4476 4474 if 'rev' in opts:
4477 4475 start = scmutil.revsingle(repo, opts['rev'], None).node()
4478 4476
4479 4477 if opts.get('topo'):
4480 4478 heads = [repo[h] for h in repo.heads(start)]
4481 4479 else:
4482 4480 heads = []
4483 4481 for branch in repo.branchmap():
4484 4482 heads += repo.branchheads(branch, start, opts.get('closed'))
4485 4483 heads = [repo[h] for h in heads]
4486 4484
4487 4485 if branchrevs:
4488 4486 branches = set(repo[br].branch() for br in branchrevs)
4489 4487 heads = [h for h in heads if h.branch() in branches]
4490 4488
4491 4489 if opts.get('active') and branchrevs:
4492 4490 dagheads = repo.heads(start)
4493 4491 heads = [h for h in heads if h.node() in dagheads]
4494 4492
4495 4493 if branchrevs:
4496 4494 haveheads = set(h.branch() for h in heads)
4497 4495 if branches - haveheads:
4498 4496 headless = ', '.join(b for b in branches - haveheads)
4499 4497 msg = _('no open branch heads found on branches %s')
4500 4498 if opts.get('rev'):
4501 4499 msg += _(' (started at %s)') % opts['rev']
4502 4500 ui.warn((msg + '\n') % headless)
4503 4501
4504 4502 if not heads:
4505 4503 return 1
4506 4504
4507 4505 heads = sorted(heads, key=lambda x: -x.rev())
4508 4506 displayer = cmdutil.show_changeset(ui, repo, opts)
4509 4507 for ctx in heads:
4510 4508 displayer.show(ctx)
4511 4509 displayer.close()
4512 4510
4513 4511 @command('help',
4514 4512 [('e', 'extension', None, _('show only help for extensions')),
4515 4513 ('c', 'command', None, _('show only help for commands')),
4516 4514 ('k', 'keyword', None, _('show topics matching keyword')),
4517 4515 ('s', 'system', [], _('show help for specific platform(s)')),
4518 4516 ],
4519 4517 _('[-ecks] [TOPIC]'),
4520 4518 norepo=True)
4521 4519 def help_(ui, name=None, **opts):
4522 4520 """show help for a given topic or a help overview
4523 4521
4524 4522 With no arguments, print a list of commands with short help messages.
4525 4523
4526 4524 Given a topic, extension, or command name, print help for that
4527 4525 topic.
4528 4526
4529 4527 Returns 0 if successful.
4530 4528 """
4531 4529
4532 4530 textwidth = min(ui.termwidth(), 80) - 2
4533 4531
4534 4532 keep = opts.get('system') or []
4535 4533 if len(keep) == 0:
4536 4534 if sys.platform.startswith('win'):
4537 4535 keep.append('windows')
4538 4536 elif sys.platform == 'OpenVMS':
4539 4537 keep.append('vms')
4540 4538 elif sys.platform == 'plan9':
4541 4539 keep.append('plan9')
4542 4540 else:
4543 4541 keep.append('unix')
4544 4542 keep.append(sys.platform.lower())
4545 4543 if ui.verbose:
4546 4544 keep.append('verbose')
4547 4545
4548 4546 section = None
4549 4547 subtopic = None
4550 4548 if name and '.' in name:
4551 4549 name, section = name.split('.', 1)
4552 4550 section = section.lower()
4553 4551 if '.' in section:
4554 4552 subtopic, section = section.split('.', 1)
4555 4553 else:
4556 4554 subtopic = section
4557 4555
4558 4556 text = help.help_(ui, name, subtopic=subtopic, **opts)
4559 4557
4560 4558 formatted, pruned = minirst.format(text, textwidth, keep=keep,
4561 4559 section=section)
4562 4560
4563 4561 # We could have been given a weird ".foo" section without a name
4564 4562 # to look for, or we could have simply failed to found "foo.bar"
4565 4563 # because bar isn't a section of foo
4566 4564 if section and not (formatted and name):
4567 4565 raise error.Abort(_("help section not found"))
4568 4566
4569 4567 if 'verbose' in pruned:
4570 4568 keep.append('omitted')
4571 4569 else:
4572 4570 keep.append('notomitted')
4573 4571 formatted, pruned = minirst.format(text, textwidth, keep=keep,
4574 4572 section=section)
4575 4573 ui.write(formatted)
4576 4574
4577 4575
4578 4576 @command('identify|id',
4579 4577 [('r', 'rev', '',
4580 4578 _('identify the specified revision'), _('REV')),
4581 4579 ('n', 'num', None, _('show local revision number')),
4582 4580 ('i', 'id', None, _('show global revision id')),
4583 4581 ('b', 'branch', None, _('show branch')),
4584 4582 ('t', 'tags', None, _('show tags')),
4585 4583 ('B', 'bookmarks', None, _('show bookmarks')),
4586 4584 ] + remoteopts,
4587 4585 _('[-nibtB] [-r REV] [SOURCE]'),
4588 4586 optionalrepo=True)
4589 4587 def identify(ui, repo, source=None, rev=None,
4590 4588 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
4591 4589 """identify the working directory or specified revision
4592 4590
4593 4591 Print a summary identifying the repository state at REV using one or
4594 4592 two parent hash identifiers, followed by a "+" if the working
4595 4593 directory has uncommitted changes, the branch name (if not default),
4596 4594 a list of tags, and a list of bookmarks.
4597 4595
4598 4596 When REV is not given, print a summary of the current state of the
4599 4597 repository.
4600 4598
4601 4599 Specifying a path to a repository root or Mercurial bundle will
4602 4600 cause lookup to operate on that repository/bundle.
4603 4601
4604 4602 .. container:: verbose
4605 4603
4606 4604 Examples:
4607 4605
4608 4606 - generate a build identifier for the working directory::
4609 4607
4610 4608 hg id --id > build-id.dat
4611 4609
4612 4610 - find the revision corresponding to a tag::
4613 4611
4614 4612 hg id -n -r 1.3
4615 4613
4616 4614 - check the most recent revision of a remote repository::
4617 4615
4618 4616 hg id -r tip http://selenic.com/hg/
4619 4617
4620 4618 See :hg:`log` for generating more information about specific revisions,
4621 4619 including full hash identifiers.
4622 4620
4623 4621 Returns 0 if successful.
4624 4622 """
4625 4623
4626 4624 if not repo and not source:
4627 4625 raise error.Abort(_("there is no Mercurial repository here "
4628 4626 "(.hg not found)"))
4629 4627
4630 4628 if ui.debugflag:
4631 4629 hexfunc = hex
4632 4630 else:
4633 4631 hexfunc = short
4634 4632 default = not (num or id or branch or tags or bookmarks)
4635 4633 output = []
4636 4634 revs = []
4637 4635
4638 4636 if source:
4639 4637 source, branches = hg.parseurl(ui.expandpath(source))
4640 4638 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
4641 4639 repo = peer.local()
4642 4640 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
4643 4641
4644 4642 if not repo:
4645 4643 if num or branch or tags:
4646 4644 raise error.Abort(
4647 4645 _("can't query remote revision number, branch, or tags"))
4648 4646 if not rev and revs:
4649 4647 rev = revs[0]
4650 4648 if not rev:
4651 4649 rev = "tip"
4652 4650
4653 4651 remoterev = peer.lookup(rev)
4654 4652 if default or id:
4655 4653 output = [hexfunc(remoterev)]
4656 4654
4657 4655 def getbms():
4658 4656 bms = []
4659 4657
4660 4658 if 'bookmarks' in peer.listkeys('namespaces'):
4661 4659 hexremoterev = hex(remoterev)
4662 4660 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
4663 4661 if bmr == hexremoterev]
4664 4662
4665 4663 return sorted(bms)
4666 4664
4667 4665 if bookmarks:
4668 4666 output.extend(getbms())
4669 4667 elif default and not ui.quiet:
4670 4668 # multiple bookmarks for a single parent separated by '/'
4671 4669 bm = '/'.join(getbms())
4672 4670 if bm:
4673 4671 output.append(bm)
4674 4672 else:
4675 4673 ctx = scmutil.revsingle(repo, rev, None)
4676 4674
4677 4675 if ctx.rev() is None:
4678 4676 ctx = repo[None]
4679 4677 parents = ctx.parents()
4680 4678 taglist = []
4681 4679 for p in parents:
4682 4680 taglist.extend(p.tags())
4683 4681
4684 4682 changed = ""
4685 4683 if default or id or num:
4686 4684 if (any(repo.status())
4687 4685 or any(ctx.sub(s).dirty() for s in ctx.substate)):
4688 4686 changed = '+'
4689 4687 if default or id:
4690 4688 output = ["%s%s" %
4691 4689 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4692 4690 if num:
4693 4691 output.append("%s%s" %
4694 4692 ('+'.join([str(p.rev()) for p in parents]), changed))
4695 4693 else:
4696 4694 if default or id:
4697 4695 output = [hexfunc(ctx.node())]
4698 4696 if num:
4699 4697 output.append(str(ctx.rev()))
4700 4698 taglist = ctx.tags()
4701 4699
4702 4700 if default and not ui.quiet:
4703 4701 b = ctx.branch()
4704 4702 if b != 'default':
4705 4703 output.append("(%s)" % b)
4706 4704
4707 4705 # multiple tags for a single parent separated by '/'
4708 4706 t = '/'.join(taglist)
4709 4707 if t:
4710 4708 output.append(t)
4711 4709
4712 4710 # multiple bookmarks for a single parent separated by '/'
4713 4711 bm = '/'.join(ctx.bookmarks())
4714 4712 if bm:
4715 4713 output.append(bm)
4716 4714 else:
4717 4715 if branch:
4718 4716 output.append(ctx.branch())
4719 4717
4720 4718 if tags:
4721 4719 output.extend(taglist)
4722 4720
4723 4721 if bookmarks:
4724 4722 output.extend(ctx.bookmarks())
4725 4723
4726 4724 ui.write("%s\n" % ' '.join(output))
4727 4725
4728 4726 @command('import|patch',
4729 4727 [('p', 'strip', 1,
4730 4728 _('directory strip option for patch. This has the same '
4731 4729 'meaning as the corresponding patch option'), _('NUM')),
4732 4730 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4733 4731 ('e', 'edit', False, _('invoke editor on commit messages')),
4734 4732 ('f', 'force', None,
4735 4733 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4736 4734 ('', 'no-commit', None,
4737 4735 _("don't commit, just update the working directory")),
4738 4736 ('', 'bypass', None,
4739 4737 _("apply patch without touching the working directory")),
4740 4738 ('', 'partial', None,
4741 4739 _('commit even if some hunks fail')),
4742 4740 ('', 'exact', None,
4743 4741 _('apply patch to the nodes from which it was generated')),
4744 4742 ('', 'prefix', '',
4745 4743 _('apply patch to subdirectory'), _('DIR')),
4746 4744 ('', 'import-branch', None,
4747 4745 _('use any branch information in patch (implied by --exact)'))] +
4748 4746 commitopts + commitopts2 + similarityopts,
4749 4747 _('[OPTION]... PATCH...'))
4750 4748 def import_(ui, repo, patch1=None, *patches, **opts):
4751 4749 """import an ordered set of patches
4752 4750
4753 4751 Import a list of patches and commit them individually (unless
4754 4752 --no-commit is specified).
4755 4753
4756 4754 To read a patch from standard input, use "-" as the patch name. If
4757 4755 a URL is specified, the patch will be downloaded from there.
4758 4756
4759 4757 Import first applies changes to the working directory (unless
4760 4758 --bypass is specified), import will abort if there are outstanding
4761 4759 changes.
4762 4760
4763 4761 Use --bypass to apply and commit patches directly to the
4764 4762 repository, without affecting the working directory. Without
4765 4763 --exact, patches will be applied on top of the working directory
4766 4764 parent revision.
4767 4765
4768 4766 You can import a patch straight from a mail message. Even patches
4769 4767 as attachments work (to use the body part, it must have type
4770 4768 text/plain or text/x-patch). From and Subject headers of email
4771 4769 message are used as default committer and commit message. All
4772 4770 text/plain body parts before first diff are added to the commit
4773 4771 message.
4774 4772
4775 4773 If the imported patch was generated by :hg:`export`, user and
4776 4774 description from patch override values from message headers and
4777 4775 body. Values given on command line with -m/--message and -u/--user
4778 4776 override these.
4779 4777
4780 4778 If --exact is specified, import will set the working directory to
4781 4779 the parent of each patch before applying it, and will abort if the
4782 4780 resulting changeset has a different ID than the one recorded in
4783 4781 the patch. This may happen due to character set problems or other
4784 4782 deficiencies in the text patch format.
4785 4783
4786 4784 Use --partial to ensure a changeset will be created from the patch
4787 4785 even if some hunks fail to apply. Hunks that fail to apply will be
4788 4786 written to a <target-file>.rej file. Conflicts can then be resolved
4789 4787 by hand before :hg:`commit --amend` is run to update the created
4790 4788 changeset. This flag exists to let people import patches that
4791 4789 partially apply without losing the associated metadata (author,
4792 4790 date, description, ...).
4793 4791
4794 4792 .. note::
4795 4793
4796 4794 When no hunks apply cleanly, :hg:`import --partial` will create
4797 4795 an empty changeset, importing only the patch metadata.
4798 4796
4799 4797 With -s/--similarity, hg will attempt to discover renames and
4800 4798 copies in the patch in the same way as :hg:`addremove`.
4801 4799
4802 4800 It is possible to use external patch programs to perform the patch
4803 4801 by setting the ``ui.patch`` configuration option. For the default
4804 4802 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4805 4803 See :hg:`help config` for more information about configuration
4806 4804 files and how to use these options.
4807 4805
4808 4806 See :hg:`help dates` for a list of formats valid for -d/--date.
4809 4807
4810 4808 .. container:: verbose
4811 4809
4812 4810 Examples:
4813 4811
4814 4812 - import a traditional patch from a website and detect renames::
4815 4813
4816 4814 hg import -s 80 http://example.com/bugfix.patch
4817 4815
4818 4816 - import a changeset from an hgweb server::
4819 4817
4820 4818 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
4821 4819
4822 4820 - import all the patches in an Unix-style mbox::
4823 4821
4824 4822 hg import incoming-patches.mbox
4825 4823
4826 4824 - attempt to exactly restore an exported changeset (not always
4827 4825 possible)::
4828 4826
4829 4827 hg import --exact proposed-fix.patch
4830 4828
4831 4829 - use an external tool to apply a patch which is too fuzzy for
4832 4830 the default internal tool.
4833 4831
4834 4832 hg import --config ui.patch="patch --merge" fuzzy.patch
4835 4833
4836 4834 - change the default fuzzing from 2 to a less strict 7
4837 4835
4838 4836 hg import --config ui.fuzz=7 fuzz.patch
4839 4837
4840 4838 Returns 0 on success, 1 on partial success (see --partial).
4841 4839 """
4842 4840
4843 4841 if not patch1:
4844 4842 raise error.Abort(_('need at least one patch to import'))
4845 4843
4846 4844 patches = (patch1,) + patches
4847 4845
4848 4846 date = opts.get('date')
4849 4847 if date:
4850 4848 opts['date'] = util.parsedate(date)
4851 4849
4852 4850 exact = opts.get('exact')
4853 4851 update = not opts.get('bypass')
4854 4852 if not update and opts.get('no_commit'):
4855 4853 raise error.Abort(_('cannot use --no-commit with --bypass'))
4856 4854 try:
4857 4855 sim = float(opts.get('similarity') or 0)
4858 4856 except ValueError:
4859 4857 raise error.Abort(_('similarity must be a number'))
4860 4858 if sim < 0 or sim > 100:
4861 4859 raise error.Abort(_('similarity must be between 0 and 100'))
4862 4860 if sim and not update:
4863 4861 raise error.Abort(_('cannot use --similarity with --bypass'))
4864 4862 if exact:
4865 4863 if opts.get('edit'):
4866 4864 raise error.Abort(_('cannot use --exact with --edit'))
4867 4865 if opts.get('prefix'):
4868 4866 raise error.Abort(_('cannot use --exact with --prefix'))
4869 4867
4870 4868 base = opts["base"]
4871 4869 wlock = dsguard = lock = tr = None
4872 4870 msgs = []
4873 4871 ret = 0
4874 4872
4875 4873
4876 4874 try:
4877 4875 wlock = repo.wlock()
4878 4876
4879 4877 if update:
4880 4878 cmdutil.checkunfinished(repo)
4881 4879 if (exact or not opts.get('force')):
4882 4880 cmdutil.bailifchanged(repo)
4883 4881
4884 4882 if not opts.get('no_commit'):
4885 4883 lock = repo.lock()
4886 4884 tr = repo.transaction('import')
4887 4885 else:
4888 4886 dsguard = cmdutil.dirstateguard(repo, 'import')
4889 4887 parents = repo[None].parents()
4890 4888 for patchurl in patches:
4891 4889 if patchurl == '-':
4892 4890 ui.status(_('applying patch from stdin\n'))
4893 4891 patchfile = ui.fin
4894 4892 patchurl = 'stdin' # for error message
4895 4893 else:
4896 4894 patchurl = os.path.join(base, patchurl)
4897 4895 ui.status(_('applying %s\n') % patchurl)
4898 4896 patchfile = hg.openpath(ui, patchurl)
4899 4897
4900 4898 haspatch = False
4901 4899 for hunk in patch.split(patchfile):
4902 4900 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4903 4901 parents, opts,
4904 4902 msgs, hg.clean)
4905 4903 if msg:
4906 4904 haspatch = True
4907 4905 ui.note(msg + '\n')
4908 4906 if update or exact:
4909 4907 parents = repo[None].parents()
4910 4908 else:
4911 4909 parents = [repo[node]]
4912 4910 if rej:
4913 4911 ui.write_err(_("patch applied partially\n"))
4914 4912 ui.write_err(_("(fix the .rej files and run "
4915 4913 "`hg commit --amend`)\n"))
4916 4914 ret = 1
4917 4915 break
4918 4916
4919 4917 if not haspatch:
4920 4918 raise error.Abort(_('%s: no diffs found') % patchurl)
4921 4919
4922 4920 if tr:
4923 4921 tr.close()
4924 4922 if msgs:
4925 4923 repo.savecommitmessage('\n* * *\n'.join(msgs))
4926 4924 if dsguard:
4927 4925 dsguard.close()
4928 4926 return ret
4929 4927 finally:
4930 4928 if tr:
4931 4929 tr.release()
4932 4930 release(lock, dsguard, wlock)
4933 4931
4934 4932 @command('incoming|in',
4935 4933 [('f', 'force', None,
4936 4934 _('run even if remote repository is unrelated')),
4937 4935 ('n', 'newest-first', None, _('show newest record first')),
4938 4936 ('', 'bundle', '',
4939 4937 _('file to store the bundles into'), _('FILE')),
4940 4938 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4941 4939 ('B', 'bookmarks', False, _("compare bookmarks")),
4942 4940 ('b', 'branch', [],
4943 4941 _('a specific branch you would like to pull'), _('BRANCH')),
4944 4942 ] + logopts + remoteopts + subrepoopts,
4945 4943 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4946 4944 def incoming(ui, repo, source="default", **opts):
4947 4945 """show new changesets found in source
4948 4946
4949 4947 Show new changesets found in the specified path/URL or the default
4950 4948 pull location. These are the changesets that would have been pulled
4951 4949 if a pull at the time you issued this command.
4952 4950
4953 4951 See pull for valid source format details.
4954 4952
4955 4953 .. container:: verbose
4956 4954
4957 4955 With -B/--bookmarks, the result of bookmark comparison between
4958 4956 local and remote repositories is displayed. With -v/--verbose,
4959 4957 status is also displayed for each bookmark like below::
4960 4958
4961 4959 BM1 01234567890a added
4962 4960 BM2 1234567890ab advanced
4963 4961 BM3 234567890abc diverged
4964 4962 BM4 34567890abcd changed
4965 4963
4966 4964 The action taken locally when pulling depends on the
4967 4965 status of each bookmark:
4968 4966
4969 4967 :``added``: pull will create it
4970 4968 :``advanced``: pull will update it
4971 4969 :``diverged``: pull will create a divergent bookmark
4972 4970 :``changed``: result depends on remote changesets
4973 4971
4974 4972 From the point of view of pulling behavior, bookmark
4975 4973 existing only in the remote repository are treated as ``added``,
4976 4974 even if it is in fact locally deleted.
4977 4975
4978 4976 .. container:: verbose
4979 4977
4980 4978 For remote repository, using --bundle avoids downloading the
4981 4979 changesets twice if the incoming is followed by a pull.
4982 4980
4983 4981 Examples:
4984 4982
4985 4983 - show incoming changes with patches and full description::
4986 4984
4987 4985 hg incoming -vp
4988 4986
4989 4987 - show incoming changes excluding merges, store a bundle::
4990 4988
4991 4989 hg in -vpM --bundle incoming.hg
4992 4990 hg pull incoming.hg
4993 4991
4994 4992 - briefly list changes inside a bundle::
4995 4993
4996 4994 hg in changes.hg -T "{desc|firstline}\\n"
4997 4995
4998 4996 Returns 0 if there are incoming changes, 1 otherwise.
4999 4997 """
5000 4998 if opts.get('graph'):
5001 4999 cmdutil.checkunsupportedgraphflags([], opts)
5002 5000 def display(other, chlist, displayer):
5003 5001 revdag = cmdutil.graphrevs(other, chlist, opts)
5004 5002 cmdutil.displaygraph(ui, repo, revdag, displayer,
5005 5003 graphmod.asciiedges)
5006 5004
5007 5005 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
5008 5006 return 0
5009 5007
5010 5008 if opts.get('bundle') and opts.get('subrepos'):
5011 5009 raise error.Abort(_('cannot combine --bundle and --subrepos'))
5012 5010
5013 5011 if opts.get('bookmarks'):
5014 5012 source, branches = hg.parseurl(ui.expandpath(source),
5015 5013 opts.get('branch'))
5016 5014 other = hg.peer(repo, opts, source)
5017 5015 if 'bookmarks' not in other.listkeys('namespaces'):
5018 5016 ui.warn(_("remote doesn't support bookmarks\n"))
5019 5017 return 0
5020 5018 ui.status(_('comparing with %s\n') % util.hidepassword(source))
5021 5019 return bookmarks.incoming(ui, repo, other)
5022 5020
5023 5021 repo._subtoppath = ui.expandpath(source)
5024 5022 try:
5025 5023 return hg.incoming(ui, repo, source, opts)
5026 5024 finally:
5027 5025 del repo._subtoppath
5028 5026
5029 5027
5030 5028 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
5031 5029 norepo=True)
5032 5030 def init(ui, dest=".", **opts):
5033 5031 """create a new repository in the given directory
5034 5032
5035 5033 Initialize a new repository in the given directory. If the given
5036 5034 directory does not exist, it will be created.
5037 5035
5038 5036 If no directory is given, the current directory is used.
5039 5037
5040 5038 It is possible to specify an ``ssh://`` URL as the destination.
5041 5039 See :hg:`help urls` for more information.
5042 5040
5043 5041 Returns 0 on success.
5044 5042 """
5045 5043 hg.peer(ui, opts, ui.expandpath(dest), create=True)
5046 5044
5047 5045 @command('locate',
5048 5046 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
5049 5047 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5050 5048 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
5051 5049 ] + walkopts,
5052 5050 _('[OPTION]... [PATTERN]...'))
5053 5051 def locate(ui, repo, *pats, **opts):
5054 5052 """locate files matching specific patterns (DEPRECATED)
5055 5053
5056 5054 Print files under Mercurial control in the working directory whose
5057 5055 names match the given patterns.
5058 5056
5059 5057 By default, this command searches all directories in the working
5060 5058 directory. To search just the current directory and its
5061 5059 subdirectories, use "--include .".
5062 5060
5063 5061 If no patterns are given to match, this command prints the names
5064 5062 of all files under Mercurial control in the working directory.
5065 5063
5066 5064 If you want to feed the output of this command into the "xargs"
5067 5065 command, use the -0 option to both this command and "xargs". This
5068 5066 will avoid the problem of "xargs" treating single filenames that
5069 5067 contain whitespace as multiple filenames.
5070 5068
5071 5069 See :hg:`help files` for a more versatile command.
5072 5070
5073 5071 Returns 0 if a match is found, 1 otherwise.
5074 5072 """
5075 5073 if opts.get('print0'):
5076 5074 end = '\0'
5077 5075 else:
5078 5076 end = '\n'
5079 5077 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
5080 5078
5081 5079 ret = 1
5082 5080 ctx = repo[rev]
5083 5081 m = scmutil.match(ctx, pats, opts, default='relglob',
5084 5082 badfn=lambda x, y: False)
5085 5083
5086 5084 for abs in ctx.matches(m):
5087 5085 if opts.get('fullpath'):
5088 5086 ui.write(repo.wjoin(abs), end)
5089 5087 else:
5090 5088 ui.write(((pats and m.rel(abs)) or abs), end)
5091 5089 ret = 0
5092 5090
5093 5091 return ret
5094 5092
5095 5093 @command('^log|history',
5096 5094 [('f', 'follow', None,
5097 5095 _('follow changeset history, or file history across copies and renames')),
5098 5096 ('', 'follow-first', None,
5099 5097 _('only follow the first parent of merge changesets (DEPRECATED)')),
5100 5098 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
5101 5099 ('C', 'copies', None, _('show copied files')),
5102 5100 ('k', 'keyword', [],
5103 5101 _('do case-insensitive search for a given text'), _('TEXT')),
5104 5102 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
5105 5103 ('', 'removed', None, _('include revisions where files were removed')),
5106 5104 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
5107 5105 ('u', 'user', [], _('revisions committed by user'), _('USER')),
5108 5106 ('', 'only-branch', [],
5109 5107 _('show only changesets within the given named branch (DEPRECATED)'),
5110 5108 _('BRANCH')),
5111 5109 ('b', 'branch', [],
5112 5110 _('show changesets within the given named branch'), _('BRANCH')),
5113 5111 ('P', 'prune', [],
5114 5112 _('do not display revision or any of its ancestors'), _('REV')),
5115 5113 ] + logopts + walkopts,
5116 5114 _('[OPTION]... [FILE]'),
5117 5115 inferrepo=True)
5118 5116 def log(ui, repo, *pats, **opts):
5119 5117 """show revision history of entire repository or files
5120 5118
5121 5119 Print the revision history of the specified files or the entire
5122 5120 project.
5123 5121
5124 5122 If no revision range is specified, the default is ``tip:0`` unless
5125 5123 --follow is set, in which case the working directory parent is
5126 5124 used as the starting revision.
5127 5125
5128 5126 File history is shown without following rename or copy history of
5129 5127 files. Use -f/--follow with a filename to follow history across
5130 5128 renames and copies. --follow without a filename will only show
5131 5129 ancestors or descendants of the starting revision.
5132 5130
5133 5131 By default this command prints revision number and changeset id,
5134 5132 tags, non-trivial parents, user, date and time, and a summary for
5135 5133 each commit. When the -v/--verbose switch is used, the list of
5136 5134 changed files and full commit message are shown.
5137 5135
5138 5136 With --graph the revisions are shown as an ASCII art DAG with the most
5139 5137 recent changeset at the top.
5140 5138 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
5141 5139 and '+' represents a fork where the changeset from the lines below is a
5142 5140 parent of the 'o' merge on the same line.
5143 5141
5144 5142 .. note::
5145 5143
5146 5144 :hg:`log --patch` may generate unexpected diff output for merge
5147 5145 changesets, as it will only compare the merge changeset against
5148 5146 its first parent. Also, only files different from BOTH parents
5149 5147 will appear in files:.
5150 5148
5151 5149 .. note::
5152 5150
5153 5151 For performance reasons, :hg:`log FILE` may omit duplicate changes
5154 5152 made on branches and will not show removals or mode changes. To
5155 5153 see all such changes, use the --removed switch.
5156 5154
5157 5155 .. container:: verbose
5158 5156
5159 5157 Some examples:
5160 5158
5161 5159 - changesets with full descriptions and file lists::
5162 5160
5163 5161 hg log -v
5164 5162
5165 5163 - changesets ancestral to the working directory::
5166 5164
5167 5165 hg log -f
5168 5166
5169 5167 - last 10 commits on the current branch::
5170 5168
5171 5169 hg log -l 10 -b .
5172 5170
5173 5171 - changesets showing all modifications of a file, including removals::
5174 5172
5175 5173 hg log --removed file.c
5176 5174
5177 5175 - all changesets that touch a directory, with diffs, excluding merges::
5178 5176
5179 5177 hg log -Mp lib/
5180 5178
5181 5179 - all revision numbers that match a keyword::
5182 5180
5183 5181 hg log -k bug --template "{rev}\\n"
5184 5182
5185 5183 - the full hash identifier of the working directory parent::
5186 5184
5187 5185 hg log -r . --template "{node}\\n"
5188 5186
5189 5187 - list available log templates::
5190 5188
5191 5189 hg log -T list
5192 5190
5193 5191 - check if a given changeset is included in a tagged release::
5194 5192
5195 5193 hg log -r "a21ccf and ancestor(1.9)"
5196 5194
5197 5195 - find all changesets by some user in a date range::
5198 5196
5199 5197 hg log -k alice -d "may 2008 to jul 2008"
5200 5198
5201 5199 - summary of all changesets after the last tag::
5202 5200
5203 5201 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
5204 5202
5205 5203 See :hg:`help dates` for a list of formats valid for -d/--date.
5206 5204
5207 5205 See :hg:`help revisions` and :hg:`help revsets` for more about
5208 5206 specifying and ordering revisions.
5209 5207
5210 5208 See :hg:`help templates` for more about pre-packaged styles and
5211 5209 specifying custom templates.
5212 5210
5213 5211 Returns 0 on success.
5214 5212
5215 5213 """
5216 5214 if opts.get('follow') and opts.get('rev'):
5217 5215 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
5218 5216 del opts['follow']
5219 5217
5220 5218 if opts.get('graph'):
5221 5219 return cmdutil.graphlog(ui, repo, *pats, **opts)
5222 5220
5223 5221 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
5224 5222 limit = cmdutil.loglimit(opts)
5225 5223 count = 0
5226 5224
5227 5225 getrenamed = None
5228 5226 if opts.get('copies'):
5229 5227 endrev = None
5230 5228 if opts.get('rev'):
5231 5229 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
5232 5230 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
5233 5231
5234 5232 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
5235 5233 for rev in revs:
5236 5234 if count == limit:
5237 5235 break
5238 5236 ctx = repo[rev]
5239 5237 copies = None
5240 5238 if getrenamed is not None and rev:
5241 5239 copies = []
5242 5240 for fn in ctx.files():
5243 5241 rename = getrenamed(fn, rev)
5244 5242 if rename:
5245 5243 copies.append((fn, rename[0]))
5246 5244 if filematcher:
5247 5245 revmatchfn = filematcher(ctx.rev())
5248 5246 else:
5249 5247 revmatchfn = None
5250 5248 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
5251 5249 if displayer.flush(ctx):
5252 5250 count += 1
5253 5251
5254 5252 displayer.close()
5255 5253
5256 5254 @command('manifest',
5257 5255 [('r', 'rev', '', _('revision to display'), _('REV')),
5258 5256 ('', 'all', False, _("list files from all revisions"))]
5259 5257 + formatteropts,
5260 5258 _('[-r REV]'))
5261 5259 def manifest(ui, repo, node=None, rev=None, **opts):
5262 5260 """output the current or given revision of the project manifest
5263 5261
5264 5262 Print a list of version controlled files for the given revision.
5265 5263 If no revision is given, the first parent of the working directory
5266 5264 is used, or the null revision if no revision is checked out.
5267 5265
5268 5266 With -v, print file permissions, symlink and executable bits.
5269 5267 With --debug, print file revision hashes.
5270 5268
5271 5269 If option --all is specified, the list of all files from all revisions
5272 5270 is printed. This includes deleted and renamed files.
5273 5271
5274 5272 Returns 0 on success.
5275 5273 """
5276 5274
5277 5275 fm = ui.formatter('manifest', opts)
5278 5276
5279 5277 if opts.get('all'):
5280 5278 if rev or node:
5281 5279 raise error.Abort(_("can't specify a revision with --all"))
5282 5280
5283 5281 res = []
5284 5282 prefix = "data/"
5285 5283 suffix = ".i"
5286 5284 plen = len(prefix)
5287 5285 slen = len(suffix)
5288 5286 with repo.lock():
5289 5287 for fn, b, size in repo.store.datafiles():
5290 5288 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
5291 5289 res.append(fn[plen:-slen])
5292 5290 for f in res:
5293 5291 fm.startitem()
5294 5292 fm.write("path", '%s\n', f)
5295 5293 fm.end()
5296 5294 return
5297 5295
5298 5296 if rev and node:
5299 5297 raise error.Abort(_("please specify just one revision"))
5300 5298
5301 5299 if not node:
5302 5300 node = rev
5303 5301
5304 5302 char = {'l': '@', 'x': '*', '': ''}
5305 5303 mode = {'l': '644', 'x': '755', '': '644'}
5306 5304 ctx = scmutil.revsingle(repo, node)
5307 5305 mf = ctx.manifest()
5308 5306 for f in ctx:
5309 5307 fm.startitem()
5310 5308 fl = ctx[f].flags()
5311 5309 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
5312 5310 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
5313 5311 fm.write('path', '%s\n', f)
5314 5312 fm.end()
5315 5313
5316 5314 @command('^merge',
5317 5315 [('f', 'force', None,
5318 5316 _('force a merge including outstanding changes (DEPRECATED)')),
5319 5317 ('r', 'rev', '', _('revision to merge'), _('REV')),
5320 5318 ('P', 'preview', None,
5321 5319 _('review revisions to merge (no merge is performed)'))
5322 5320 ] + mergetoolopts,
5323 5321 _('[-P] [[-r] REV]'))
5324 5322 def merge(ui, repo, node=None, **opts):
5325 5323 """merge another revision into working directory
5326 5324
5327 5325 The current working directory is updated with all changes made in
5328 5326 the requested revision since the last common predecessor revision.
5329 5327
5330 5328 Files that changed between either parent are marked as changed for
5331 5329 the next commit and a commit must be performed before any further
5332 5330 updates to the repository are allowed. The next commit will have
5333 5331 two parents.
5334 5332
5335 5333 ``--tool`` can be used to specify the merge tool used for file
5336 5334 merges. It overrides the HGMERGE environment variable and your
5337 5335 configuration files. See :hg:`help merge-tools` for options.
5338 5336
5339 5337 If no revision is specified, the working directory's parent is a
5340 5338 head revision, and the current branch contains exactly one other
5341 5339 head, the other head is merged with by default. Otherwise, an
5342 5340 explicit revision with which to merge with must be provided.
5343 5341
5344 5342 See :hg:`help resolve` for information on handling file conflicts.
5345 5343
5346 5344 To undo an uncommitted merge, use :hg:`update --clean .` which
5347 5345 will check out a clean copy of the original merge parent, losing
5348 5346 all changes.
5349 5347
5350 5348 Returns 0 on success, 1 if there are unresolved files.
5351 5349 """
5352 5350
5353 5351 if opts.get('rev') and node:
5354 5352 raise error.Abort(_("please specify just one revision"))
5355 5353 if not node:
5356 5354 node = opts.get('rev')
5357 5355
5358 5356 if node:
5359 5357 node = scmutil.revsingle(repo, node).node()
5360 5358
5361 5359 if not node:
5362 5360 node = repo[destutil.destmerge(repo)].node()
5363 5361
5364 5362 if opts.get('preview'):
5365 5363 # find nodes that are ancestors of p2 but not of p1
5366 5364 p1 = repo.lookup('.')
5367 5365 p2 = repo.lookup(node)
5368 5366 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
5369 5367
5370 5368 displayer = cmdutil.show_changeset(ui, repo, opts)
5371 5369 for node in nodes:
5372 5370 displayer.show(repo[node])
5373 5371 displayer.close()
5374 5372 return 0
5375 5373
5376 5374 try:
5377 5375 # ui.forcemerge is an internal variable, do not document
5378 5376 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
5379 5377 force = opts.get('force')
5380 5378 return hg.merge(repo, node, force=force, mergeforce=force)
5381 5379 finally:
5382 5380 ui.setconfig('ui', 'forcemerge', '', 'merge')
5383 5381
5384 5382 @command('outgoing|out',
5385 5383 [('f', 'force', None, _('run even when the destination is unrelated')),
5386 5384 ('r', 'rev', [],
5387 5385 _('a changeset intended to be included in the destination'), _('REV')),
5388 5386 ('n', 'newest-first', None, _('show newest record first')),
5389 5387 ('B', 'bookmarks', False, _('compare bookmarks')),
5390 5388 ('b', 'branch', [], _('a specific branch you would like to push'),
5391 5389 _('BRANCH')),
5392 5390 ] + logopts + remoteopts + subrepoopts,
5393 5391 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
5394 5392 def outgoing(ui, repo, dest=None, **opts):
5395 5393 """show changesets not found in the destination
5396 5394
5397 5395 Show changesets not found in the specified destination repository
5398 5396 or the default push location. These are the changesets that would
5399 5397 be pushed if a push was requested.
5400 5398
5401 5399 See pull for details of valid destination formats.
5402 5400
5403 5401 .. container:: verbose
5404 5402
5405 5403 With -B/--bookmarks, the result of bookmark comparison between
5406 5404 local and remote repositories is displayed. With -v/--verbose,
5407 5405 status is also displayed for each bookmark like below::
5408 5406
5409 5407 BM1 01234567890a added
5410 5408 BM2 deleted
5411 5409 BM3 234567890abc advanced
5412 5410 BM4 34567890abcd diverged
5413 5411 BM5 4567890abcde changed
5414 5412
5415 5413 The action taken when pushing depends on the
5416 5414 status of each bookmark:
5417 5415
5418 5416 :``added``: push with ``-B`` will create it
5419 5417 :``deleted``: push with ``-B`` will delete it
5420 5418 :``advanced``: push will update it
5421 5419 :``diverged``: push with ``-B`` will update it
5422 5420 :``changed``: push with ``-B`` will update it
5423 5421
5424 5422 From the point of view of pushing behavior, bookmarks
5425 5423 existing only in the remote repository are treated as
5426 5424 ``deleted``, even if it is in fact added remotely.
5427 5425
5428 5426 Returns 0 if there are outgoing changes, 1 otherwise.
5429 5427 """
5430 5428 if opts.get('graph'):
5431 5429 cmdutil.checkunsupportedgraphflags([], opts)
5432 5430 o, other = hg._outgoing(ui, repo, dest, opts)
5433 5431 if not o:
5434 5432 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5435 5433 return
5436 5434
5437 5435 revdag = cmdutil.graphrevs(repo, o, opts)
5438 5436 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
5439 5437 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
5440 5438 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5441 5439 return 0
5442 5440
5443 5441 if opts.get('bookmarks'):
5444 5442 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5445 5443 dest, branches = hg.parseurl(dest, opts.get('branch'))
5446 5444 other = hg.peer(repo, opts, dest)
5447 5445 if 'bookmarks' not in other.listkeys('namespaces'):
5448 5446 ui.warn(_("remote doesn't support bookmarks\n"))
5449 5447 return 0
5450 5448 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
5451 5449 return bookmarks.outgoing(ui, repo, other)
5452 5450
5453 5451 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
5454 5452 try:
5455 5453 return hg.outgoing(ui, repo, dest, opts)
5456 5454 finally:
5457 5455 del repo._subtoppath
5458 5456
5459 5457 @command('parents',
5460 5458 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
5461 5459 ] + templateopts,
5462 5460 _('[-r REV] [FILE]'),
5463 5461 inferrepo=True)
5464 5462 def parents(ui, repo, file_=None, **opts):
5465 5463 """show the parents of the working directory or revision (DEPRECATED)
5466 5464
5467 5465 Print the working directory's parent revisions. If a revision is
5468 5466 given via -r/--rev, the parent of that revision will be printed.
5469 5467 If a file argument is given, the revision in which the file was
5470 5468 last changed (before the working directory revision or the
5471 5469 argument to --rev if given) is printed.
5472 5470
5473 5471 This command is equivalent to::
5474 5472
5475 5473 hg log -r "p1()+p2()" or
5476 5474 hg log -r "p1(REV)+p2(REV)" or
5477 5475 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5478 5476 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5479 5477
5480 5478 See :hg:`summary` and :hg:`help revsets` for related information.
5481 5479
5482 5480 Returns 0 on success.
5483 5481 """
5484 5482
5485 5483 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
5486 5484
5487 5485 if file_:
5488 5486 m = scmutil.match(ctx, (file_,), opts)
5489 5487 if m.anypats() or len(m.files()) != 1:
5490 5488 raise error.Abort(_('can only specify an explicit filename'))
5491 5489 file_ = m.files()[0]
5492 5490 filenodes = []
5493 5491 for cp in ctx.parents():
5494 5492 if not cp:
5495 5493 continue
5496 5494 try:
5497 5495 filenodes.append(cp.filenode(file_))
5498 5496 except error.LookupError:
5499 5497 pass
5500 5498 if not filenodes:
5501 5499 raise error.Abort(_("'%s' not found in manifest!") % file_)
5502 5500 p = []
5503 5501 for fn in filenodes:
5504 5502 fctx = repo.filectx(file_, fileid=fn)
5505 5503 p.append(fctx.node())
5506 5504 else:
5507 5505 p = [cp.node() for cp in ctx.parents()]
5508 5506
5509 5507 displayer = cmdutil.show_changeset(ui, repo, opts)
5510 5508 for n in p:
5511 5509 if n != nullid:
5512 5510 displayer.show(repo[n])
5513 5511 displayer.close()
5514 5512
5515 5513 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
5516 5514 def paths(ui, repo, search=None, **opts):
5517 5515 """show aliases for remote repositories
5518 5516
5519 5517 Show definition of symbolic path name NAME. If no name is given,
5520 5518 show definition of all available names.
5521 5519
5522 5520 Option -q/--quiet suppresses all output when searching for NAME
5523 5521 and shows only the path names when listing all definitions.
5524 5522
5525 5523 Path names are defined in the [paths] section of your
5526 5524 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5527 5525 repository, ``.hg/hgrc`` is used, too.
5528 5526
5529 5527 The path names ``default`` and ``default-push`` have a special
5530 5528 meaning. When performing a push or pull operation, they are used
5531 5529 as fallbacks if no location is specified on the command-line.
5532 5530 When ``default-push`` is set, it will be used for push and
5533 5531 ``default`` will be used for pull; otherwise ``default`` is used
5534 5532 as the fallback for both. When cloning a repository, the clone
5535 5533 source is written as ``default`` in ``.hg/hgrc``.
5536 5534
5537 5535 .. note::
5538 5536
5539 5537 ``default`` and ``default-push`` apply to all inbound (e.g.
5540 5538 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5541 5539 and :hg:`bundle`) operations.
5542 5540
5543 5541 See :hg:`help urls` for more information.
5544 5542
5545 5543 Returns 0 on success.
5546 5544 """
5547 5545 if search:
5548 5546 pathitems = [(name, path) for name, path in ui.paths.iteritems()
5549 5547 if name == search]
5550 5548 else:
5551 5549 pathitems = sorted(ui.paths.iteritems())
5552 5550
5553 5551 fm = ui.formatter('paths', opts)
5554 5552 if fm:
5555 5553 hidepassword = str
5556 5554 else:
5557 5555 hidepassword = util.hidepassword
5558 5556 if ui.quiet:
5559 5557 namefmt = '%s\n'
5560 5558 else:
5561 5559 namefmt = '%s = '
5562 5560 showsubopts = not search and not ui.quiet
5563 5561
5564 5562 for name, path in pathitems:
5565 5563 fm.startitem()
5566 5564 fm.condwrite(not search, 'name', namefmt, name)
5567 5565 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
5568 5566 for subopt, value in sorted(path.suboptions.items()):
5569 5567 assert subopt not in ('name', 'url')
5570 5568 if showsubopts:
5571 5569 fm.plain('%s:%s = ' % (name, subopt))
5572 5570 fm.condwrite(showsubopts, subopt, '%s\n', value)
5573 5571
5574 5572 fm.end()
5575 5573
5576 5574 if search and not pathitems:
5577 5575 if not ui.quiet:
5578 5576 ui.warn(_("not found!\n"))
5579 5577 return 1
5580 5578 else:
5581 5579 return 0
5582 5580
5583 5581 @command('phase',
5584 5582 [('p', 'public', False, _('set changeset phase to public')),
5585 5583 ('d', 'draft', False, _('set changeset phase to draft')),
5586 5584 ('s', 'secret', False, _('set changeset phase to secret')),
5587 5585 ('f', 'force', False, _('allow to move boundary backward')),
5588 5586 ('r', 'rev', [], _('target revision'), _('REV')),
5589 5587 ],
5590 5588 _('[-p|-d|-s] [-f] [-r] [REV...]'))
5591 5589 def phase(ui, repo, *revs, **opts):
5592 5590 """set or show the current phase name
5593 5591
5594 5592 With no argument, show the phase name of the current revision(s).
5595 5593
5596 5594 With one of -p/--public, -d/--draft or -s/--secret, change the
5597 5595 phase value of the specified revisions.
5598 5596
5599 5597 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
5600 5598 lower phase to an higher phase. Phases are ordered as follows::
5601 5599
5602 5600 public < draft < secret
5603 5601
5604 5602 Returns 0 on success, 1 if some phases could not be changed.
5605 5603
5606 5604 (For more information about the phases concept, see :hg:`help phases`.)
5607 5605 """
5608 5606 # search for a unique phase argument
5609 5607 targetphase = None
5610 5608 for idx, name in enumerate(phases.phasenames):
5611 5609 if opts[name]:
5612 5610 if targetphase is not None:
5613 5611 raise error.Abort(_('only one phase can be specified'))
5614 5612 targetphase = idx
5615 5613
5616 5614 # look for specified revision
5617 5615 revs = list(revs)
5618 5616 revs.extend(opts['rev'])
5619 5617 if not revs:
5620 5618 # display both parents as the second parent phase can influence
5621 5619 # the phase of a merge commit
5622 5620 revs = [c.rev() for c in repo[None].parents()]
5623 5621
5624 5622 revs = scmutil.revrange(repo, revs)
5625 5623
5626 5624 lock = None
5627 5625 ret = 0
5628 5626 if targetphase is None:
5629 5627 # display
5630 5628 for r in revs:
5631 5629 ctx = repo[r]
5632 5630 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5633 5631 else:
5634 5632 tr = None
5635 5633 lock = repo.lock()
5636 5634 try:
5637 5635 tr = repo.transaction("phase")
5638 5636 # set phase
5639 5637 if not revs:
5640 5638 raise error.Abort(_('empty revision set'))
5641 5639 nodes = [repo[r].node() for r in revs]
5642 5640 # moving revision from public to draft may hide them
5643 5641 # We have to check result on an unfiltered repository
5644 5642 unfi = repo.unfiltered()
5645 5643 getphase = unfi._phasecache.phase
5646 5644 olddata = [getphase(unfi, r) for r in unfi]
5647 5645 phases.advanceboundary(repo, tr, targetphase, nodes)
5648 5646 if opts['force']:
5649 5647 phases.retractboundary(repo, tr, targetphase, nodes)
5650 5648 tr.close()
5651 5649 finally:
5652 5650 if tr is not None:
5653 5651 tr.release()
5654 5652 lock.release()
5655 5653 getphase = unfi._phasecache.phase
5656 5654 newdata = [getphase(unfi, r) for r in unfi]
5657 5655 changes = sum(newdata[r] != olddata[r] for r in unfi)
5658 5656 cl = unfi.changelog
5659 5657 rejected = [n for n in nodes
5660 5658 if newdata[cl.rev(n)] < targetphase]
5661 5659 if rejected:
5662 5660 ui.warn(_('cannot move %i changesets to a higher '
5663 5661 'phase, use --force\n') % len(rejected))
5664 5662 ret = 1
5665 5663 if changes:
5666 5664 msg = _('phase changed for %i changesets\n') % changes
5667 5665 if ret:
5668 5666 ui.status(msg)
5669 5667 else:
5670 5668 ui.note(msg)
5671 5669 else:
5672 5670 ui.warn(_('no phases changed\n'))
5673 5671 return ret
5674 5672
5675 5673 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5676 5674 """Run after a changegroup has been added via pull/unbundle
5677 5675
5678 5676 This takes arguments below:
5679 5677
5680 5678 :modheads: change of heads by pull/unbundle
5681 5679 :optupdate: updating working directory is needed or not
5682 5680 :checkout: update destination revision (or None to default destination)
5683 5681 :brev: a name, which might be a bookmark to be activated after updating
5684 5682 """
5685 5683 if modheads == 0:
5686 5684 return
5687 5685 if optupdate:
5688 5686 try:
5689 5687 return hg.updatetotally(ui, repo, checkout, brev)
5690 5688 except error.UpdateAbort as inst:
5691 5689 msg = _("not updating: %s") % str(inst)
5692 5690 hint = inst.hint
5693 5691 raise error.UpdateAbort(msg, hint=hint)
5694 5692 if modheads > 1:
5695 5693 currentbranchheads = len(repo.branchheads())
5696 5694 if currentbranchheads == modheads:
5697 5695 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
5698 5696 elif currentbranchheads > 1:
5699 5697 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
5700 5698 "merge)\n"))
5701 5699 else:
5702 5700 ui.status(_("(run 'hg heads' to see heads)\n"))
5703 5701 else:
5704 5702 ui.status(_("(run 'hg update' to get a working copy)\n"))
5705 5703
5706 5704 @command('^pull',
5707 5705 [('u', 'update', None,
5708 5706 _('update to new branch head if changesets were pulled')),
5709 5707 ('f', 'force', None, _('run even when remote repository is unrelated')),
5710 5708 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
5711 5709 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
5712 5710 ('b', 'branch', [], _('a specific branch you would like to pull'),
5713 5711 _('BRANCH')),
5714 5712 ] + remoteopts,
5715 5713 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
5716 5714 def pull(ui, repo, source="default", **opts):
5717 5715 """pull changes from the specified source
5718 5716
5719 5717 Pull changes from a remote repository to a local one.
5720 5718
5721 5719 This finds all changes from the repository at the specified path
5722 5720 or URL and adds them to a local repository (the current one unless
5723 5721 -R is specified). By default, this does not update the copy of the
5724 5722 project in the working directory.
5725 5723
5726 5724 Use :hg:`incoming` if you want to see what would have been added
5727 5725 by a pull at the time you issued this command. If you then decide
5728 5726 to add those changes to the repository, you should use :hg:`pull
5729 5727 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5730 5728
5731 5729 If SOURCE is omitted, the 'default' path will be used.
5732 5730 See :hg:`help urls` for more information.
5733 5731
5734 5732 Returns 0 on success, 1 if an update had unresolved files.
5735 5733 """
5736 5734 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5737 5735 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5738 5736 other = hg.peer(repo, opts, source)
5739 5737 try:
5740 5738 revs, checkout = hg.addbranchrevs(repo, other, branches,
5741 5739 opts.get('rev'))
5742 5740
5743 5741
5744 5742 pullopargs = {}
5745 5743 if opts.get('bookmark'):
5746 5744 if not revs:
5747 5745 revs = []
5748 5746 # The list of bookmark used here is not the one used to actually
5749 5747 # update the bookmark name. This can result in the revision pulled
5750 5748 # not ending up with the name of the bookmark because of a race
5751 5749 # condition on the server. (See issue 4689 for details)
5752 5750 remotebookmarks = other.listkeys('bookmarks')
5753 5751 pullopargs['remotebookmarks'] = remotebookmarks
5754 5752 for b in opts['bookmark']:
5755 5753 if b not in remotebookmarks:
5756 5754 raise error.Abort(_('remote bookmark %s not found!') % b)
5757 5755 revs.append(remotebookmarks[b])
5758 5756
5759 5757 if revs:
5760 5758 try:
5761 5759 # When 'rev' is a bookmark name, we cannot guarantee that it
5762 5760 # will be updated with that name because of a race condition
5763 5761 # server side. (See issue 4689 for details)
5764 5762 oldrevs = revs
5765 5763 revs = [] # actually, nodes
5766 5764 for r in oldrevs:
5767 5765 node = other.lookup(r)
5768 5766 revs.append(node)
5769 5767 if r == checkout:
5770 5768 checkout = node
5771 5769 except error.CapabilityError:
5772 5770 err = _("other repository doesn't support revision lookup, "
5773 5771 "so a rev cannot be specified.")
5774 5772 raise error.Abort(err)
5775 5773
5776 5774 pullopargs.update(opts.get('opargs', {}))
5777 5775 modheads = exchange.pull(repo, other, heads=revs,
5778 5776 force=opts.get('force'),
5779 5777 bookmarks=opts.get('bookmark', ()),
5780 5778 opargs=pullopargs).cgresult
5781 5779
5782 5780 # brev is a name, which might be a bookmark to be activated at
5783 5781 # the end of the update. In other words, it is an explicit
5784 5782 # destination of the update
5785 5783 brev = None
5786 5784
5787 5785 if checkout:
5788 5786 checkout = str(repo.changelog.rev(checkout))
5789 5787
5790 5788 # order below depends on implementation of
5791 5789 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5792 5790 # because 'checkout' is determined without it.
5793 5791 if opts.get('rev'):
5794 5792 brev = opts['rev'][0]
5795 5793 elif opts.get('branch'):
5796 5794 brev = opts['branch'][0]
5797 5795 else:
5798 5796 brev = branches[0]
5799 5797 repo._subtoppath = source
5800 5798 try:
5801 5799 ret = postincoming(ui, repo, modheads, opts.get('update'),
5802 5800 checkout, brev)
5803 5801
5804 5802 finally:
5805 5803 del repo._subtoppath
5806 5804
5807 5805 finally:
5808 5806 other.close()
5809 5807 return ret
5810 5808
5811 5809 @command('^push',
5812 5810 [('f', 'force', None, _('force push')),
5813 5811 ('r', 'rev', [],
5814 5812 _('a changeset intended to be included in the destination'),
5815 5813 _('REV')),
5816 5814 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5817 5815 ('b', 'branch', [],
5818 5816 _('a specific branch you would like to push'), _('BRANCH')),
5819 5817 ('', 'new-branch', False, _('allow pushing a new branch')),
5820 5818 ] + remoteopts,
5821 5819 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5822 5820 def push(ui, repo, dest=None, **opts):
5823 5821 """push changes to the specified destination
5824 5822
5825 5823 Push changesets from the local repository to the specified
5826 5824 destination.
5827 5825
5828 5826 This operation is symmetrical to pull: it is identical to a pull
5829 5827 in the destination repository from the current one.
5830 5828
5831 5829 By default, push will not allow creation of new heads at the
5832 5830 destination, since multiple heads would make it unclear which head
5833 5831 to use. In this situation, it is recommended to pull and merge
5834 5832 before pushing.
5835 5833
5836 5834 Use --new-branch if you want to allow push to create a new named
5837 5835 branch that is not present at the destination. This allows you to
5838 5836 only create a new branch without forcing other changes.
5839 5837
5840 5838 .. note::
5841 5839
5842 5840 Extra care should be taken with the -f/--force option,
5843 5841 which will push all new heads on all branches, an action which will
5844 5842 almost always cause confusion for collaborators.
5845 5843
5846 5844 If -r/--rev is used, the specified revision and all its ancestors
5847 5845 will be pushed to the remote repository.
5848 5846
5849 5847 If -B/--bookmark is used, the specified bookmarked revision, its
5850 5848 ancestors, and the bookmark will be pushed to the remote
5851 5849 repository. Specifying ``.`` is equivalent to specifying the active
5852 5850 bookmark's name.
5853 5851
5854 5852 Please see :hg:`help urls` for important details about ``ssh://``
5855 5853 URLs. If DESTINATION is omitted, a default path will be used.
5856 5854
5857 5855 Returns 0 if push was successful, 1 if nothing to push.
5858 5856 """
5859 5857
5860 5858 if opts.get('bookmark'):
5861 5859 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5862 5860 for b in opts['bookmark']:
5863 5861 # translate -B options to -r so changesets get pushed
5864 5862 b = repo._bookmarks.expandname(b)
5865 5863 if b in repo._bookmarks:
5866 5864 opts.setdefault('rev', []).append(b)
5867 5865 else:
5868 5866 # if we try to push a deleted bookmark, translate it to null
5869 5867 # this lets simultaneous -r, -b options continue working
5870 5868 opts.setdefault('rev', []).append("null")
5871 5869
5872 5870 path = ui.paths.getpath(dest, default=('default-push', 'default'))
5873 5871 if not path:
5874 5872 raise error.Abort(_('default repository not configured!'),
5875 5873 hint=_('see the "path" section in "hg help config"'))
5876 5874 dest = path.pushloc or path.loc
5877 5875 branches = (path.branch, opts.get('branch') or [])
5878 5876 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5879 5877 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5880 5878 other = hg.peer(repo, opts, dest)
5881 5879
5882 5880 if revs:
5883 5881 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5884 5882 if not revs:
5885 5883 raise error.Abort(_("specified revisions evaluate to an empty set"),
5886 5884 hint=_("use different revision arguments"))
5887 5885
5888 5886 repo._subtoppath = dest
5889 5887 try:
5890 5888 # push subrepos depth-first for coherent ordering
5891 5889 c = repo['']
5892 5890 subs = c.substate # only repos that are committed
5893 5891 for s in sorted(subs):
5894 5892 result = c.sub(s).push(opts)
5895 5893 if result == 0:
5896 5894 return not result
5897 5895 finally:
5898 5896 del repo._subtoppath
5899 5897 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5900 5898 newbranch=opts.get('new_branch'),
5901 5899 bookmarks=opts.get('bookmark', ()),
5902 5900 opargs=opts.get('opargs'))
5903 5901
5904 5902 result = not pushop.cgresult
5905 5903
5906 5904 if pushop.bkresult is not None:
5907 5905 if pushop.bkresult == 2:
5908 5906 result = 2
5909 5907 elif not result and pushop.bkresult:
5910 5908 result = 2
5911 5909
5912 5910 return result
5913 5911
5914 5912 @command('recover', [])
5915 5913 def recover(ui, repo):
5916 5914 """roll back an interrupted transaction
5917 5915
5918 5916 Recover from an interrupted commit or pull.
5919 5917
5920 5918 This command tries to fix the repository status after an
5921 5919 interrupted operation. It should only be necessary when Mercurial
5922 5920 suggests it.
5923 5921
5924 5922 Returns 0 if successful, 1 if nothing to recover or verify fails.
5925 5923 """
5926 5924 if repo.recover():
5927 5925 return hg.verify(repo)
5928 5926 return 1
5929 5927
5930 5928 @command('^remove|rm',
5931 5929 [('A', 'after', None, _('record delete for missing files')),
5932 5930 ('f', 'force', None,
5933 5931 _('remove (and delete) file even if added or modified')),
5934 5932 ] + subrepoopts + walkopts,
5935 5933 _('[OPTION]... FILE...'),
5936 5934 inferrepo=True)
5937 5935 def remove(ui, repo, *pats, **opts):
5938 5936 """remove the specified files on the next commit
5939 5937
5940 5938 Schedule the indicated files for removal from the current branch.
5941 5939
5942 5940 This command schedules the files to be removed at the next commit.
5943 5941 To undo a remove before that, see :hg:`revert`. To undo added
5944 5942 files, see :hg:`forget`.
5945 5943
5946 5944 .. container:: verbose
5947 5945
5948 5946 -A/--after can be used to remove only files that have already
5949 5947 been deleted, -f/--force can be used to force deletion, and -Af
5950 5948 can be used to remove files from the next revision without
5951 5949 deleting them from the working directory.
5952 5950
5953 5951 The following table details the behavior of remove for different
5954 5952 file states (columns) and option combinations (rows). The file
5955 5953 states are Added [A], Clean [C], Modified [M] and Missing [!]
5956 5954 (as reported by :hg:`status`). The actions are Warn, Remove
5957 5955 (from branch) and Delete (from disk):
5958 5956
5959 5957 ========= == == == ==
5960 5958 opt/state A C M !
5961 5959 ========= == == == ==
5962 5960 none W RD W R
5963 5961 -f R RD RD R
5964 5962 -A W W W R
5965 5963 -Af R R R R
5966 5964 ========= == == == ==
5967 5965
5968 5966 .. note::
5969 5967
5970 5968 :hg:`remove` never deletes files in Added [A] state from the
5971 5969 working directory, not even if ``--force`` is specified.
5972 5970
5973 5971 Returns 0 on success, 1 if any warnings encountered.
5974 5972 """
5975 5973
5976 5974 after, force = opts.get('after'), opts.get('force')
5977 5975 if not pats and not after:
5978 5976 raise error.Abort(_('no files specified'))
5979 5977
5980 5978 m = scmutil.match(repo[None], pats, opts)
5981 5979 subrepos = opts.get('subrepos')
5982 5980 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5983 5981
5984 5982 @command('rename|move|mv',
5985 5983 [('A', 'after', None, _('record a rename that has already occurred')),
5986 5984 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5987 5985 ] + walkopts + dryrunopts,
5988 5986 _('[OPTION]... SOURCE... DEST'))
5989 5987 def rename(ui, repo, *pats, **opts):
5990 5988 """rename files; equivalent of copy + remove
5991 5989
5992 5990 Mark dest as copies of sources; mark sources for deletion. If dest
5993 5991 is a directory, copies are put in that directory. If dest is a
5994 5992 file, there can only be one source.
5995 5993
5996 5994 By default, this command copies the contents of files as they
5997 5995 exist in the working directory. If invoked with -A/--after, the
5998 5996 operation is recorded, but no copying is performed.
5999 5997
6000 5998 This command takes effect at the next commit. To undo a rename
6001 5999 before that, see :hg:`revert`.
6002 6000
6003 6001 Returns 0 on success, 1 if errors are encountered.
6004 6002 """
6005 6003 with repo.wlock(False):
6006 6004 return cmdutil.copy(ui, repo, pats, opts, rename=True)
6007 6005
6008 6006 @command('resolve',
6009 6007 [('a', 'all', None, _('select all unresolved files')),
6010 6008 ('l', 'list', None, _('list state of files needing merge')),
6011 6009 ('m', 'mark', None, _('mark files as resolved')),
6012 6010 ('u', 'unmark', None, _('mark files as unresolved')),
6013 6011 ('n', 'no-status', None, _('hide status prefix'))]
6014 6012 + mergetoolopts + walkopts + formatteropts,
6015 6013 _('[OPTION]... [FILE]...'),
6016 6014 inferrepo=True)
6017 6015 def resolve(ui, repo, *pats, **opts):
6018 6016 """redo merges or set/view the merge status of files
6019 6017
6020 6018 Merges with unresolved conflicts are often the result of
6021 6019 non-interactive merging using the ``internal:merge`` configuration
6022 6020 setting, or a command-line merge tool like ``diff3``. The resolve
6023 6021 command is used to manage the files involved in a merge, after
6024 6022 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
6025 6023 working directory must have two parents). See :hg:`help
6026 6024 merge-tools` for information on configuring merge tools.
6027 6025
6028 6026 The resolve command can be used in the following ways:
6029 6027
6030 6028 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
6031 6029 files, discarding any previous merge attempts. Re-merging is not
6032 6030 performed for files already marked as resolved. Use ``--all/-a``
6033 6031 to select all unresolved files. ``--tool`` can be used to specify
6034 6032 the merge tool used for the given files. It overrides the HGMERGE
6035 6033 environment variable and your configuration files. Previous file
6036 6034 contents are saved with a ``.orig`` suffix.
6037 6035
6038 6036 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
6039 6037 (e.g. after having manually fixed-up the files). The default is
6040 6038 to mark all unresolved files.
6041 6039
6042 6040 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
6043 6041 default is to mark all resolved files.
6044 6042
6045 6043 - :hg:`resolve -l`: list files which had or still have conflicts.
6046 6044 In the printed list, ``U`` = unresolved and ``R`` = resolved.
6047 6045
6048 6046 .. note::
6049 6047
6050 6048 Mercurial will not let you commit files with unresolved merge
6051 6049 conflicts. You must use :hg:`resolve -m ...` before you can
6052 6050 commit after a conflicting merge.
6053 6051
6054 6052 Returns 0 on success, 1 if any files fail a resolve attempt.
6055 6053 """
6056 6054
6057 6055 flaglist = 'all mark unmark list no_status'.split()
6058 6056 all, mark, unmark, show, nostatus = \
6059 6057 [opts.get(o) for o in flaglist]
6060 6058
6061 6059 if (show and (mark or unmark)) or (mark and unmark):
6062 6060 raise error.Abort(_("too many options specified"))
6063 6061 if pats and all:
6064 6062 raise error.Abort(_("can't specify --all and patterns"))
6065 6063 if not (all or pats or show or mark or unmark):
6066 6064 raise error.Abort(_('no files or directories specified'),
6067 6065 hint=('use --all to re-merge all unresolved files'))
6068 6066
6069 6067 if show:
6070 6068 fm = ui.formatter('resolve', opts)
6071 6069 ms = mergemod.mergestate.read(repo)
6072 6070 m = scmutil.match(repo[None], pats, opts)
6073 6071 for f in ms:
6074 6072 if not m(f):
6075 6073 continue
6076 6074 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
6077 6075 'd': 'driverresolved'}[ms[f]]
6078 6076 fm.startitem()
6079 6077 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
6080 6078 fm.write('path', '%s\n', f, label=l)
6081 6079 fm.end()
6082 6080 return 0
6083 6081
6084 6082 with repo.wlock():
6085 6083 ms = mergemod.mergestate.read(repo)
6086 6084
6087 6085 if not (ms.active() or repo.dirstate.p2() != nullid):
6088 6086 raise error.Abort(
6089 6087 _('resolve command not applicable when not merging'))
6090 6088
6091 6089 wctx = repo[None]
6092 6090
6093 6091 if ms.mergedriver and ms.mdstate() == 'u':
6094 6092 proceed = mergemod.driverpreprocess(repo, ms, wctx)
6095 6093 ms.commit()
6096 6094 # allow mark and unmark to go through
6097 6095 if not mark and not unmark and not proceed:
6098 6096 return 1
6099 6097
6100 6098 m = scmutil.match(wctx, pats, opts)
6101 6099 ret = 0
6102 6100 didwork = False
6103 6101 runconclude = False
6104 6102
6105 6103 tocomplete = []
6106 6104 for f in ms:
6107 6105 if not m(f):
6108 6106 continue
6109 6107
6110 6108 didwork = True
6111 6109
6112 6110 # don't let driver-resolved files be marked, and run the conclude
6113 6111 # step if asked to resolve
6114 6112 if ms[f] == "d":
6115 6113 exact = m.exact(f)
6116 6114 if mark:
6117 6115 if exact:
6118 6116 ui.warn(_('not marking %s as it is driver-resolved\n')
6119 6117 % f)
6120 6118 elif unmark:
6121 6119 if exact:
6122 6120 ui.warn(_('not unmarking %s as it is driver-resolved\n')
6123 6121 % f)
6124 6122 else:
6125 6123 runconclude = True
6126 6124 continue
6127 6125
6128 6126 if mark:
6129 6127 ms.mark(f, "r")
6130 6128 elif unmark:
6131 6129 ms.mark(f, "u")
6132 6130 else:
6133 6131 # backup pre-resolve (merge uses .orig for its own purposes)
6134 6132 a = repo.wjoin(f)
6135 6133 try:
6136 6134 util.copyfile(a, a + ".resolve")
6137 6135 except (IOError, OSError) as inst:
6138 6136 if inst.errno != errno.ENOENT:
6139 6137 raise
6140 6138
6141 6139 try:
6142 6140 # preresolve file
6143 6141 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
6144 6142 'resolve')
6145 6143 complete, r = ms.preresolve(f, wctx)
6146 6144 if not complete:
6147 6145 tocomplete.append(f)
6148 6146 elif r:
6149 6147 ret = 1
6150 6148 finally:
6151 6149 ui.setconfig('ui', 'forcemerge', '', 'resolve')
6152 6150 ms.commit()
6153 6151
6154 6152 # replace filemerge's .orig file with our resolve file, but only
6155 6153 # for merges that are complete
6156 6154 if complete:
6157 6155 try:
6158 6156 util.rename(a + ".resolve",
6159 6157 scmutil.origpath(ui, repo, a))
6160 6158 except OSError as inst:
6161 6159 if inst.errno != errno.ENOENT:
6162 6160 raise
6163 6161
6164 6162 for f in tocomplete:
6165 6163 try:
6166 6164 # resolve file
6167 6165 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
6168 6166 'resolve')
6169 6167 r = ms.resolve(f, wctx)
6170 6168 if r:
6171 6169 ret = 1
6172 6170 finally:
6173 6171 ui.setconfig('ui', 'forcemerge', '', 'resolve')
6174 6172 ms.commit()
6175 6173
6176 6174 # replace filemerge's .orig file with our resolve file
6177 6175 a = repo.wjoin(f)
6178 6176 try:
6179 6177 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
6180 6178 except OSError as inst:
6181 6179 if inst.errno != errno.ENOENT:
6182 6180 raise
6183 6181
6184 6182 ms.commit()
6185 6183 ms.recordactions()
6186 6184
6187 6185 if not didwork and pats:
6188 6186 hint = None
6189 6187 if not any([p for p in pats if p.find(':') >= 0]):
6190 6188 pats = ['path:%s' % p for p in pats]
6191 6189 m = scmutil.match(wctx, pats, opts)
6192 6190 for f in ms:
6193 6191 if not m(f):
6194 6192 continue
6195 6193 flags = ''.join(['-%s ' % o[0] for o in flaglist
6196 6194 if opts.get(o)])
6197 6195 hint = _("(try: hg resolve %s%s)\n") % (
6198 6196 flags,
6199 6197 ' '.join(pats))
6200 6198 break
6201 6199 ui.warn(_("arguments do not match paths that need resolving\n"))
6202 6200 if hint:
6203 6201 ui.warn(hint)
6204 6202 elif ms.mergedriver and ms.mdstate() != 's':
6205 6203 # run conclude step when either a driver-resolved file is requested
6206 6204 # or there are no driver-resolved files
6207 6205 # we can't use 'ret' to determine whether any files are unresolved
6208 6206 # because we might not have tried to resolve some
6209 6207 if ((runconclude or not list(ms.driverresolved()))
6210 6208 and not list(ms.unresolved())):
6211 6209 proceed = mergemod.driverconclude(repo, ms, wctx)
6212 6210 ms.commit()
6213 6211 if not proceed:
6214 6212 return 1
6215 6213
6216 6214 # Nudge users into finishing an unfinished operation
6217 6215 unresolvedf = list(ms.unresolved())
6218 6216 driverresolvedf = list(ms.driverresolved())
6219 6217 if not unresolvedf and not driverresolvedf:
6220 6218 ui.status(_('(no more unresolved files)\n'))
6221 6219 cmdutil.checkafterresolved(repo)
6222 6220 elif not unresolvedf:
6223 6221 ui.status(_('(no more unresolved files -- '
6224 6222 'run "hg resolve --all" to conclude)\n'))
6225 6223
6226 6224 return ret
6227 6225
6228 6226 @command('revert',
6229 6227 [('a', 'all', None, _('revert all changes when no arguments given')),
6230 6228 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6231 6229 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
6232 6230 ('C', 'no-backup', None, _('do not save backup copies of files')),
6233 6231 ('i', 'interactive', None,
6234 6232 _('interactively select the changes (EXPERIMENTAL)')),
6235 6233 ] + walkopts + dryrunopts,
6236 6234 _('[OPTION]... [-r REV] [NAME]...'))
6237 6235 def revert(ui, repo, *pats, **opts):
6238 6236 """restore files to their checkout state
6239 6237
6240 6238 .. note::
6241 6239
6242 6240 To check out earlier revisions, you should use :hg:`update REV`.
6243 6241 To cancel an uncommitted merge (and lose your changes),
6244 6242 use :hg:`update --clean .`.
6245 6243
6246 6244 With no revision specified, revert the specified files or directories
6247 6245 to the contents they had in the parent of the working directory.
6248 6246 This restores the contents of files to an unmodified
6249 6247 state and unschedules adds, removes, copies, and renames. If the
6250 6248 working directory has two parents, you must explicitly specify a
6251 6249 revision.
6252 6250
6253 6251 Using the -r/--rev or -d/--date options, revert the given files or
6254 6252 directories to their states as of a specific revision. Because
6255 6253 revert does not change the working directory parents, this will
6256 6254 cause these files to appear modified. This can be helpful to "back
6257 6255 out" some or all of an earlier change. See :hg:`backout` for a
6258 6256 related method.
6259 6257
6260 6258 Modified files are saved with a .orig suffix before reverting.
6261 6259 To disable these backups, use --no-backup.
6262 6260
6263 6261 See :hg:`help dates` for a list of formats valid for -d/--date.
6264 6262
6265 6263 See :hg:`help backout` for a way to reverse the effect of an
6266 6264 earlier changeset.
6267 6265
6268 6266 Returns 0 on success.
6269 6267 """
6270 6268
6271 6269 if opts.get("date"):
6272 6270 if opts.get("rev"):
6273 6271 raise error.Abort(_("you can't specify a revision and a date"))
6274 6272 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
6275 6273
6276 6274 parent, p2 = repo.dirstate.parents()
6277 6275 if not opts.get('rev') and p2 != nullid:
6278 6276 # revert after merge is a trap for new users (issue2915)
6279 6277 raise error.Abort(_('uncommitted merge with no revision specified'),
6280 6278 hint=_('use "hg update" or see "hg help revert"'))
6281 6279
6282 6280 ctx = scmutil.revsingle(repo, opts.get('rev'))
6283 6281
6284 6282 if (not (pats or opts.get('include') or opts.get('exclude') or
6285 6283 opts.get('all') or opts.get('interactive'))):
6286 6284 msg = _("no files or directories specified")
6287 6285 if p2 != nullid:
6288 6286 hint = _("uncommitted merge, use --all to discard all changes,"
6289 6287 " or 'hg update -C .' to abort the merge")
6290 6288 raise error.Abort(msg, hint=hint)
6291 6289 dirty = any(repo.status())
6292 6290 node = ctx.node()
6293 6291 if node != parent:
6294 6292 if dirty:
6295 6293 hint = _("uncommitted changes, use --all to discard all"
6296 6294 " changes, or 'hg update %s' to update") % ctx.rev()
6297 6295 else:
6298 6296 hint = _("use --all to revert all files,"
6299 6297 " or 'hg update %s' to update") % ctx.rev()
6300 6298 elif dirty:
6301 6299 hint = _("uncommitted changes, use --all to discard all changes")
6302 6300 else:
6303 6301 hint = _("use --all to revert all files")
6304 6302 raise error.Abort(msg, hint=hint)
6305 6303
6306 6304 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
6307 6305
6308 6306 @command('rollback', dryrunopts +
6309 6307 [('f', 'force', False, _('ignore safety measures'))])
6310 6308 def rollback(ui, repo, **opts):
6311 6309 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6312 6310
6313 6311 Please use :hg:`commit --amend` instead of rollback to correct
6314 6312 mistakes in the last commit.
6315 6313
6316 6314 This command should be used with care. There is only one level of
6317 6315 rollback, and there is no way to undo a rollback. It will also
6318 6316 restore the dirstate at the time of the last transaction, losing
6319 6317 any dirstate changes since that time. This command does not alter
6320 6318 the working directory.
6321 6319
6322 6320 Transactions are used to encapsulate the effects of all commands
6323 6321 that create new changesets or propagate existing changesets into a
6324 6322 repository.
6325 6323
6326 6324 .. container:: verbose
6327 6325
6328 6326 For example, the following commands are transactional, and their
6329 6327 effects can be rolled back:
6330 6328
6331 6329 - commit
6332 6330 - import
6333 6331 - pull
6334 6332 - push (with this repository as the destination)
6335 6333 - unbundle
6336 6334
6337 6335 To avoid permanent data loss, rollback will refuse to rollback a
6338 6336 commit transaction if it isn't checked out. Use --force to
6339 6337 override this protection.
6340 6338
6341 6339 This command is not intended for use on public repositories. Once
6342 6340 changes are visible for pull by other users, rolling a transaction
6343 6341 back locally is ineffective (someone else may already have pulled
6344 6342 the changes). Furthermore, a race is possible with readers of the
6345 6343 repository; for example an in-progress pull from the repository
6346 6344 may fail if a rollback is performed.
6347 6345
6348 6346 Returns 0 on success, 1 if no rollback data is available.
6349 6347 """
6350 6348 return repo.rollback(dryrun=opts.get('dry_run'),
6351 6349 force=opts.get('force'))
6352 6350
6353 6351 @command('root', [])
6354 6352 def root(ui, repo):
6355 6353 """print the root (top) of the current working directory
6356 6354
6357 6355 Print the root directory of the current repository.
6358 6356
6359 6357 Returns 0 on success.
6360 6358 """
6361 6359 ui.write(repo.root + "\n")
6362 6360
6363 6361 @command('^serve',
6364 6362 [('A', 'accesslog', '', _('name of access log file to write to'),
6365 6363 _('FILE')),
6366 6364 ('d', 'daemon', None, _('run server in background')),
6367 6365 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
6368 6366 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
6369 6367 # use string type, then we can check if something was passed
6370 6368 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
6371 6369 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
6372 6370 _('ADDR')),
6373 6371 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
6374 6372 _('PREFIX')),
6375 6373 ('n', 'name', '',
6376 6374 _('name to show in web pages (default: working directory)'), _('NAME')),
6377 6375 ('', 'web-conf', '',
6378 6376 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
6379 6377 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
6380 6378 _('FILE')),
6381 6379 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
6382 6380 ('', 'stdio', None, _('for remote clients')),
6383 6381 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
6384 6382 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
6385 6383 ('', 'style', '', _('template style to use'), _('STYLE')),
6386 6384 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
6387 6385 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
6388 6386 _('[OPTION]...'),
6389 6387 optionalrepo=True)
6390 6388 def serve(ui, repo, **opts):
6391 6389 """start stand-alone webserver
6392 6390
6393 6391 Start a local HTTP repository browser and pull server. You can use
6394 6392 this for ad-hoc sharing and browsing of repositories. It is
6395 6393 recommended to use a real web server to serve a repository for
6396 6394 longer periods of time.
6397 6395
6398 6396 Please note that the server does not implement access control.
6399 6397 This means that, by default, anybody can read from the server and
6400 6398 nobody can write to it by default. Set the ``web.allow_push``
6401 6399 option to ``*`` to allow everybody to push to the server. You
6402 6400 should use a real web server if you need to authenticate users.
6403 6401
6404 6402 By default, the server logs accesses to stdout and errors to
6405 6403 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6406 6404 files.
6407 6405
6408 6406 To have the server choose a free port number to listen on, specify
6409 6407 a port number of 0; in this case, the server will print the port
6410 6408 number it uses.
6411 6409
6412 6410 Returns 0 on success.
6413 6411 """
6414 6412
6415 6413 if opts["stdio"] and opts["cmdserver"]:
6416 6414 raise error.Abort(_("cannot use --stdio with --cmdserver"))
6417 6415
6418 6416 if opts["stdio"]:
6419 6417 if repo is None:
6420 6418 raise error.RepoError(_("there is no Mercurial repository here"
6421 6419 " (.hg not found)"))
6422 6420 s = sshserver.sshserver(ui, repo)
6423 6421 s.serve_forever()
6424 6422
6425 6423 if opts["cmdserver"]:
6426 6424 service = commandserver.createservice(ui, repo, opts)
6427 6425 else:
6428 6426 service = hgweb.createservice(ui, repo, opts)
6429 6427 return cmdutil.service(opts, initfn=service.init, runfn=service.run)
6430 6428
6431 6429 @command('^status|st',
6432 6430 [('A', 'all', None, _('show status of all files')),
6433 6431 ('m', 'modified', None, _('show only modified files')),
6434 6432 ('a', 'added', None, _('show only added files')),
6435 6433 ('r', 'removed', None, _('show only removed files')),
6436 6434 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
6437 6435 ('c', 'clean', None, _('show only files without changes')),
6438 6436 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
6439 6437 ('i', 'ignored', None, _('show only ignored files')),
6440 6438 ('n', 'no-status', None, _('hide status prefix')),
6441 6439 ('C', 'copies', None, _('show source of copied files')),
6442 6440 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
6443 6441 ('', 'rev', [], _('show difference from revision'), _('REV')),
6444 6442 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
6445 6443 ] + walkopts + subrepoopts + formatteropts,
6446 6444 _('[OPTION]... [FILE]...'),
6447 6445 inferrepo=True)
6448 6446 def status(ui, repo, *pats, **opts):
6449 6447 """show changed files in the working directory
6450 6448
6451 6449 Show status of files in the repository. If names are given, only
6452 6450 files that match are shown. Files that are clean or ignored or
6453 6451 the source of a copy/move operation, are not listed unless
6454 6452 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6455 6453 Unless options described with "show only ..." are given, the
6456 6454 options -mardu are used.
6457 6455
6458 6456 Option -q/--quiet hides untracked (unknown and ignored) files
6459 6457 unless explicitly requested with -u/--unknown or -i/--ignored.
6460 6458
6461 6459 .. note::
6462 6460
6463 6461 :hg:`status` may appear to disagree with diff if permissions have
6464 6462 changed or a merge has occurred. The standard diff format does
6465 6463 not report permission changes and diff only reports changes
6466 6464 relative to one merge parent.
6467 6465
6468 6466 If one revision is given, it is used as the base revision.
6469 6467 If two revisions are given, the differences between them are
6470 6468 shown. The --change option can also be used as a shortcut to list
6471 6469 the changed files of a revision from its first parent.
6472 6470
6473 6471 The codes used to show the status of files are::
6474 6472
6475 6473 M = modified
6476 6474 A = added
6477 6475 R = removed
6478 6476 C = clean
6479 6477 ! = missing (deleted by non-hg command, but still tracked)
6480 6478 ? = not tracked
6481 6479 I = ignored
6482 6480 = origin of the previous file (with --copies)
6483 6481
6484 6482 .. container:: verbose
6485 6483
6486 6484 Examples:
6487 6485
6488 6486 - show changes in the working directory relative to a
6489 6487 changeset::
6490 6488
6491 6489 hg status --rev 9353
6492 6490
6493 6491 - show changes in the working directory relative to the
6494 6492 current directory (see :hg:`help patterns` for more information)::
6495 6493
6496 6494 hg status re:
6497 6495
6498 6496 - show all changes including copies in an existing changeset::
6499 6497
6500 6498 hg status --copies --change 9353
6501 6499
6502 6500 - get a NUL separated list of added files, suitable for xargs::
6503 6501
6504 6502 hg status -an0
6505 6503
6506 6504 Returns 0 on success.
6507 6505 """
6508 6506
6509 6507 revs = opts.get('rev')
6510 6508 change = opts.get('change')
6511 6509
6512 6510 if revs and change:
6513 6511 msg = _('cannot specify --rev and --change at the same time')
6514 6512 raise error.Abort(msg)
6515 6513 elif change:
6516 6514 node2 = scmutil.revsingle(repo, change, None).node()
6517 6515 node1 = repo[node2].p1().node()
6518 6516 else:
6519 6517 node1, node2 = scmutil.revpair(repo, revs)
6520 6518
6521 6519 if pats:
6522 6520 cwd = repo.getcwd()
6523 6521 else:
6524 6522 cwd = ''
6525 6523
6526 6524 if opts.get('print0'):
6527 6525 end = '\0'
6528 6526 else:
6529 6527 end = '\n'
6530 6528 copy = {}
6531 6529 states = 'modified added removed deleted unknown ignored clean'.split()
6532 6530 show = [k for k in states if opts.get(k)]
6533 6531 if opts.get('all'):
6534 6532 show += ui.quiet and (states[:4] + ['clean']) or states
6535 6533 if not show:
6536 6534 if ui.quiet:
6537 6535 show = states[:4]
6538 6536 else:
6539 6537 show = states[:5]
6540 6538
6541 6539 m = scmutil.match(repo[node2], pats, opts)
6542 6540 stat = repo.status(node1, node2, m,
6543 6541 'ignored' in show, 'clean' in show, 'unknown' in show,
6544 6542 opts.get('subrepos'))
6545 6543 changestates = zip(states, 'MAR!?IC', stat)
6546 6544
6547 6545 if (opts.get('all') or opts.get('copies')
6548 6546 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
6549 6547 copy = copies.pathcopies(repo[node1], repo[node2], m)
6550 6548
6551 6549 fm = ui.formatter('status', opts)
6552 6550 fmt = '%s' + end
6553 6551 showchar = not opts.get('no_status')
6554 6552
6555 6553 for state, char, files in changestates:
6556 6554 if state in show:
6557 6555 label = 'status.' + state
6558 6556 for f in files:
6559 6557 fm.startitem()
6560 6558 fm.condwrite(showchar, 'status', '%s ', char, label=label)
6561 6559 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
6562 6560 if f in copy:
6563 6561 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
6564 6562 label='status.copied')
6565 6563 fm.end()
6566 6564
6567 6565 @command('^summary|sum',
6568 6566 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
6569 6567 def summary(ui, repo, **opts):
6570 6568 """summarize working directory state
6571 6569
6572 6570 This generates a brief summary of the working directory state,
6573 6571 including parents, branch, commit status, phase and available updates.
6574 6572
6575 6573 With the --remote option, this will check the default paths for
6576 6574 incoming and outgoing changes. This can be time-consuming.
6577 6575
6578 6576 Returns 0 on success.
6579 6577 """
6580 6578
6581 6579 ctx = repo[None]
6582 6580 parents = ctx.parents()
6583 6581 pnode = parents[0].node()
6584 6582 marks = []
6585 6583
6586 6584 ms = None
6587 6585 try:
6588 6586 ms = mergemod.mergestate.read(repo)
6589 6587 except error.UnsupportedMergeRecords as e:
6590 6588 s = ' '.join(e.recordtypes)
6591 6589 ui.warn(
6592 6590 _('warning: merge state has unsupported record types: %s\n') % s)
6593 6591 unresolved = 0
6594 6592 else:
6595 6593 unresolved = [f for f in ms if ms[f] == 'u']
6596 6594
6597 6595 for p in parents:
6598 6596 # label with log.changeset (instead of log.parent) since this
6599 6597 # shows a working directory parent *changeset*:
6600 6598 # i18n: column positioning for "hg summary"
6601 6599 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
6602 6600 label='log.changeset changeset.%s' % p.phasestr())
6603 6601 ui.write(' '.join(p.tags()), label='log.tag')
6604 6602 if p.bookmarks():
6605 6603 marks.extend(p.bookmarks())
6606 6604 if p.rev() == -1:
6607 6605 if not len(repo):
6608 6606 ui.write(_(' (empty repository)'))
6609 6607 else:
6610 6608 ui.write(_(' (no revision checked out)'))
6611 6609 ui.write('\n')
6612 6610 if p.description():
6613 6611 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
6614 6612 label='log.summary')
6615 6613
6616 6614 branch = ctx.branch()
6617 6615 bheads = repo.branchheads(branch)
6618 6616 # i18n: column positioning for "hg summary"
6619 6617 m = _('branch: %s\n') % branch
6620 6618 if branch != 'default':
6621 6619 ui.write(m, label='log.branch')
6622 6620 else:
6623 6621 ui.status(m, label='log.branch')
6624 6622
6625 6623 if marks:
6626 6624 active = repo._activebookmark
6627 6625 # i18n: column positioning for "hg summary"
6628 6626 ui.write(_('bookmarks:'), label='log.bookmark')
6629 6627 if active is not None:
6630 6628 if active in marks:
6631 6629 ui.write(' *' + active, label=activebookmarklabel)
6632 6630 marks.remove(active)
6633 6631 else:
6634 6632 ui.write(' [%s]' % active, label=activebookmarklabel)
6635 6633 for m in marks:
6636 6634 ui.write(' ' + m, label='log.bookmark')
6637 6635 ui.write('\n', label='log.bookmark')
6638 6636
6639 6637 status = repo.status(unknown=True)
6640 6638
6641 6639 c = repo.dirstate.copies()
6642 6640 copied, renamed = [], []
6643 6641 for d, s in c.iteritems():
6644 6642 if s in status.removed:
6645 6643 status.removed.remove(s)
6646 6644 renamed.append(d)
6647 6645 else:
6648 6646 copied.append(d)
6649 6647 if d in status.added:
6650 6648 status.added.remove(d)
6651 6649
6652 6650 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6653 6651
6654 6652 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
6655 6653 (ui.label(_('%d added'), 'status.added'), status.added),
6656 6654 (ui.label(_('%d removed'), 'status.removed'), status.removed),
6657 6655 (ui.label(_('%d renamed'), 'status.copied'), renamed),
6658 6656 (ui.label(_('%d copied'), 'status.copied'), copied),
6659 6657 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
6660 6658 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
6661 6659 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
6662 6660 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
6663 6661 t = []
6664 6662 for l, s in labels:
6665 6663 if s:
6666 6664 t.append(l % len(s))
6667 6665
6668 6666 t = ', '.join(t)
6669 6667 cleanworkdir = False
6670 6668
6671 6669 if repo.vfs.exists('graftstate'):
6672 6670 t += _(' (graft in progress)')
6673 6671 if repo.vfs.exists('updatestate'):
6674 6672 t += _(' (interrupted update)')
6675 6673 elif len(parents) > 1:
6676 6674 t += _(' (merge)')
6677 6675 elif branch != parents[0].branch():
6678 6676 t += _(' (new branch)')
6679 6677 elif (parents[0].closesbranch() and
6680 6678 pnode in repo.branchheads(branch, closed=True)):
6681 6679 t += _(' (head closed)')
6682 6680 elif not (status.modified or status.added or status.removed or renamed or
6683 6681 copied or subs):
6684 6682 t += _(' (clean)')
6685 6683 cleanworkdir = True
6686 6684 elif pnode not in bheads:
6687 6685 t += _(' (new branch head)')
6688 6686
6689 6687 if parents:
6690 6688 pendingphase = max(p.phase() for p in parents)
6691 6689 else:
6692 6690 pendingphase = phases.public
6693 6691
6694 6692 if pendingphase > phases.newcommitphase(ui):
6695 6693 t += ' (%s)' % phases.phasenames[pendingphase]
6696 6694
6697 6695 if cleanworkdir:
6698 6696 # i18n: column positioning for "hg summary"
6699 6697 ui.status(_('commit: %s\n') % t.strip())
6700 6698 else:
6701 6699 # i18n: column positioning for "hg summary"
6702 6700 ui.write(_('commit: %s\n') % t.strip())
6703 6701
6704 6702 # all ancestors of branch heads - all ancestors of parent = new csets
6705 6703 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
6706 6704 bheads))
6707 6705
6708 6706 if new == 0:
6709 6707 # i18n: column positioning for "hg summary"
6710 6708 ui.status(_('update: (current)\n'))
6711 6709 elif pnode not in bheads:
6712 6710 # i18n: column positioning for "hg summary"
6713 6711 ui.write(_('update: %d new changesets (update)\n') % new)
6714 6712 else:
6715 6713 # i18n: column positioning for "hg summary"
6716 6714 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
6717 6715 (new, len(bheads)))
6718 6716
6719 6717 t = []
6720 6718 draft = len(repo.revs('draft()'))
6721 6719 if draft:
6722 6720 t.append(_('%d draft') % draft)
6723 6721 secret = len(repo.revs('secret()'))
6724 6722 if secret:
6725 6723 t.append(_('%d secret') % secret)
6726 6724
6727 6725 if draft or secret:
6728 6726 ui.status(_('phases: %s\n') % ', '.join(t))
6729 6727
6730 6728 if obsolete.isenabled(repo, obsolete.createmarkersopt):
6731 6729 for trouble in ("unstable", "divergent", "bumped"):
6732 6730 numtrouble = len(repo.revs(trouble + "()"))
6733 6731 # We write all the possibilities to ease translation
6734 6732 troublemsg = {
6735 6733 "unstable": _("unstable: %d changesets"),
6736 6734 "divergent": _("divergent: %d changesets"),
6737 6735 "bumped": _("bumped: %d changesets"),
6738 6736 }
6739 6737 if numtrouble > 0:
6740 6738 ui.status(troublemsg[trouble] % numtrouble + "\n")
6741 6739
6742 6740 cmdutil.summaryhooks(ui, repo)
6743 6741
6744 6742 if opts.get('remote'):
6745 6743 needsincoming, needsoutgoing = True, True
6746 6744 else:
6747 6745 needsincoming, needsoutgoing = False, False
6748 6746 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
6749 6747 if i:
6750 6748 needsincoming = True
6751 6749 if o:
6752 6750 needsoutgoing = True
6753 6751 if not needsincoming and not needsoutgoing:
6754 6752 return
6755 6753
6756 6754 def getincoming():
6757 6755 source, branches = hg.parseurl(ui.expandpath('default'))
6758 6756 sbranch = branches[0]
6759 6757 try:
6760 6758 other = hg.peer(repo, {}, source)
6761 6759 except error.RepoError:
6762 6760 if opts.get('remote'):
6763 6761 raise
6764 6762 return source, sbranch, None, None, None
6765 6763 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6766 6764 if revs:
6767 6765 revs = [other.lookup(rev) for rev in revs]
6768 6766 ui.debug('comparing with %s\n' % util.hidepassword(source))
6769 6767 repo.ui.pushbuffer()
6770 6768 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6771 6769 repo.ui.popbuffer()
6772 6770 return source, sbranch, other, commoninc, commoninc[1]
6773 6771
6774 6772 if needsincoming:
6775 6773 source, sbranch, sother, commoninc, incoming = getincoming()
6776 6774 else:
6777 6775 source = sbranch = sother = commoninc = incoming = None
6778 6776
6779 6777 def getoutgoing():
6780 6778 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
6781 6779 dbranch = branches[0]
6782 6780 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
6783 6781 if source != dest:
6784 6782 try:
6785 6783 dother = hg.peer(repo, {}, dest)
6786 6784 except error.RepoError:
6787 6785 if opts.get('remote'):
6788 6786 raise
6789 6787 return dest, dbranch, None, None
6790 6788 ui.debug('comparing with %s\n' % util.hidepassword(dest))
6791 6789 elif sother is None:
6792 6790 # there is no explicit destination peer, but source one is invalid
6793 6791 return dest, dbranch, None, None
6794 6792 else:
6795 6793 dother = sother
6796 6794 if (source != dest or (sbranch is not None and sbranch != dbranch)):
6797 6795 common = None
6798 6796 else:
6799 6797 common = commoninc
6800 6798 if revs:
6801 6799 revs = [repo.lookup(rev) for rev in revs]
6802 6800 repo.ui.pushbuffer()
6803 6801 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
6804 6802 commoninc=common)
6805 6803 repo.ui.popbuffer()
6806 6804 return dest, dbranch, dother, outgoing
6807 6805
6808 6806 if needsoutgoing:
6809 6807 dest, dbranch, dother, outgoing = getoutgoing()
6810 6808 else:
6811 6809 dest = dbranch = dother = outgoing = None
6812 6810
6813 6811 if opts.get('remote'):
6814 6812 t = []
6815 6813 if incoming:
6816 6814 t.append(_('1 or more incoming'))
6817 6815 o = outgoing.missing
6818 6816 if o:
6819 6817 t.append(_('%d outgoing') % len(o))
6820 6818 other = dother or sother
6821 6819 if 'bookmarks' in other.listkeys('namespaces'):
6822 6820 counts = bookmarks.summary(repo, other)
6823 6821 if counts[0] > 0:
6824 6822 t.append(_('%d incoming bookmarks') % counts[0])
6825 6823 if counts[1] > 0:
6826 6824 t.append(_('%d outgoing bookmarks') % counts[1])
6827 6825
6828 6826 if t:
6829 6827 # i18n: column positioning for "hg summary"
6830 6828 ui.write(_('remote: %s\n') % (', '.join(t)))
6831 6829 else:
6832 6830 # i18n: column positioning for "hg summary"
6833 6831 ui.status(_('remote: (synced)\n'))
6834 6832
6835 6833 cmdutil.summaryremotehooks(ui, repo, opts,
6836 6834 ((source, sbranch, sother, commoninc),
6837 6835 (dest, dbranch, dother, outgoing)))
6838 6836
6839 6837 @command('tag',
6840 6838 [('f', 'force', None, _('force tag')),
6841 6839 ('l', 'local', None, _('make the tag local')),
6842 6840 ('r', 'rev', '', _('revision to tag'), _('REV')),
6843 6841 ('', 'remove', None, _('remove a tag')),
6844 6842 # -l/--local is already there, commitopts cannot be used
6845 6843 ('e', 'edit', None, _('invoke editor on commit messages')),
6846 6844 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6847 6845 ] + commitopts2,
6848 6846 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6849 6847 def tag(ui, repo, name1, *names, **opts):
6850 6848 """add one or more tags for the current or given revision
6851 6849
6852 6850 Name a particular revision using <name>.
6853 6851
6854 6852 Tags are used to name particular revisions of the repository and are
6855 6853 very useful to compare different revisions, to go back to significant
6856 6854 earlier versions or to mark branch points as releases, etc. Changing
6857 6855 an existing tag is normally disallowed; use -f/--force to override.
6858 6856
6859 6857 If no revision is given, the parent of the working directory is
6860 6858 used.
6861 6859
6862 6860 To facilitate version control, distribution, and merging of tags,
6863 6861 they are stored as a file named ".hgtags" which is managed similarly
6864 6862 to other project files and can be hand-edited if necessary. This
6865 6863 also means that tagging creates a new commit. The file
6866 6864 ".hg/localtags" is used for local tags (not shared among
6867 6865 repositories).
6868 6866
6869 6867 Tag commits are usually made at the head of a branch. If the parent
6870 6868 of the working directory is not a branch head, :hg:`tag` aborts; use
6871 6869 -f/--force to force the tag commit to be based on a non-head
6872 6870 changeset.
6873 6871
6874 6872 See :hg:`help dates` for a list of formats valid for -d/--date.
6875 6873
6876 6874 Since tag names have priority over branch names during revision
6877 6875 lookup, using an existing branch name as a tag name is discouraged.
6878 6876
6879 6877 Returns 0 on success.
6880 6878 """
6881 6879 wlock = lock = None
6882 6880 try:
6883 6881 wlock = repo.wlock()
6884 6882 lock = repo.lock()
6885 6883 rev_ = "."
6886 6884 names = [t.strip() for t in (name1,) + names]
6887 6885 if len(names) != len(set(names)):
6888 6886 raise error.Abort(_('tag names must be unique'))
6889 6887 for n in names:
6890 6888 scmutil.checknewlabel(repo, n, 'tag')
6891 6889 if not n:
6892 6890 raise error.Abort(_('tag names cannot consist entirely of '
6893 6891 'whitespace'))
6894 6892 if opts.get('rev') and opts.get('remove'):
6895 6893 raise error.Abort(_("--rev and --remove are incompatible"))
6896 6894 if opts.get('rev'):
6897 6895 rev_ = opts['rev']
6898 6896 message = opts.get('message')
6899 6897 if opts.get('remove'):
6900 6898 if opts.get('local'):
6901 6899 expectedtype = 'local'
6902 6900 else:
6903 6901 expectedtype = 'global'
6904 6902
6905 6903 for n in names:
6906 6904 if not repo.tagtype(n):
6907 6905 raise error.Abort(_("tag '%s' does not exist") % n)
6908 6906 if repo.tagtype(n) != expectedtype:
6909 6907 if expectedtype == 'global':
6910 6908 raise error.Abort(_("tag '%s' is not a global tag") % n)
6911 6909 else:
6912 6910 raise error.Abort(_("tag '%s' is not a local tag") % n)
6913 6911 rev_ = 'null'
6914 6912 if not message:
6915 6913 # we don't translate commit messages
6916 6914 message = 'Removed tag %s' % ', '.join(names)
6917 6915 elif not opts.get('force'):
6918 6916 for n in names:
6919 6917 if n in repo.tags():
6920 6918 raise error.Abort(_("tag '%s' already exists "
6921 6919 "(use -f to force)") % n)
6922 6920 if not opts.get('local'):
6923 6921 p1, p2 = repo.dirstate.parents()
6924 6922 if p2 != nullid:
6925 6923 raise error.Abort(_('uncommitted merge'))
6926 6924 bheads = repo.branchheads()
6927 6925 if not opts.get('force') and bheads and p1 not in bheads:
6928 6926 raise error.Abort(_('not at a branch head (use -f to force)'))
6929 6927 r = scmutil.revsingle(repo, rev_).node()
6930 6928
6931 6929 if not message:
6932 6930 # we don't translate commit messages
6933 6931 message = ('Added tag %s for changeset %s' %
6934 6932 (', '.join(names), short(r)))
6935 6933
6936 6934 date = opts.get('date')
6937 6935 if date:
6938 6936 date = util.parsedate(date)
6939 6937
6940 6938 if opts.get('remove'):
6941 6939 editform = 'tag.remove'
6942 6940 else:
6943 6941 editform = 'tag.add'
6944 6942 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6945 6943
6946 6944 # don't allow tagging the null rev
6947 6945 if (not opts.get('remove') and
6948 6946 scmutil.revsingle(repo, rev_).rev() == nullrev):
6949 6947 raise error.Abort(_("cannot tag null revision"))
6950 6948
6951 6949 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6952 6950 editor=editor)
6953 6951 finally:
6954 6952 release(lock, wlock)
6955 6953
6956 6954 @command('tags', formatteropts, '')
6957 6955 def tags(ui, repo, **opts):
6958 6956 """list repository tags
6959 6957
6960 6958 This lists both regular and local tags. When the -v/--verbose
6961 6959 switch is used, a third column "local" is printed for local tags.
6962 6960 When the -q/--quiet switch is used, only the tag name is printed.
6963 6961
6964 6962 Returns 0 on success.
6965 6963 """
6966 6964
6967 6965 fm = ui.formatter('tags', opts)
6968 6966 hexfunc = fm.hexfunc
6969 6967 tagtype = ""
6970 6968
6971 6969 for t, n in reversed(repo.tagslist()):
6972 6970 hn = hexfunc(n)
6973 6971 label = 'tags.normal'
6974 6972 tagtype = ''
6975 6973 if repo.tagtype(t) == 'local':
6976 6974 label = 'tags.local'
6977 6975 tagtype = 'local'
6978 6976
6979 6977 fm.startitem()
6980 6978 fm.write('tag', '%s', t, label=label)
6981 6979 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6982 6980 fm.condwrite(not ui.quiet, 'rev node', fmt,
6983 6981 repo.changelog.rev(n), hn, label=label)
6984 6982 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6985 6983 tagtype, label=label)
6986 6984 fm.plain('\n')
6987 6985 fm.end()
6988 6986
6989 6987 @command('tip',
6990 6988 [('p', 'patch', None, _('show patch')),
6991 6989 ('g', 'git', None, _('use git extended diff format')),
6992 6990 ] + templateopts,
6993 6991 _('[-p] [-g]'))
6994 6992 def tip(ui, repo, **opts):
6995 6993 """show the tip revision (DEPRECATED)
6996 6994
6997 6995 The tip revision (usually just called the tip) is the changeset
6998 6996 most recently added to the repository (and therefore the most
6999 6997 recently changed head).
7000 6998
7001 6999 If you have just made a commit, that commit will be the tip. If
7002 7000 you have just pulled changes from another repository, the tip of
7003 7001 that repository becomes the current tip. The "tip" tag is special
7004 7002 and cannot be renamed or assigned to a different changeset.
7005 7003
7006 7004 This command is deprecated, please use :hg:`heads` instead.
7007 7005
7008 7006 Returns 0 on success.
7009 7007 """
7010 7008 displayer = cmdutil.show_changeset(ui, repo, opts)
7011 7009 displayer.show(repo['tip'])
7012 7010 displayer.close()
7013 7011
7014 7012 @command('unbundle',
7015 7013 [('u', 'update', None,
7016 7014 _('update to new branch head if changesets were unbundled'))],
7017 7015 _('[-u] FILE...'))
7018 7016 def unbundle(ui, repo, fname1, *fnames, **opts):
7019 7017 """apply one or more changegroup files
7020 7018
7021 7019 Apply one or more compressed changegroup files generated by the
7022 7020 bundle command.
7023 7021
7024 7022 Returns 0 on success, 1 if an update has unresolved files.
7025 7023 """
7026 7024 fnames = (fname1,) + fnames
7027 7025
7028 7026 with repo.lock():
7029 7027 for fname in fnames:
7030 7028 f = hg.openpath(ui, fname)
7031 7029 gen = exchange.readbundle(ui, f, fname)
7032 7030 if isinstance(gen, bundle2.unbundle20):
7033 7031 tr = repo.transaction('unbundle')
7034 7032 try:
7035 7033 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
7036 7034 url='bundle:' + fname)
7037 7035 tr.close()
7038 7036 except error.BundleUnknownFeatureError as exc:
7039 7037 raise error.Abort(_('%s: unknown bundle feature, %s')
7040 7038 % (fname, exc),
7041 7039 hint=_("see https://mercurial-scm.org/"
7042 7040 "wiki/BundleFeature for more "
7043 7041 "information"))
7044 7042 finally:
7045 7043 if tr:
7046 7044 tr.release()
7047 7045 changes = [r.get('return', 0)
7048 7046 for r in op.records['changegroup']]
7049 7047 modheads = changegroup.combineresults(changes)
7050 7048 elif isinstance(gen, streamclone.streamcloneapplier):
7051 7049 raise error.Abort(
7052 7050 _('packed bundles cannot be applied with '
7053 7051 '"hg unbundle"'),
7054 7052 hint=_('use "hg debugapplystreamclonebundle"'))
7055 7053 else:
7056 7054 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
7057 7055
7058 7056 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
7059 7057
7060 7058 @command('^update|up|checkout|co',
7061 7059 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
7062 7060 ('c', 'check', None,
7063 7061 _('update across branches if no uncommitted changes')),
7064 7062 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
7065 7063 ('r', 'rev', '', _('revision'), _('REV'))
7066 7064 ] + mergetoolopts,
7067 7065 _('[-c] [-C] [-d DATE] [[-r] REV]'))
7068 7066 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
7069 7067 tool=None):
7070 7068 """update working directory (or switch revisions)
7071 7069
7072 7070 Update the repository's working directory to the specified
7073 7071 changeset. If no changeset is specified, update to the tip of the
7074 7072 current named branch and move the active bookmark (see :hg:`help
7075 7073 bookmarks`).
7076 7074
7077 7075 Update sets the working directory's parent revision to the specified
7078 7076 changeset (see :hg:`help parents`).
7079 7077
7080 7078 If the changeset is not a descendant or ancestor of the working
7081 7079 directory's parent, the update is aborted. With the -c/--check
7082 7080 option, the working directory is checked for uncommitted changes; if
7083 7081 none are found, the working directory is updated to the specified
7084 7082 changeset.
7085 7083
7086 7084 .. container:: verbose
7087 7085
7088 7086 The following rules apply when the working directory contains
7089 7087 uncommitted changes:
7090 7088
7091 7089 1. If neither -c/--check nor -C/--clean is specified, and if
7092 7090 the requested changeset is an ancestor or descendant of
7093 7091 the working directory's parent, the uncommitted changes
7094 7092 are merged into the requested changeset and the merged
7095 7093 result is left uncommitted. If the requested changeset is
7096 7094 not an ancestor or descendant (that is, it is on another
7097 7095 branch), the update is aborted and the uncommitted changes
7098 7096 are preserved.
7099 7097
7100 7098 2. With the -c/--check option, the update is aborted and the
7101 7099 uncommitted changes are preserved.
7102 7100
7103 7101 3. With the -C/--clean option, uncommitted changes are discarded and
7104 7102 the working directory is updated to the requested changeset.
7105 7103
7106 7104 To cancel an uncommitted merge (and lose your changes), use
7107 7105 :hg:`update --clean .`.
7108 7106
7109 7107 Use null as the changeset to remove the working directory (like
7110 7108 :hg:`clone -U`).
7111 7109
7112 7110 If you want to revert just one file to an older revision, use
7113 7111 :hg:`revert [-r REV] NAME`.
7114 7112
7115 7113 See :hg:`help dates` for a list of formats valid for -d/--date.
7116 7114
7117 7115 Returns 0 on success, 1 if there are unresolved files.
7118 7116 """
7119 7117 if rev and node:
7120 7118 raise error.Abort(_("please specify just one revision"))
7121 7119
7122 7120 if rev is None or rev == '':
7123 7121 rev = node
7124 7122
7125 7123 if date and rev is not None:
7126 7124 raise error.Abort(_("you can't specify a revision and a date"))
7127 7125
7128 7126 if check and clean:
7129 7127 raise error.Abort(_("cannot specify both -c/--check and -C/--clean"))
7130 7128
7131 7129 with repo.wlock():
7132 7130 cmdutil.clearunfinished(repo)
7133 7131
7134 7132 if date:
7135 7133 rev = cmdutil.finddate(ui, repo, date)
7136 7134
7137 7135 # if we defined a bookmark, we have to remember the original name
7138 7136 brev = rev
7139 7137 rev = scmutil.revsingle(repo, rev, rev).rev()
7140 7138
7141 7139 if check:
7142 7140 cmdutil.bailifchanged(repo, merge=False)
7143 7141
7144 7142 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
7145 7143
7146 7144 return hg.updatetotally(ui, repo, rev, brev, clean=clean, check=check)
7147 7145
7148 7146 @command('verify', [])
7149 7147 def verify(ui, repo):
7150 7148 """verify the integrity of the repository
7151 7149
7152 7150 Verify the integrity of the current repository.
7153 7151
7154 7152 This will perform an extensive check of the repository's
7155 7153 integrity, validating the hashes and checksums of each entry in
7156 7154 the changelog, manifest, and tracked files, as well as the
7157 7155 integrity of their crosslinks and indices.
7158 7156
7159 7157 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7160 7158 for more information about recovery from corruption of the
7161 7159 repository.
7162 7160
7163 7161 Returns 0 on success, 1 if errors are encountered.
7164 7162 """
7165 7163 return hg.verify(repo)
7166 7164
7167 7165 @command('version', [], norepo=True)
7168 7166 def version_(ui):
7169 7167 """output version and copyright information"""
7170 7168 ui.write(_("Mercurial Distributed SCM (version %s)\n")
7171 7169 % util.version())
7172 7170 ui.status(_(
7173 7171 "(see https://mercurial-scm.org for more information)\n"
7174 7172 "\nCopyright (C) 2005-2016 Matt Mackall and others\n"
7175 7173 "This is free software; see the source for copying conditions. "
7176 7174 "There is NO\nwarranty; "
7177 7175 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7178 7176 ))
7179 7177
7180 7178 ui.note(_("\nEnabled extensions:\n\n"))
7181 7179 if ui.verbose:
7182 7180 # format names and versions into columns
7183 7181 names = []
7184 7182 vers = []
7185 7183 place = []
7186 7184 for name, module in extensions.extensions():
7187 7185 names.append(name)
7188 7186 vers.append(extensions.moduleversion(module))
7189 7187 if extensions.ismoduleinternal(module):
7190 7188 place.append(_("internal"))
7191 7189 else:
7192 7190 place.append(_("external"))
7193 7191 if names:
7194 7192 maxnamelen = max(len(n) for n in names)
7195 7193 for i, name in enumerate(names):
7196 7194 ui.write(" %-*s %s %s\n" %
7197 7195 (maxnamelen, name, place[i], vers[i]))
7198 7196
7199 7197 def loadcmdtable(ui, name, cmdtable):
7200 7198 """Load command functions from specified cmdtable
7201 7199 """
7202 7200 overrides = [cmd for cmd in cmdtable if cmd in table]
7203 7201 if overrides:
7204 7202 ui.warn(_("extension '%s' overrides commands: %s\n")
7205 7203 % (name, " ".join(overrides)))
7206 7204 table.update(cmdtable)
General Comments 0
You need to be logged in to leave comments. Login now