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