##// END OF EJS Templates
config: add an experimental option to list all known config...
marmoute -
r48208:b1b31272 default
parent child Browse files
Show More
@@ -1,7947 +1,7964
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Olivia Mackall <olivia@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 errno
11 11 import os
12 12 import re
13 13 import sys
14 14
15 15 from .i18n import _
16 16 from .node import (
17 17 hex,
18 18 nullrev,
19 19 short,
20 20 wdirrev,
21 21 )
22 22 from .pycompat import open
23 23 from . import (
24 24 archival,
25 25 bookmarks,
26 26 bundle2,
27 27 bundlecaches,
28 28 changegroup,
29 29 cmdutil,
30 30 copies,
31 31 debugcommands as debugcommandsmod,
32 32 destutil,
33 33 dirstateguard,
34 34 discovery,
35 35 encoding,
36 36 error,
37 37 exchange,
38 38 extensions,
39 39 filemerge,
40 40 formatter,
41 41 graphmod,
42 42 grep as grepmod,
43 43 hbisect,
44 44 help,
45 45 hg,
46 46 logcmdutil,
47 47 merge as mergemod,
48 48 mergestate as mergestatemod,
49 49 narrowspec,
50 50 obsolete,
51 51 obsutil,
52 52 patch,
53 53 phases,
54 54 pycompat,
55 55 rcutil,
56 56 registrar,
57 57 requirements,
58 58 revsetlang,
59 59 rewriteutil,
60 60 scmutil,
61 61 server,
62 62 shelve as shelvemod,
63 63 state as statemod,
64 64 streamclone,
65 65 tags as tagsmod,
66 66 ui as uimod,
67 67 util,
68 68 verify as verifymod,
69 69 vfs as vfsmod,
70 70 wireprotoserver,
71 71 )
72 72 from .utils import (
73 73 dateutil,
74 74 stringutil,
75 75 urlutil,
76 76 )
77 77
78 78 if pycompat.TYPE_CHECKING:
79 79 from typing import (
80 80 List,
81 81 )
82 82
83 83
84 84 table = {}
85 85 table.update(debugcommandsmod.command._table)
86 86
87 87 command = registrar.command(table)
88 88 INTENT_READONLY = registrar.INTENT_READONLY
89 89
90 90 # common command options
91 91
92 92 globalopts = [
93 93 (
94 94 b'R',
95 95 b'repository',
96 96 b'',
97 97 _(b'repository root directory or name of overlay bundle file'),
98 98 _(b'REPO'),
99 99 ),
100 100 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
101 101 (
102 102 b'y',
103 103 b'noninteractive',
104 104 None,
105 105 _(
106 106 b'do not prompt, automatically pick the first choice for all prompts'
107 107 ),
108 108 ),
109 109 (b'q', b'quiet', None, _(b'suppress output')),
110 110 (b'v', b'verbose', None, _(b'enable additional output')),
111 111 (
112 112 b'',
113 113 b'color',
114 114 b'',
115 115 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
116 116 # and should not be translated
117 117 _(b"when to colorize (boolean, always, auto, never, or debug)"),
118 118 _(b'TYPE'),
119 119 ),
120 120 (
121 121 b'',
122 122 b'config',
123 123 [],
124 124 _(b'set/override config option (use \'section.name=value\')'),
125 125 _(b'CONFIG'),
126 126 ),
127 127 (b'', b'debug', None, _(b'enable debugging output')),
128 128 (b'', b'debugger', None, _(b'start debugger')),
129 129 (
130 130 b'',
131 131 b'encoding',
132 132 encoding.encoding,
133 133 _(b'set the charset encoding'),
134 134 _(b'ENCODE'),
135 135 ),
136 136 (
137 137 b'',
138 138 b'encodingmode',
139 139 encoding.encodingmode,
140 140 _(b'set the charset encoding mode'),
141 141 _(b'MODE'),
142 142 ),
143 143 (b'', b'traceback', None, _(b'always print a traceback on exception')),
144 144 (b'', b'time', None, _(b'time how long the command takes')),
145 145 (b'', b'profile', None, _(b'print command execution profile')),
146 146 (b'', b'version', None, _(b'output version information and exit')),
147 147 (b'h', b'help', None, _(b'display help and exit')),
148 148 (b'', b'hidden', False, _(b'consider hidden changesets')),
149 149 (
150 150 b'',
151 151 b'pager',
152 152 b'auto',
153 153 _(b"when to paginate (boolean, always, auto, or never)"),
154 154 _(b'TYPE'),
155 155 ),
156 156 ]
157 157
158 158 dryrunopts = cmdutil.dryrunopts
159 159 remoteopts = cmdutil.remoteopts
160 160 walkopts = cmdutil.walkopts
161 161 commitopts = cmdutil.commitopts
162 162 commitopts2 = cmdutil.commitopts2
163 163 commitopts3 = cmdutil.commitopts3
164 164 formatteropts = cmdutil.formatteropts
165 165 templateopts = cmdutil.templateopts
166 166 logopts = cmdutil.logopts
167 167 diffopts = cmdutil.diffopts
168 168 diffwsopts = cmdutil.diffwsopts
169 169 diffopts2 = cmdutil.diffopts2
170 170 mergetoolopts = cmdutil.mergetoolopts
171 171 similarityopts = cmdutil.similarityopts
172 172 subrepoopts = cmdutil.subrepoopts
173 173 debugrevlogopts = cmdutil.debugrevlogopts
174 174
175 175 # Commands start here, listed alphabetically
176 176
177 177
178 178 @command(
179 179 b'abort',
180 180 dryrunopts,
181 181 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
182 182 helpbasic=True,
183 183 )
184 184 def abort(ui, repo, **opts):
185 185 """abort an unfinished operation (EXPERIMENTAL)
186 186
187 187 Aborts a multistep operation like graft, histedit, rebase, merge,
188 188 and unshelve if they are in an unfinished state.
189 189
190 190 use --dry-run/-n to dry run the command.
191 191 """
192 192 dryrun = opts.get('dry_run')
193 193 abortstate = cmdutil.getunfinishedstate(repo)
194 194 if not abortstate:
195 195 raise error.StateError(_(b'no operation in progress'))
196 196 if not abortstate.abortfunc:
197 197 raise error.InputError(
198 198 (
199 199 _(b"%s in progress but does not support 'hg abort'")
200 200 % (abortstate._opname)
201 201 ),
202 202 hint=abortstate.hint(),
203 203 )
204 204 if dryrun:
205 205 ui.status(
206 206 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
207 207 )
208 208 return
209 209 return abortstate.abortfunc(ui, repo)
210 210
211 211
212 212 @command(
213 213 b'add',
214 214 walkopts + subrepoopts + dryrunopts,
215 215 _(b'[OPTION]... [FILE]...'),
216 216 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
217 217 helpbasic=True,
218 218 inferrepo=True,
219 219 )
220 220 def add(ui, repo, *pats, **opts):
221 221 """add the specified files on the next commit
222 222
223 223 Schedule files to be version controlled and added to the
224 224 repository.
225 225
226 226 The files will be added to the repository at the next commit. To
227 227 undo an add before that, see :hg:`forget`.
228 228
229 229 If no names are given, add all files to the repository (except
230 230 files matching ``.hgignore``).
231 231
232 232 .. container:: verbose
233 233
234 234 Examples:
235 235
236 236 - New (unknown) files are added
237 237 automatically by :hg:`add`::
238 238
239 239 $ ls
240 240 foo.c
241 241 $ hg status
242 242 ? foo.c
243 243 $ hg add
244 244 adding foo.c
245 245 $ hg status
246 246 A foo.c
247 247
248 248 - Specific files to be added can be specified::
249 249
250 250 $ ls
251 251 bar.c foo.c
252 252 $ hg status
253 253 ? bar.c
254 254 ? foo.c
255 255 $ hg add bar.c
256 256 $ hg status
257 257 A bar.c
258 258 ? foo.c
259 259
260 260 Returns 0 if all files are successfully added.
261 261 """
262 262
263 263 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
264 264 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
265 265 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
266 266 return rejected and 1 or 0
267 267
268 268
269 269 @command(
270 270 b'addremove',
271 271 similarityopts + subrepoopts + walkopts + dryrunopts,
272 272 _(b'[OPTION]... [FILE]...'),
273 273 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
274 274 inferrepo=True,
275 275 )
276 276 def addremove(ui, repo, *pats, **opts):
277 277 """add all new files, delete all missing files
278 278
279 279 Add all new files and remove all missing files from the
280 280 repository.
281 281
282 282 Unless names are given, new files are ignored if they match any of
283 283 the patterns in ``.hgignore``. As with add, these changes take
284 284 effect at the next commit.
285 285
286 286 Use the -s/--similarity option to detect renamed files. This
287 287 option takes a percentage between 0 (disabled) and 100 (files must
288 288 be identical) as its parameter. With a parameter greater than 0,
289 289 this compares every removed file with every added file and records
290 290 those similar enough as renames. Detecting renamed files this way
291 291 can be expensive. After using this option, :hg:`status -C` can be
292 292 used to check which files were identified as moved or renamed. If
293 293 not specified, -s/--similarity defaults to 100 and only renames of
294 294 identical files are detected.
295 295
296 296 .. container:: verbose
297 297
298 298 Examples:
299 299
300 300 - A number of files (bar.c and foo.c) are new,
301 301 while foobar.c has been removed (without using :hg:`remove`)
302 302 from the repository::
303 303
304 304 $ ls
305 305 bar.c foo.c
306 306 $ hg status
307 307 ! foobar.c
308 308 ? bar.c
309 309 ? foo.c
310 310 $ hg addremove
311 311 adding bar.c
312 312 adding foo.c
313 313 removing foobar.c
314 314 $ hg status
315 315 A bar.c
316 316 A foo.c
317 317 R foobar.c
318 318
319 319 - A file foobar.c was moved to foo.c without using :hg:`rename`.
320 320 Afterwards, it was edited slightly::
321 321
322 322 $ ls
323 323 foo.c
324 324 $ hg status
325 325 ! foobar.c
326 326 ? foo.c
327 327 $ hg addremove --similarity 90
328 328 removing foobar.c
329 329 adding foo.c
330 330 recording removal of foobar.c as rename to foo.c (94% similar)
331 331 $ hg status -C
332 332 A foo.c
333 333 foobar.c
334 334 R foobar.c
335 335
336 336 Returns 0 if all files are successfully added.
337 337 """
338 338 opts = pycompat.byteskwargs(opts)
339 339 if not opts.get(b'similarity'):
340 340 opts[b'similarity'] = b'100'
341 341 matcher = scmutil.match(repo[None], pats, opts)
342 342 relative = scmutil.anypats(pats, opts)
343 343 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
344 344 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
345 345
346 346
347 347 @command(
348 348 b'annotate|blame',
349 349 [
350 350 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
351 351 (
352 352 b'',
353 353 b'follow',
354 354 None,
355 355 _(b'follow copies/renames and list the filename (DEPRECATED)'),
356 356 ),
357 357 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
358 358 (b'a', b'text', None, _(b'treat all files as text')),
359 359 (b'u', b'user', None, _(b'list the author (long with -v)')),
360 360 (b'f', b'file', None, _(b'list the filename')),
361 361 (b'd', b'date', None, _(b'list the date (short with -q)')),
362 362 (b'n', b'number', None, _(b'list the revision number (default)')),
363 363 (b'c', b'changeset', None, _(b'list the changeset')),
364 364 (
365 365 b'l',
366 366 b'line-number',
367 367 None,
368 368 _(b'show line number at the first appearance'),
369 369 ),
370 370 (
371 371 b'',
372 372 b'skip',
373 373 [],
374 374 _(b'revset to not display (EXPERIMENTAL)'),
375 375 _(b'REV'),
376 376 ),
377 377 ]
378 378 + diffwsopts
379 379 + walkopts
380 380 + formatteropts,
381 381 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
382 382 helpcategory=command.CATEGORY_FILE_CONTENTS,
383 383 helpbasic=True,
384 384 inferrepo=True,
385 385 )
386 386 def annotate(ui, repo, *pats, **opts):
387 387 """show changeset information by line for each file
388 388
389 389 List changes in files, showing the revision id responsible for
390 390 each line.
391 391
392 392 This command is useful for discovering when a change was made and
393 393 by whom.
394 394
395 395 If you include --file, --user, or --date, the revision number is
396 396 suppressed unless you also include --number.
397 397
398 398 Without the -a/--text option, annotate will avoid processing files
399 399 it detects as binary. With -a, annotate will annotate the file
400 400 anyway, although the results will probably be neither useful
401 401 nor desirable.
402 402
403 403 .. container:: verbose
404 404
405 405 Template:
406 406
407 407 The following keywords are supported in addition to the common template
408 408 keywords and functions. See also :hg:`help templates`.
409 409
410 410 :lines: List of lines with annotation data.
411 411 :path: String. Repository-absolute path of the specified file.
412 412
413 413 And each entry of ``{lines}`` provides the following sub-keywords in
414 414 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
415 415
416 416 :line: String. Line content.
417 417 :lineno: Integer. Line number at that revision.
418 418 :path: String. Repository-absolute path of the file at that revision.
419 419
420 420 See :hg:`help templates.operators` for the list expansion syntax.
421 421
422 422 Returns 0 on success.
423 423 """
424 424 opts = pycompat.byteskwargs(opts)
425 425 if not pats:
426 426 raise error.InputError(
427 427 _(b'at least one filename or pattern is required')
428 428 )
429 429
430 430 if opts.get(b'follow'):
431 431 # --follow is deprecated and now just an alias for -f/--file
432 432 # to mimic the behavior of Mercurial before version 1.5
433 433 opts[b'file'] = True
434 434
435 435 if (
436 436 not opts.get(b'user')
437 437 and not opts.get(b'changeset')
438 438 and not opts.get(b'date')
439 439 and not opts.get(b'file')
440 440 ):
441 441 opts[b'number'] = True
442 442
443 443 linenumber = opts.get(b'line_number') is not None
444 444 if (
445 445 linenumber
446 446 and (not opts.get(b'changeset'))
447 447 and (not opts.get(b'number'))
448 448 ):
449 449 raise error.InputError(_(b'at least one of -n/-c is required for -l'))
450 450
451 451 rev = opts.get(b'rev')
452 452 if rev:
453 453 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
454 454 ctx = scmutil.revsingle(repo, rev)
455 455
456 456 ui.pager(b'annotate')
457 457 rootfm = ui.formatter(b'annotate', opts)
458 458 if ui.debugflag:
459 459 shorthex = pycompat.identity
460 460 else:
461 461
462 462 def shorthex(h):
463 463 return h[:12]
464 464
465 465 if ui.quiet:
466 466 datefunc = dateutil.shortdate
467 467 else:
468 468 datefunc = dateutil.datestr
469 469 if ctx.rev() is None:
470 470 if opts.get(b'changeset'):
471 471 # omit "+" suffix which is appended to node hex
472 472 def formatrev(rev):
473 473 if rev == wdirrev:
474 474 return b'%d' % ctx.p1().rev()
475 475 else:
476 476 return b'%d' % rev
477 477
478 478 else:
479 479
480 480 def formatrev(rev):
481 481 if rev == wdirrev:
482 482 return b'%d+' % ctx.p1().rev()
483 483 else:
484 484 return b'%d ' % rev
485 485
486 486 def formathex(h):
487 487 if h == repo.nodeconstants.wdirhex:
488 488 return b'%s+' % shorthex(hex(ctx.p1().node()))
489 489 else:
490 490 return b'%s ' % shorthex(h)
491 491
492 492 else:
493 493 formatrev = b'%d'.__mod__
494 494 formathex = shorthex
495 495
496 496 opmap = [
497 497 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
498 498 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
499 499 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
500 500 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
501 501 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
502 502 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
503 503 ]
504 504 opnamemap = {
505 505 b'rev': b'number',
506 506 b'node': b'changeset',
507 507 b'path': b'file',
508 508 b'lineno': b'line_number',
509 509 }
510 510
511 511 if rootfm.isplain():
512 512
513 513 def makefunc(get, fmt):
514 514 return lambda x: fmt(get(x))
515 515
516 516 else:
517 517
518 518 def makefunc(get, fmt):
519 519 return get
520 520
521 521 datahint = rootfm.datahint()
522 522 funcmap = [
523 523 (makefunc(get, fmt), sep)
524 524 for fn, sep, get, fmt in opmap
525 525 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
526 526 ]
527 527 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
528 528 fields = b' '.join(
529 529 fn
530 530 for fn, sep, get, fmt in opmap
531 531 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
532 532 )
533 533
534 534 def bad(x, y):
535 535 raise error.Abort(b"%s: %s" % (x, y))
536 536
537 537 m = scmutil.match(ctx, pats, opts, badfn=bad)
538 538
539 539 follow = not opts.get(b'no_follow')
540 540 diffopts = patch.difffeatureopts(
541 541 ui, opts, section=b'annotate', whitespace=True
542 542 )
543 543 skiprevs = opts.get(b'skip')
544 544 if skiprevs:
545 545 skiprevs = scmutil.revrange(repo, skiprevs)
546 546
547 547 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
548 548 for abs in ctx.walk(m):
549 549 fctx = ctx[abs]
550 550 rootfm.startitem()
551 551 rootfm.data(path=abs)
552 552 if not opts.get(b'text') and fctx.isbinary():
553 553 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
554 554 continue
555 555
556 556 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
557 557 lines = fctx.annotate(
558 558 follow=follow, skiprevs=skiprevs, diffopts=diffopts
559 559 )
560 560 if not lines:
561 561 fm.end()
562 562 continue
563 563 formats = []
564 564 pieces = []
565 565
566 566 for f, sep in funcmap:
567 567 l = [f(n) for n in lines]
568 568 if fm.isplain():
569 569 sizes = [encoding.colwidth(x) for x in l]
570 570 ml = max(sizes)
571 571 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
572 572 else:
573 573 formats.append([b'%s'] * len(l))
574 574 pieces.append(l)
575 575
576 576 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
577 577 fm.startitem()
578 578 fm.context(fctx=n.fctx)
579 579 fm.write(fields, b"".join(f), *p)
580 580 if n.skip:
581 581 fmt = b"* %s"
582 582 else:
583 583 fmt = b": %s"
584 584 fm.write(b'line', fmt, n.text)
585 585
586 586 if not lines[-1].text.endswith(b'\n'):
587 587 fm.plain(b'\n')
588 588 fm.end()
589 589
590 590 rootfm.end()
591 591
592 592
593 593 @command(
594 594 b'archive',
595 595 [
596 596 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
597 597 (
598 598 b'p',
599 599 b'prefix',
600 600 b'',
601 601 _(b'directory prefix for files in archive'),
602 602 _(b'PREFIX'),
603 603 ),
604 604 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
605 605 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
606 606 ]
607 607 + subrepoopts
608 608 + walkopts,
609 609 _(b'[OPTION]... DEST'),
610 610 helpcategory=command.CATEGORY_IMPORT_EXPORT,
611 611 )
612 612 def archive(ui, repo, dest, **opts):
613 613 """create an unversioned archive of a repository revision
614 614
615 615 By default, the revision used is the parent of the working
616 616 directory; use -r/--rev to specify a different revision.
617 617
618 618 The archive type is automatically detected based on file
619 619 extension (to override, use -t/--type).
620 620
621 621 .. container:: verbose
622 622
623 623 Examples:
624 624
625 625 - create a zip file containing the 1.0 release::
626 626
627 627 hg archive -r 1.0 project-1.0.zip
628 628
629 629 - create a tarball excluding .hg files::
630 630
631 631 hg archive project.tar.gz -X ".hg*"
632 632
633 633 Valid types are:
634 634
635 635 :``files``: a directory full of files (default)
636 636 :``tar``: tar archive, uncompressed
637 637 :``tbz2``: tar archive, compressed using bzip2
638 638 :``tgz``: tar archive, compressed using gzip
639 639 :``txz``: tar archive, compressed using lzma (only in Python 3)
640 640 :``uzip``: zip archive, uncompressed
641 641 :``zip``: zip archive, compressed using deflate
642 642
643 643 The exact name of the destination archive or directory is given
644 644 using a format string; see :hg:`help export` for details.
645 645
646 646 Each member added to an archive file has a directory prefix
647 647 prepended. Use -p/--prefix to specify a format string for the
648 648 prefix. The default is the basename of the archive, with suffixes
649 649 removed.
650 650
651 651 Returns 0 on success.
652 652 """
653 653
654 654 opts = pycompat.byteskwargs(opts)
655 655 rev = opts.get(b'rev')
656 656 if rev:
657 657 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
658 658 ctx = scmutil.revsingle(repo, rev)
659 659 if not ctx:
660 660 raise error.InputError(
661 661 _(b'no working directory: please specify a revision')
662 662 )
663 663 node = ctx.node()
664 664 dest = cmdutil.makefilename(ctx, dest)
665 665 if os.path.realpath(dest) == repo.root:
666 666 raise error.InputError(_(b'repository root cannot be destination'))
667 667
668 668 kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
669 669 prefix = opts.get(b'prefix')
670 670
671 671 if dest == b'-':
672 672 if kind == b'files':
673 673 raise error.InputError(_(b'cannot archive plain files to stdout'))
674 674 dest = cmdutil.makefileobj(ctx, dest)
675 675 if not prefix:
676 676 prefix = os.path.basename(repo.root) + b'-%h'
677 677
678 678 prefix = cmdutil.makefilename(ctx, prefix)
679 679 match = scmutil.match(ctx, [], opts)
680 680 archival.archive(
681 681 repo,
682 682 dest,
683 683 node,
684 684 kind,
685 685 not opts.get(b'no_decode'),
686 686 match,
687 687 prefix,
688 688 subrepos=opts.get(b'subrepos'),
689 689 )
690 690
691 691
692 692 @command(
693 693 b'backout',
694 694 [
695 695 (
696 696 b'',
697 697 b'merge',
698 698 None,
699 699 _(b'merge with old dirstate parent after backout'),
700 700 ),
701 701 (
702 702 b'',
703 703 b'commit',
704 704 None,
705 705 _(b'commit if no conflicts were encountered (DEPRECATED)'),
706 706 ),
707 707 (b'', b'no-commit', None, _(b'do not commit')),
708 708 (
709 709 b'',
710 710 b'parent',
711 711 b'',
712 712 _(b'parent to choose when backing out merge (DEPRECATED)'),
713 713 _(b'REV'),
714 714 ),
715 715 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
716 716 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
717 717 ]
718 718 + mergetoolopts
719 719 + walkopts
720 720 + commitopts
721 721 + commitopts2,
722 722 _(b'[OPTION]... [-r] REV'),
723 723 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
724 724 )
725 725 def backout(ui, repo, node=None, rev=None, **opts):
726 726 """reverse effect of earlier changeset
727 727
728 728 Prepare a new changeset with the effect of REV undone in the
729 729 current working directory. If no conflicts were encountered,
730 730 it will be committed immediately.
731 731
732 732 If REV is the parent of the working directory, then this new changeset
733 733 is committed automatically (unless --no-commit is specified).
734 734
735 735 .. note::
736 736
737 737 :hg:`backout` cannot be used to fix either an unwanted or
738 738 incorrect merge.
739 739
740 740 .. container:: verbose
741 741
742 742 Examples:
743 743
744 744 - Reverse the effect of the parent of the working directory.
745 745 This backout will be committed immediately::
746 746
747 747 hg backout -r .
748 748
749 749 - Reverse the effect of previous bad revision 23::
750 750
751 751 hg backout -r 23
752 752
753 753 - Reverse the effect of previous bad revision 23 and
754 754 leave changes uncommitted::
755 755
756 756 hg backout -r 23 --no-commit
757 757 hg commit -m "Backout revision 23"
758 758
759 759 By default, the pending changeset will have one parent,
760 760 maintaining a linear history. With --merge, the pending
761 761 changeset will instead have two parents: the old parent of the
762 762 working directory and a new child of REV that simply undoes REV.
763 763
764 764 Before version 1.7, the behavior without --merge was equivalent
765 765 to specifying --merge followed by :hg:`update --clean .` to
766 766 cancel the merge and leave the child of REV as a head to be
767 767 merged separately.
768 768
769 769 See :hg:`help dates` for a list of formats valid for -d/--date.
770 770
771 771 See :hg:`help revert` for a way to restore files to the state
772 772 of another revision.
773 773
774 774 Returns 0 on success, 1 if nothing to backout or there are unresolved
775 775 files.
776 776 """
777 777 with repo.wlock(), repo.lock():
778 778 return _dobackout(ui, repo, node, rev, **opts)
779 779
780 780
781 781 def _dobackout(ui, repo, node=None, rev=None, **opts):
782 782 cmdutil.check_incompatible_arguments(opts, 'no_commit', ['commit', 'merge'])
783 783 opts = pycompat.byteskwargs(opts)
784 784
785 785 if rev and node:
786 786 raise error.InputError(_(b"please specify just one revision"))
787 787
788 788 if not rev:
789 789 rev = node
790 790
791 791 if not rev:
792 792 raise error.InputError(_(b"please specify a revision to backout"))
793 793
794 794 date = opts.get(b'date')
795 795 if date:
796 796 opts[b'date'] = dateutil.parsedate(date)
797 797
798 798 cmdutil.checkunfinished(repo)
799 799 cmdutil.bailifchanged(repo)
800 800 ctx = scmutil.revsingle(repo, rev)
801 801 node = ctx.node()
802 802
803 803 op1, op2 = repo.dirstate.parents()
804 804 if not repo.changelog.isancestor(node, op1):
805 805 raise error.InputError(
806 806 _(b'cannot backout change that is not an ancestor')
807 807 )
808 808
809 809 p1, p2 = repo.changelog.parents(node)
810 810 if p1 == repo.nullid:
811 811 raise error.InputError(_(b'cannot backout a change with no parents'))
812 812 if p2 != repo.nullid:
813 813 if not opts.get(b'parent'):
814 814 raise error.InputError(_(b'cannot backout a merge changeset'))
815 815 p = repo.lookup(opts[b'parent'])
816 816 if p not in (p1, p2):
817 817 raise error.InputError(
818 818 _(b'%s is not a parent of %s') % (short(p), short(node))
819 819 )
820 820 parent = p
821 821 else:
822 822 if opts.get(b'parent'):
823 823 raise error.InputError(
824 824 _(b'cannot use --parent on non-merge changeset')
825 825 )
826 826 parent = p1
827 827
828 828 # the backout should appear on the same branch
829 829 branch = repo.dirstate.branch()
830 830 bheads = repo.branchheads(branch)
831 831 rctx = scmutil.revsingle(repo, hex(parent))
832 832 if not opts.get(b'merge') and op1 != node:
833 833 with dirstateguard.dirstateguard(repo, b'backout'):
834 834 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
835 835 with ui.configoverride(overrides, b'backout'):
836 836 stats = mergemod.back_out(ctx, parent=repo[parent])
837 837 repo.setparents(op1, op2)
838 838 hg._showstats(repo, stats)
839 839 if stats.unresolvedcount:
840 840 repo.ui.status(
841 841 _(b"use 'hg resolve' to retry unresolved file merges\n")
842 842 )
843 843 return 1
844 844 else:
845 845 hg.clean(repo, node, show_stats=False)
846 846 repo.dirstate.setbranch(branch)
847 847 cmdutil.revert(ui, repo, rctx)
848 848
849 849 if opts.get(b'no_commit'):
850 850 msg = _(b"changeset %s backed out, don't forget to commit.\n")
851 851 ui.status(msg % short(node))
852 852 return 0
853 853
854 854 def commitfunc(ui, repo, message, match, opts):
855 855 editform = b'backout'
856 856 e = cmdutil.getcommiteditor(
857 857 editform=editform, **pycompat.strkwargs(opts)
858 858 )
859 859 if not message:
860 860 # we don't translate commit messages
861 861 message = b"Backed out changeset %s" % short(node)
862 862 e = cmdutil.getcommiteditor(edit=True, editform=editform)
863 863 return repo.commit(
864 864 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
865 865 )
866 866
867 867 # save to detect changes
868 868 tip = repo.changelog.tip()
869 869
870 870 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
871 871 if not newnode:
872 872 ui.status(_(b"nothing changed\n"))
873 873 return 1
874 874 cmdutil.commitstatus(repo, newnode, branch, bheads, tip)
875 875
876 876 def nice(node):
877 877 return b'%d:%s' % (repo.changelog.rev(node), short(node))
878 878
879 879 ui.status(
880 880 _(b'changeset %s backs out changeset %s\n')
881 881 % (nice(newnode), nice(node))
882 882 )
883 883 if opts.get(b'merge') and op1 != node:
884 884 hg.clean(repo, op1, show_stats=False)
885 885 ui.status(_(b'merging with changeset %s\n') % nice(newnode))
886 886 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
887 887 with ui.configoverride(overrides, b'backout'):
888 888 return hg.merge(repo[b'tip'])
889 889 return 0
890 890
891 891
892 892 @command(
893 893 b'bisect',
894 894 [
895 895 (b'r', b'reset', False, _(b'reset bisect state')),
896 896 (b'g', b'good', False, _(b'mark changeset good')),
897 897 (b'b', b'bad', False, _(b'mark changeset bad')),
898 898 (b's', b'skip', False, _(b'skip testing changeset')),
899 899 (b'e', b'extend', False, _(b'extend the bisect range')),
900 900 (
901 901 b'c',
902 902 b'command',
903 903 b'',
904 904 _(b'use command to check changeset state'),
905 905 _(b'CMD'),
906 906 ),
907 907 (b'U', b'noupdate', False, _(b'do not update to target')),
908 908 ],
909 909 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
910 910 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
911 911 )
912 912 def bisect(
913 913 ui,
914 914 repo,
915 915 positional_1=None,
916 916 positional_2=None,
917 917 command=None,
918 918 reset=None,
919 919 good=None,
920 920 bad=None,
921 921 skip=None,
922 922 extend=None,
923 923 noupdate=None,
924 924 ):
925 925 """subdivision search of changesets
926 926
927 927 This command helps to find changesets which introduce problems. To
928 928 use, mark the earliest changeset you know exhibits the problem as
929 929 bad, then mark the latest changeset which is free from the problem
930 930 as good. Bisect will update your working directory to a revision
931 931 for testing (unless the -U/--noupdate option is specified). Once
932 932 you have performed tests, mark the working directory as good or
933 933 bad, and bisect will either update to another candidate changeset
934 934 or announce that it has found the bad revision.
935 935
936 936 As a shortcut, you can also use the revision argument to mark a
937 937 revision as good or bad without checking it out first.
938 938
939 939 If you supply a command, it will be used for automatic bisection.
940 940 The environment variable HG_NODE will contain the ID of the
941 941 changeset being tested. The exit status of the command will be
942 942 used to mark revisions as good or bad: status 0 means good, 125
943 943 means to skip the revision, 127 (command not found) will abort the
944 944 bisection, and any other non-zero exit status means the revision
945 945 is bad.
946 946
947 947 .. container:: verbose
948 948
949 949 Some examples:
950 950
951 951 - start a bisection with known bad revision 34, and good revision 12::
952 952
953 953 hg bisect --bad 34
954 954 hg bisect --good 12
955 955
956 956 - advance the current bisection by marking current revision as good or
957 957 bad::
958 958
959 959 hg bisect --good
960 960 hg bisect --bad
961 961
962 962 - mark the current revision, or a known revision, to be skipped (e.g. if
963 963 that revision is not usable because of another issue)::
964 964
965 965 hg bisect --skip
966 966 hg bisect --skip 23
967 967
968 968 - skip all revisions that do not touch directories ``foo`` or ``bar``::
969 969
970 970 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
971 971
972 972 - forget the current bisection::
973 973
974 974 hg bisect --reset
975 975
976 976 - use 'make && make tests' to automatically find the first broken
977 977 revision::
978 978
979 979 hg bisect --reset
980 980 hg bisect --bad 34
981 981 hg bisect --good 12
982 982 hg bisect --command "make && make tests"
983 983
984 984 - see all changesets whose states are already known in the current
985 985 bisection::
986 986
987 987 hg log -r "bisect(pruned)"
988 988
989 989 - see the changeset currently being bisected (especially useful
990 990 if running with -U/--noupdate)::
991 991
992 992 hg log -r "bisect(current)"
993 993
994 994 - see all changesets that took part in the current bisection::
995 995
996 996 hg log -r "bisect(range)"
997 997
998 998 - you can even get a nice graph::
999 999
1000 1000 hg log --graph -r "bisect(range)"
1001 1001
1002 1002 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
1003 1003
1004 1004 Returns 0 on success.
1005 1005 """
1006 1006 rev = []
1007 1007 # backward compatibility
1008 1008 if positional_1 in (b"good", b"bad", b"reset", b"init"):
1009 1009 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1010 1010 cmd = positional_1
1011 1011 rev.append(positional_2)
1012 1012 if cmd == b"good":
1013 1013 good = True
1014 1014 elif cmd == b"bad":
1015 1015 bad = True
1016 1016 else:
1017 1017 reset = True
1018 1018 elif positional_2:
1019 1019 raise error.InputError(_(b'incompatible arguments'))
1020 1020 elif positional_1 is not None:
1021 1021 rev.append(positional_1)
1022 1022
1023 1023 incompatibles = {
1024 1024 b'--bad': bad,
1025 1025 b'--command': bool(command),
1026 1026 b'--extend': extend,
1027 1027 b'--good': good,
1028 1028 b'--reset': reset,
1029 1029 b'--skip': skip,
1030 1030 }
1031 1031
1032 1032 enabled = [x for x in incompatibles if incompatibles[x]]
1033 1033
1034 1034 if len(enabled) > 1:
1035 1035 raise error.InputError(
1036 1036 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1037 1037 )
1038 1038
1039 1039 if reset:
1040 1040 hbisect.resetstate(repo)
1041 1041 return
1042 1042
1043 1043 state = hbisect.load_state(repo)
1044 1044
1045 1045 if rev:
1046 1046 nodes = [repo[i].node() for i in scmutil.revrange(repo, rev)]
1047 1047 else:
1048 1048 nodes = [repo.lookup(b'.')]
1049 1049
1050 1050 # update state
1051 1051 if good or bad or skip:
1052 1052 if good:
1053 1053 state[b'good'] += nodes
1054 1054 elif bad:
1055 1055 state[b'bad'] += nodes
1056 1056 elif skip:
1057 1057 state[b'skip'] += nodes
1058 1058 hbisect.save_state(repo, state)
1059 1059 if not (state[b'good'] and state[b'bad']):
1060 1060 return
1061 1061
1062 1062 def mayupdate(repo, node, show_stats=True):
1063 1063 """common used update sequence"""
1064 1064 if noupdate:
1065 1065 return
1066 1066 cmdutil.checkunfinished(repo)
1067 1067 cmdutil.bailifchanged(repo)
1068 1068 return hg.clean(repo, node, show_stats=show_stats)
1069 1069
1070 1070 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1071 1071
1072 1072 if command:
1073 1073 changesets = 1
1074 1074 if noupdate:
1075 1075 try:
1076 1076 node = state[b'current'][0]
1077 1077 except LookupError:
1078 1078 raise error.StateError(
1079 1079 _(
1080 1080 b'current bisect revision is unknown - '
1081 1081 b'start a new bisect to fix'
1082 1082 )
1083 1083 )
1084 1084 else:
1085 1085 node, p2 = repo.dirstate.parents()
1086 1086 if p2 != repo.nullid:
1087 1087 raise error.StateError(_(b'current bisect revision is a merge'))
1088 1088 if rev:
1089 1089 if not nodes:
1090 1090 raise error.Abort(_(b'empty revision set'))
1091 1091 node = repo[nodes[-1]].node()
1092 1092 with hbisect.restore_state(repo, state, node):
1093 1093 while changesets:
1094 1094 # update state
1095 1095 state[b'current'] = [node]
1096 1096 hbisect.save_state(repo, state)
1097 1097 status = ui.system(
1098 1098 command,
1099 1099 environ={b'HG_NODE': hex(node)},
1100 1100 blockedtag=b'bisect_check',
1101 1101 )
1102 1102 if status == 125:
1103 1103 transition = b"skip"
1104 1104 elif status == 0:
1105 1105 transition = b"good"
1106 1106 # status < 0 means process was killed
1107 1107 elif status == 127:
1108 1108 raise error.Abort(_(b"failed to execute %s") % command)
1109 1109 elif status < 0:
1110 1110 raise error.Abort(_(b"%s killed") % command)
1111 1111 else:
1112 1112 transition = b"bad"
1113 1113 state[transition].append(node)
1114 1114 ctx = repo[node]
1115 1115 summary = cmdutil.format_changeset_summary(ui, ctx, b'bisect')
1116 1116 ui.status(_(b'changeset %s: %s\n') % (summary, transition))
1117 1117 hbisect.checkstate(state)
1118 1118 # bisect
1119 1119 nodes, changesets, bgood = hbisect.bisect(repo, state)
1120 1120 # update to next check
1121 1121 node = nodes[0]
1122 1122 mayupdate(repo, node, show_stats=False)
1123 1123 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1124 1124 return
1125 1125
1126 1126 hbisect.checkstate(state)
1127 1127
1128 1128 # actually bisect
1129 1129 nodes, changesets, good = hbisect.bisect(repo, state)
1130 1130 if extend:
1131 1131 if not changesets:
1132 1132 extendctx = hbisect.extendrange(repo, state, nodes, good)
1133 1133 if extendctx is not None:
1134 1134 ui.write(
1135 1135 _(b"Extending search to changeset %s\n")
1136 1136 % cmdutil.format_changeset_summary(ui, extendctx, b'bisect')
1137 1137 )
1138 1138 state[b'current'] = [extendctx.node()]
1139 1139 hbisect.save_state(repo, state)
1140 1140 return mayupdate(repo, extendctx.node())
1141 1141 raise error.StateError(_(b"nothing to extend"))
1142 1142
1143 1143 if changesets == 0:
1144 1144 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1145 1145 else:
1146 1146 assert len(nodes) == 1 # only a single node can be tested next
1147 1147 node = nodes[0]
1148 1148 # compute the approximate number of remaining tests
1149 1149 tests, size = 0, 2
1150 1150 while size <= changesets:
1151 1151 tests, size = tests + 1, size * 2
1152 1152 rev = repo.changelog.rev(node)
1153 1153 summary = cmdutil.format_changeset_summary(ui, repo[rev], b'bisect')
1154 1154 ui.write(
1155 1155 _(
1156 1156 b"Testing changeset %s "
1157 1157 b"(%d changesets remaining, ~%d tests)\n"
1158 1158 )
1159 1159 % (summary, changesets, tests)
1160 1160 )
1161 1161 state[b'current'] = [node]
1162 1162 hbisect.save_state(repo, state)
1163 1163 return mayupdate(repo, node)
1164 1164
1165 1165
1166 1166 @command(
1167 1167 b'bookmarks|bookmark',
1168 1168 [
1169 1169 (b'f', b'force', False, _(b'force')),
1170 1170 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1171 1171 (b'd', b'delete', False, _(b'delete a given bookmark')),
1172 1172 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1173 1173 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1174 1174 (b'l', b'list', False, _(b'list existing bookmarks')),
1175 1175 ]
1176 1176 + formatteropts,
1177 1177 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1178 1178 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1179 1179 )
1180 1180 def bookmark(ui, repo, *names, **opts):
1181 1181 """create a new bookmark or list existing bookmarks
1182 1182
1183 1183 Bookmarks are labels on changesets to help track lines of development.
1184 1184 Bookmarks are unversioned and can be moved, renamed and deleted.
1185 1185 Deleting or moving a bookmark has no effect on the associated changesets.
1186 1186
1187 1187 Creating or updating to a bookmark causes it to be marked as 'active'.
1188 1188 The active bookmark is indicated with a '*'.
1189 1189 When a commit is made, the active bookmark will advance to the new commit.
1190 1190 A plain :hg:`update` will also advance an active bookmark, if possible.
1191 1191 Updating away from a bookmark will cause it to be deactivated.
1192 1192
1193 1193 Bookmarks can be pushed and pulled between repositories (see
1194 1194 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1195 1195 diverged, a new 'divergent bookmark' of the form 'name@path' will
1196 1196 be created. Using :hg:`merge` will resolve the divergence.
1197 1197
1198 1198 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1199 1199 the active bookmark's name.
1200 1200
1201 1201 A bookmark named '@' has the special property that :hg:`clone` will
1202 1202 check it out by default if it exists.
1203 1203
1204 1204 .. container:: verbose
1205 1205
1206 1206 Template:
1207 1207
1208 1208 The following keywords are supported in addition to the common template
1209 1209 keywords and functions such as ``{bookmark}``. See also
1210 1210 :hg:`help templates`.
1211 1211
1212 1212 :active: Boolean. True if the bookmark is active.
1213 1213
1214 1214 Examples:
1215 1215
1216 1216 - create an active bookmark for a new line of development::
1217 1217
1218 1218 hg book new-feature
1219 1219
1220 1220 - create an inactive bookmark as a place marker::
1221 1221
1222 1222 hg book -i reviewed
1223 1223
1224 1224 - create an inactive bookmark on another changeset::
1225 1225
1226 1226 hg book -r .^ tested
1227 1227
1228 1228 - rename bookmark turkey to dinner::
1229 1229
1230 1230 hg book -m turkey dinner
1231 1231
1232 1232 - move the '@' bookmark from another branch::
1233 1233
1234 1234 hg book -f @
1235 1235
1236 1236 - print only the active bookmark name::
1237 1237
1238 1238 hg book -ql .
1239 1239 """
1240 1240 opts = pycompat.byteskwargs(opts)
1241 1241 force = opts.get(b'force')
1242 1242 rev = opts.get(b'rev')
1243 1243 inactive = opts.get(b'inactive') # meaning add/rename to inactive bookmark
1244 1244
1245 1245 action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1246 1246 if action:
1247 1247 cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1248 1248 elif names or rev:
1249 1249 action = b'add'
1250 1250 elif inactive:
1251 1251 action = b'inactive' # meaning deactivate
1252 1252 else:
1253 1253 action = b'list'
1254 1254
1255 1255 cmdutil.check_incompatible_arguments(
1256 1256 opts, b'inactive', [b'delete', b'list']
1257 1257 )
1258 1258 if not names and action in {b'add', b'delete'}:
1259 1259 raise error.InputError(_(b"bookmark name required"))
1260 1260
1261 1261 if action in {b'add', b'delete', b'rename', b'inactive'}:
1262 1262 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1263 1263 if action == b'delete':
1264 1264 names = pycompat.maplist(repo._bookmarks.expandname, names)
1265 1265 bookmarks.delete(repo, tr, names)
1266 1266 elif action == b'rename':
1267 1267 if not names:
1268 1268 raise error.InputError(_(b"new bookmark name required"))
1269 1269 elif len(names) > 1:
1270 1270 raise error.InputError(
1271 1271 _(b"only one new bookmark name allowed")
1272 1272 )
1273 1273 oldname = repo._bookmarks.expandname(opts[b'rename'])
1274 1274 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1275 1275 elif action == b'add':
1276 1276 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1277 1277 elif action == b'inactive':
1278 1278 if len(repo._bookmarks) == 0:
1279 1279 ui.status(_(b"no bookmarks set\n"))
1280 1280 elif not repo._activebookmark:
1281 1281 ui.status(_(b"no active bookmark\n"))
1282 1282 else:
1283 1283 bookmarks.deactivate(repo)
1284 1284 elif action == b'list':
1285 1285 names = pycompat.maplist(repo._bookmarks.expandname, names)
1286 1286 with ui.formatter(b'bookmarks', opts) as fm:
1287 1287 bookmarks.printbookmarks(ui, repo, fm, names)
1288 1288 else:
1289 1289 raise error.ProgrammingError(b'invalid action: %s' % action)
1290 1290
1291 1291
1292 1292 @command(
1293 1293 b'branch',
1294 1294 [
1295 1295 (
1296 1296 b'f',
1297 1297 b'force',
1298 1298 None,
1299 1299 _(b'set branch name even if it shadows an existing branch'),
1300 1300 ),
1301 1301 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1302 1302 (
1303 1303 b'r',
1304 1304 b'rev',
1305 1305 [],
1306 1306 _(b'change branches of the given revs (EXPERIMENTAL)'),
1307 1307 ),
1308 1308 ],
1309 1309 _(b'[-fC] [NAME]'),
1310 1310 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1311 1311 )
1312 1312 def branch(ui, repo, label=None, **opts):
1313 1313 """set or show the current branch name
1314 1314
1315 1315 .. note::
1316 1316
1317 1317 Branch names are permanent and global. Use :hg:`bookmark` to create a
1318 1318 light-weight bookmark instead. See :hg:`help glossary` for more
1319 1319 information about named branches and bookmarks.
1320 1320
1321 1321 With no argument, show the current branch name. With one argument,
1322 1322 set the working directory branch name (the branch will not exist
1323 1323 in the repository until the next commit). Standard practice
1324 1324 recommends that primary development take place on the 'default'
1325 1325 branch.
1326 1326
1327 1327 Unless -f/--force is specified, branch will not let you set a
1328 1328 branch name that already exists.
1329 1329
1330 1330 Use -C/--clean to reset the working directory branch to that of
1331 1331 the parent of the working directory, negating a previous branch
1332 1332 change.
1333 1333
1334 1334 Use the command :hg:`update` to switch to an existing branch. Use
1335 1335 :hg:`commit --close-branch` to mark this branch head as closed.
1336 1336 When all heads of a branch are closed, the branch will be
1337 1337 considered closed.
1338 1338
1339 1339 Returns 0 on success.
1340 1340 """
1341 1341 opts = pycompat.byteskwargs(opts)
1342 1342 revs = opts.get(b'rev')
1343 1343 if label:
1344 1344 label = label.strip()
1345 1345
1346 1346 if not opts.get(b'clean') and not label:
1347 1347 if revs:
1348 1348 raise error.InputError(
1349 1349 _(b"no branch name specified for the revisions")
1350 1350 )
1351 1351 ui.write(b"%s\n" % repo.dirstate.branch())
1352 1352 return
1353 1353
1354 1354 with repo.wlock():
1355 1355 if opts.get(b'clean'):
1356 1356 label = repo[b'.'].branch()
1357 1357 repo.dirstate.setbranch(label)
1358 1358 ui.status(_(b'reset working directory to branch %s\n') % label)
1359 1359 elif label:
1360 1360
1361 1361 scmutil.checknewlabel(repo, label, b'branch')
1362 1362 if revs:
1363 1363 return cmdutil.changebranch(ui, repo, revs, label, opts)
1364 1364
1365 1365 if not opts.get(b'force') and label in repo.branchmap():
1366 1366 if label not in [p.branch() for p in repo[None].parents()]:
1367 1367 raise error.InputError(
1368 1368 _(b'a branch of the same name already exists'),
1369 1369 # i18n: "it" refers to an existing branch
1370 1370 hint=_(b"use 'hg update' to switch to it"),
1371 1371 )
1372 1372
1373 1373 repo.dirstate.setbranch(label)
1374 1374 ui.status(_(b'marked working directory as branch %s\n') % label)
1375 1375
1376 1376 # find any open named branches aside from default
1377 1377 for n, h, t, c in repo.branchmap().iterbranches():
1378 1378 if n != b"default" and not c:
1379 1379 return 0
1380 1380 ui.status(
1381 1381 _(
1382 1382 b'(branches are permanent and global, '
1383 1383 b'did you want a bookmark?)\n'
1384 1384 )
1385 1385 )
1386 1386
1387 1387
1388 1388 @command(
1389 1389 b'branches',
1390 1390 [
1391 1391 (
1392 1392 b'a',
1393 1393 b'active',
1394 1394 False,
1395 1395 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1396 1396 ),
1397 1397 (b'c', b'closed', False, _(b'show normal and closed branches')),
1398 1398 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1399 1399 ]
1400 1400 + formatteropts,
1401 1401 _(b'[-c]'),
1402 1402 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1403 1403 intents={INTENT_READONLY},
1404 1404 )
1405 1405 def branches(ui, repo, active=False, closed=False, **opts):
1406 1406 """list repository named branches
1407 1407
1408 1408 List the repository's named branches, indicating which ones are
1409 1409 inactive. If -c/--closed is specified, also list branches which have
1410 1410 been marked closed (see :hg:`commit --close-branch`).
1411 1411
1412 1412 Use the command :hg:`update` to switch to an existing branch.
1413 1413
1414 1414 .. container:: verbose
1415 1415
1416 1416 Template:
1417 1417
1418 1418 The following keywords are supported in addition to the common template
1419 1419 keywords and functions such as ``{branch}``. See also
1420 1420 :hg:`help templates`.
1421 1421
1422 1422 :active: Boolean. True if the branch is active.
1423 1423 :closed: Boolean. True if the branch is closed.
1424 1424 :current: Boolean. True if it is the current branch.
1425 1425
1426 1426 Returns 0.
1427 1427 """
1428 1428
1429 1429 opts = pycompat.byteskwargs(opts)
1430 1430 revs = opts.get(b'rev')
1431 1431 selectedbranches = None
1432 1432 if revs:
1433 1433 revs = scmutil.revrange(repo, revs)
1434 1434 getbi = repo.revbranchcache().branchinfo
1435 1435 selectedbranches = {getbi(r)[0] for r in revs}
1436 1436
1437 1437 ui.pager(b'branches')
1438 1438 fm = ui.formatter(b'branches', opts)
1439 1439 hexfunc = fm.hexfunc
1440 1440
1441 1441 allheads = set(repo.heads())
1442 1442 branches = []
1443 1443 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1444 1444 if selectedbranches is not None and tag not in selectedbranches:
1445 1445 continue
1446 1446 isactive = False
1447 1447 if not isclosed:
1448 1448 openheads = set(repo.branchmap().iteropen(heads))
1449 1449 isactive = bool(openheads & allheads)
1450 1450 branches.append((tag, repo[tip], isactive, not isclosed))
1451 1451 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1452 1452
1453 1453 for tag, ctx, isactive, isopen in branches:
1454 1454 if active and not isactive:
1455 1455 continue
1456 1456 if isactive:
1457 1457 label = b'branches.active'
1458 1458 notice = b''
1459 1459 elif not isopen:
1460 1460 if not closed:
1461 1461 continue
1462 1462 label = b'branches.closed'
1463 1463 notice = _(b' (closed)')
1464 1464 else:
1465 1465 label = b'branches.inactive'
1466 1466 notice = _(b' (inactive)')
1467 1467 current = tag == repo.dirstate.branch()
1468 1468 if current:
1469 1469 label = b'branches.current'
1470 1470
1471 1471 fm.startitem()
1472 1472 fm.write(b'branch', b'%s', tag, label=label)
1473 1473 rev = ctx.rev()
1474 1474 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1475 1475 fmt = b' ' * padsize + b' %d:%s'
1476 1476 fm.condwrite(
1477 1477 not ui.quiet,
1478 1478 b'rev node',
1479 1479 fmt,
1480 1480 rev,
1481 1481 hexfunc(ctx.node()),
1482 1482 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1483 1483 )
1484 1484 fm.context(ctx=ctx)
1485 1485 fm.data(active=isactive, closed=not isopen, current=current)
1486 1486 if not ui.quiet:
1487 1487 fm.plain(notice)
1488 1488 fm.plain(b'\n')
1489 1489 fm.end()
1490 1490
1491 1491
1492 1492 @command(
1493 1493 b'bundle',
1494 1494 [
1495 1495 (
1496 1496 b'f',
1497 1497 b'force',
1498 1498 None,
1499 1499 _(b'run even when the destination is unrelated'),
1500 1500 ),
1501 1501 (
1502 1502 b'r',
1503 1503 b'rev',
1504 1504 [],
1505 1505 _(b'a changeset intended to be added to the destination'),
1506 1506 _(b'REV'),
1507 1507 ),
1508 1508 (
1509 1509 b'b',
1510 1510 b'branch',
1511 1511 [],
1512 1512 _(b'a specific branch you would like to bundle'),
1513 1513 _(b'BRANCH'),
1514 1514 ),
1515 1515 (
1516 1516 b'',
1517 1517 b'base',
1518 1518 [],
1519 1519 _(b'a base changeset assumed to be available at the destination'),
1520 1520 _(b'REV'),
1521 1521 ),
1522 1522 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1523 1523 (
1524 1524 b't',
1525 1525 b'type',
1526 1526 b'bzip2',
1527 1527 _(b'bundle compression type to use'),
1528 1528 _(b'TYPE'),
1529 1529 ),
1530 1530 ]
1531 1531 + remoteopts,
1532 1532 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]...'),
1533 1533 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1534 1534 )
1535 1535 def bundle(ui, repo, fname, *dests, **opts):
1536 1536 """create a bundle file
1537 1537
1538 1538 Generate a bundle file containing data to be transferred to another
1539 1539 repository.
1540 1540
1541 1541 To create a bundle containing all changesets, use -a/--all
1542 1542 (or --base null). Otherwise, hg assumes the destination will have
1543 1543 all the nodes you specify with --base parameters. Otherwise, hg
1544 1544 will assume the repository has all the nodes in destination, or
1545 1545 default-push/default if no destination is specified, where destination
1546 1546 is the repositories you provide through DEST option.
1547 1547
1548 1548 You can change bundle format with the -t/--type option. See
1549 1549 :hg:`help bundlespec` for documentation on this format. By default,
1550 1550 the most appropriate format is used and compression defaults to
1551 1551 bzip2.
1552 1552
1553 1553 The bundle file can then be transferred using conventional means
1554 1554 and applied to another repository with the unbundle or pull
1555 1555 command. This is useful when direct push and pull are not
1556 1556 available or when exporting an entire repository is undesirable.
1557 1557
1558 1558 Applying bundles preserves all changeset contents including
1559 1559 permissions, copy/rename information, and revision history.
1560 1560
1561 1561 Returns 0 on success, 1 if no changes found.
1562 1562 """
1563 1563 opts = pycompat.byteskwargs(opts)
1564 1564 revs = None
1565 1565 if b'rev' in opts:
1566 1566 revstrings = opts[b'rev']
1567 1567 revs = scmutil.revrange(repo, revstrings)
1568 1568 if revstrings and not revs:
1569 1569 raise error.InputError(_(b'no commits to bundle'))
1570 1570
1571 1571 bundletype = opts.get(b'type', b'bzip2').lower()
1572 1572 try:
1573 1573 bundlespec = bundlecaches.parsebundlespec(
1574 1574 repo, bundletype, strict=False
1575 1575 )
1576 1576 except error.UnsupportedBundleSpecification as e:
1577 1577 raise error.InputError(
1578 1578 pycompat.bytestr(e),
1579 1579 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1580 1580 )
1581 1581 cgversion = bundlespec.contentopts[b"cg.version"]
1582 1582
1583 1583 # Packed bundles are a pseudo bundle format for now.
1584 1584 if cgversion == b's1':
1585 1585 raise error.InputError(
1586 1586 _(b'packed bundles cannot be produced by "hg bundle"'),
1587 1587 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1588 1588 )
1589 1589
1590 1590 if opts.get(b'all'):
1591 1591 if dests:
1592 1592 raise error.InputError(
1593 1593 _(b"--all is incompatible with specifying destinations")
1594 1594 )
1595 1595 if opts.get(b'base'):
1596 1596 ui.warn(_(b"ignoring --base because --all was specified\n"))
1597 1597 base = [nullrev]
1598 1598 else:
1599 1599 base = scmutil.revrange(repo, opts.get(b'base'))
1600 1600 if cgversion not in changegroup.supportedoutgoingversions(repo):
1601 1601 raise error.Abort(
1602 1602 _(b"repository does not support bundle version %s") % cgversion
1603 1603 )
1604 1604
1605 1605 if base:
1606 1606 if dests:
1607 1607 raise error.InputError(
1608 1608 _(b"--base is incompatible with specifying destinations")
1609 1609 )
1610 1610 common = [repo[rev].node() for rev in base]
1611 1611 heads = [repo[r].node() for r in revs] if revs else None
1612 1612 outgoing = discovery.outgoing(repo, common, heads)
1613 1613 missing = outgoing.missing
1614 1614 excluded = outgoing.excluded
1615 1615 else:
1616 1616 missing = set()
1617 1617 excluded = set()
1618 1618 for path in urlutil.get_push_paths(repo, ui, dests):
1619 1619 other = hg.peer(repo, opts, path.rawloc)
1620 1620 if revs is not None:
1621 1621 hex_revs = [repo[r].hex() for r in revs]
1622 1622 else:
1623 1623 hex_revs = None
1624 1624 branches = (path.branch, [])
1625 1625 head_revs, checkout = hg.addbranchrevs(
1626 1626 repo, repo, branches, hex_revs
1627 1627 )
1628 1628 heads = (
1629 1629 head_revs
1630 1630 and pycompat.maplist(repo.lookup, head_revs)
1631 1631 or head_revs
1632 1632 )
1633 1633 outgoing = discovery.findcommonoutgoing(
1634 1634 repo,
1635 1635 other,
1636 1636 onlyheads=heads,
1637 1637 force=opts.get(b'force'),
1638 1638 portable=True,
1639 1639 )
1640 1640 missing.update(outgoing.missing)
1641 1641 excluded.update(outgoing.excluded)
1642 1642
1643 1643 if not missing:
1644 1644 scmutil.nochangesfound(ui, repo, not base and excluded)
1645 1645 return 1
1646 1646
1647 1647 if heads:
1648 1648 outgoing = discovery.outgoing(
1649 1649 repo, missingroots=missing, ancestorsof=heads
1650 1650 )
1651 1651 else:
1652 1652 outgoing = discovery.outgoing(repo, missingroots=missing)
1653 1653 outgoing.excluded = sorted(excluded)
1654 1654
1655 1655 if cgversion == b'01': # bundle1
1656 1656 bversion = b'HG10' + bundlespec.wirecompression
1657 1657 bcompression = None
1658 1658 elif cgversion in (b'02', b'03'):
1659 1659 bversion = b'HG20'
1660 1660 bcompression = bundlespec.wirecompression
1661 1661 else:
1662 1662 raise error.ProgrammingError(
1663 1663 b'bundle: unexpected changegroup version %s' % cgversion
1664 1664 )
1665 1665
1666 1666 # TODO compression options should be derived from bundlespec parsing.
1667 1667 # This is a temporary hack to allow adjusting bundle compression
1668 1668 # level without a) formalizing the bundlespec changes to declare it
1669 1669 # b) introducing a command flag.
1670 1670 compopts = {}
1671 1671 complevel = ui.configint(
1672 1672 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1673 1673 )
1674 1674 if complevel is None:
1675 1675 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1676 1676 if complevel is not None:
1677 1677 compopts[b'level'] = complevel
1678 1678
1679 1679 compthreads = ui.configint(
1680 1680 b'experimental', b'bundlecompthreads.' + bundlespec.compression
1681 1681 )
1682 1682 if compthreads is None:
1683 1683 compthreads = ui.configint(b'experimental', b'bundlecompthreads')
1684 1684 if compthreads is not None:
1685 1685 compopts[b'threads'] = compthreads
1686 1686
1687 1687 # Bundling of obsmarker and phases is optional as not all clients
1688 1688 # support the necessary features.
1689 1689 cfg = ui.configbool
1690 1690 contentopts = {
1691 1691 b'obsolescence': cfg(b'experimental', b'evolution.bundle-obsmarker'),
1692 1692 b'obsolescence-mandatory': cfg(
1693 1693 b'experimental', b'evolution.bundle-obsmarker:mandatory'
1694 1694 ),
1695 1695 b'phases': cfg(b'experimental', b'bundle-phases'),
1696 1696 }
1697 1697 bundlespec.contentopts.update(contentopts)
1698 1698
1699 1699 bundle2.writenewbundle(
1700 1700 ui,
1701 1701 repo,
1702 1702 b'bundle',
1703 1703 fname,
1704 1704 bversion,
1705 1705 outgoing,
1706 1706 bundlespec.contentopts,
1707 1707 compression=bcompression,
1708 1708 compopts=compopts,
1709 1709 )
1710 1710
1711 1711
1712 1712 @command(
1713 1713 b'cat',
1714 1714 [
1715 1715 (
1716 1716 b'o',
1717 1717 b'output',
1718 1718 b'',
1719 1719 _(b'print output to file with formatted name'),
1720 1720 _(b'FORMAT'),
1721 1721 ),
1722 1722 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1723 1723 (b'', b'decode', None, _(b'apply any matching decode filter')),
1724 1724 ]
1725 1725 + walkopts
1726 1726 + formatteropts,
1727 1727 _(b'[OPTION]... FILE...'),
1728 1728 helpcategory=command.CATEGORY_FILE_CONTENTS,
1729 1729 inferrepo=True,
1730 1730 intents={INTENT_READONLY},
1731 1731 )
1732 1732 def cat(ui, repo, file1, *pats, **opts):
1733 1733 """output the current or given revision of files
1734 1734
1735 1735 Print the specified files as they were at the given revision. If
1736 1736 no revision is given, the parent of the working directory is used.
1737 1737
1738 1738 Output may be to a file, in which case the name of the file is
1739 1739 given using a template string. See :hg:`help templates`. In addition
1740 1740 to the common template keywords, the following formatting rules are
1741 1741 supported:
1742 1742
1743 1743 :``%%``: literal "%" character
1744 1744 :``%s``: basename of file being printed
1745 1745 :``%d``: dirname of file being printed, or '.' if in repository root
1746 1746 :``%p``: root-relative path name of file being printed
1747 1747 :``%H``: changeset hash (40 hexadecimal digits)
1748 1748 :``%R``: changeset revision number
1749 1749 :``%h``: short-form changeset hash (12 hexadecimal digits)
1750 1750 :``%r``: zero-padded changeset revision number
1751 1751 :``%b``: basename of the exporting repository
1752 1752 :``\\``: literal "\\" character
1753 1753
1754 1754 .. container:: verbose
1755 1755
1756 1756 Template:
1757 1757
1758 1758 The following keywords are supported in addition to the common template
1759 1759 keywords and functions. See also :hg:`help templates`.
1760 1760
1761 1761 :data: String. File content.
1762 1762 :path: String. Repository-absolute path of the file.
1763 1763
1764 1764 Returns 0 on success.
1765 1765 """
1766 1766 opts = pycompat.byteskwargs(opts)
1767 1767 rev = opts.get(b'rev')
1768 1768 if rev:
1769 1769 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1770 1770 ctx = scmutil.revsingle(repo, rev)
1771 1771 m = scmutil.match(ctx, (file1,) + pats, opts)
1772 1772 fntemplate = opts.pop(b'output', b'')
1773 1773 if cmdutil.isstdiofilename(fntemplate):
1774 1774 fntemplate = b''
1775 1775
1776 1776 if fntemplate:
1777 1777 fm = formatter.nullformatter(ui, b'cat', opts)
1778 1778 else:
1779 1779 ui.pager(b'cat')
1780 1780 fm = ui.formatter(b'cat', opts)
1781 1781 with fm:
1782 1782 return cmdutil.cat(
1783 1783 ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1784 1784 )
1785 1785
1786 1786
1787 1787 @command(
1788 1788 b'clone',
1789 1789 [
1790 1790 (
1791 1791 b'U',
1792 1792 b'noupdate',
1793 1793 None,
1794 1794 _(
1795 1795 b'the clone will include an empty working '
1796 1796 b'directory (only a repository)'
1797 1797 ),
1798 1798 ),
1799 1799 (
1800 1800 b'u',
1801 1801 b'updaterev',
1802 1802 b'',
1803 1803 _(b'revision, tag, or branch to check out'),
1804 1804 _(b'REV'),
1805 1805 ),
1806 1806 (
1807 1807 b'r',
1808 1808 b'rev',
1809 1809 [],
1810 1810 _(
1811 1811 b'do not clone everything, but include this changeset'
1812 1812 b' and its ancestors'
1813 1813 ),
1814 1814 _(b'REV'),
1815 1815 ),
1816 1816 (
1817 1817 b'b',
1818 1818 b'branch',
1819 1819 [],
1820 1820 _(
1821 1821 b'do not clone everything, but include this branch\'s'
1822 1822 b' changesets and their ancestors'
1823 1823 ),
1824 1824 _(b'BRANCH'),
1825 1825 ),
1826 1826 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1827 1827 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1828 1828 (b'', b'stream', None, _(b'clone with minimal data processing')),
1829 1829 ]
1830 1830 + remoteopts,
1831 1831 _(b'[OPTION]... SOURCE [DEST]'),
1832 1832 helpcategory=command.CATEGORY_REPO_CREATION,
1833 1833 helpbasic=True,
1834 1834 norepo=True,
1835 1835 )
1836 1836 def clone(ui, source, dest=None, **opts):
1837 1837 """make a copy of an existing repository
1838 1838
1839 1839 Create a copy of an existing repository in a new directory.
1840 1840
1841 1841 If no destination directory name is specified, it defaults to the
1842 1842 basename of the source.
1843 1843
1844 1844 The location of the source is added to the new repository's
1845 1845 ``.hg/hgrc`` file, as the default to be used for future pulls.
1846 1846
1847 1847 Only local paths and ``ssh://`` URLs are supported as
1848 1848 destinations. For ``ssh://`` destinations, no working directory or
1849 1849 ``.hg/hgrc`` will be created on the remote side.
1850 1850
1851 1851 If the source repository has a bookmark called '@' set, that
1852 1852 revision will be checked out in the new repository by default.
1853 1853
1854 1854 To check out a particular version, use -u/--update, or
1855 1855 -U/--noupdate to create a clone with no working directory.
1856 1856
1857 1857 To pull only a subset of changesets, specify one or more revisions
1858 1858 identifiers with -r/--rev or branches with -b/--branch. The
1859 1859 resulting clone will contain only the specified changesets and
1860 1860 their ancestors. These options (or 'clone src#rev dest') imply
1861 1861 --pull, even for local source repositories.
1862 1862
1863 1863 In normal clone mode, the remote normalizes repository data into a common
1864 1864 exchange format and the receiving end translates this data into its local
1865 1865 storage format. --stream activates a different clone mode that essentially
1866 1866 copies repository files from the remote with minimal data processing. This
1867 1867 significantly reduces the CPU cost of a clone both remotely and locally.
1868 1868 However, it often increases the transferred data size by 30-40%. This can
1869 1869 result in substantially faster clones where I/O throughput is plentiful,
1870 1870 especially for larger repositories. A side-effect of --stream clones is
1871 1871 that storage settings and requirements on the remote are applied locally:
1872 1872 a modern client may inherit legacy or inefficient storage used by the
1873 1873 remote or a legacy Mercurial client may not be able to clone from a
1874 1874 modern Mercurial remote.
1875 1875
1876 1876 .. note::
1877 1877
1878 1878 Specifying a tag will include the tagged changeset but not the
1879 1879 changeset containing the tag.
1880 1880
1881 1881 .. container:: verbose
1882 1882
1883 1883 For efficiency, hardlinks are used for cloning whenever the
1884 1884 source and destination are on the same filesystem (note this
1885 1885 applies only to the repository data, not to the working
1886 1886 directory). Some filesystems, such as AFS, implement hardlinking
1887 1887 incorrectly, but do not report errors. In these cases, use the
1888 1888 --pull option to avoid hardlinking.
1889 1889
1890 1890 Mercurial will update the working directory to the first applicable
1891 1891 revision from this list:
1892 1892
1893 1893 a) null if -U or the source repository has no changesets
1894 1894 b) if -u . and the source repository is local, the first parent of
1895 1895 the source repository's working directory
1896 1896 c) the changeset specified with -u (if a branch name, this means the
1897 1897 latest head of that branch)
1898 1898 d) the changeset specified with -r
1899 1899 e) the tipmost head specified with -b
1900 1900 f) the tipmost head specified with the url#branch source syntax
1901 1901 g) the revision marked with the '@' bookmark, if present
1902 1902 h) the tipmost head of the default branch
1903 1903 i) tip
1904 1904
1905 1905 When cloning from servers that support it, Mercurial may fetch
1906 1906 pre-generated data from a server-advertised URL or inline from the
1907 1907 same stream. When this is done, hooks operating on incoming changesets
1908 1908 and changegroups may fire more than once, once for each pre-generated
1909 1909 bundle and as well as for any additional remaining data. In addition,
1910 1910 if an error occurs, the repository may be rolled back to a partial
1911 1911 clone. This behavior may change in future releases.
1912 1912 See :hg:`help -e clonebundles` for more.
1913 1913
1914 1914 Examples:
1915 1915
1916 1916 - clone a remote repository to a new directory named hg/::
1917 1917
1918 1918 hg clone https://www.mercurial-scm.org/repo/hg/
1919 1919
1920 1920 - create a lightweight local clone::
1921 1921
1922 1922 hg clone project/ project-feature/
1923 1923
1924 1924 - clone from an absolute path on an ssh server (note double-slash)::
1925 1925
1926 1926 hg clone ssh://user@server//home/projects/alpha/
1927 1927
1928 1928 - do a streaming clone while checking out a specified version::
1929 1929
1930 1930 hg clone --stream http://server/repo -u 1.5
1931 1931
1932 1932 - create a repository without changesets after a particular revision::
1933 1933
1934 1934 hg clone -r 04e544 experimental/ good/
1935 1935
1936 1936 - clone (and track) a particular named branch::
1937 1937
1938 1938 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1939 1939
1940 1940 See :hg:`help urls` for details on specifying URLs.
1941 1941
1942 1942 Returns 0 on success.
1943 1943 """
1944 1944 opts = pycompat.byteskwargs(opts)
1945 1945 cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1946 1946
1947 1947 # --include/--exclude can come from narrow or sparse.
1948 1948 includepats, excludepats = None, None
1949 1949
1950 1950 # hg.clone() differentiates between None and an empty set. So make sure
1951 1951 # patterns are sets if narrow is requested without patterns.
1952 1952 if opts.get(b'narrow'):
1953 1953 includepats = set()
1954 1954 excludepats = set()
1955 1955
1956 1956 if opts.get(b'include'):
1957 1957 includepats = narrowspec.parsepatterns(opts.get(b'include'))
1958 1958 if opts.get(b'exclude'):
1959 1959 excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1960 1960
1961 1961 r = hg.clone(
1962 1962 ui,
1963 1963 opts,
1964 1964 source,
1965 1965 dest,
1966 1966 pull=opts.get(b'pull'),
1967 1967 stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1968 1968 revs=opts.get(b'rev'),
1969 1969 update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1970 1970 branch=opts.get(b'branch'),
1971 1971 shareopts=opts.get(b'shareopts'),
1972 1972 storeincludepats=includepats,
1973 1973 storeexcludepats=excludepats,
1974 1974 depth=opts.get(b'depth') or None,
1975 1975 )
1976 1976
1977 1977 return r is None
1978 1978
1979 1979
1980 1980 @command(
1981 1981 b'commit|ci',
1982 1982 [
1983 1983 (
1984 1984 b'A',
1985 1985 b'addremove',
1986 1986 None,
1987 1987 _(b'mark new/missing files as added/removed before committing'),
1988 1988 ),
1989 1989 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1990 1990 (b'', b'amend', None, _(b'amend the parent of the working directory')),
1991 1991 (b's', b'secret', None, _(b'use the secret phase for committing')),
1992 1992 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1993 1993 (
1994 1994 b'',
1995 1995 b'force-close-branch',
1996 1996 None,
1997 1997 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1998 1998 ),
1999 1999 (b'i', b'interactive', None, _(b'use interactive mode')),
2000 2000 ]
2001 2001 + walkopts
2002 2002 + commitopts
2003 2003 + commitopts2
2004 2004 + subrepoopts,
2005 2005 _(b'[OPTION]... [FILE]...'),
2006 2006 helpcategory=command.CATEGORY_COMMITTING,
2007 2007 helpbasic=True,
2008 2008 inferrepo=True,
2009 2009 )
2010 2010 def commit(ui, repo, *pats, **opts):
2011 2011 """commit the specified files or all outstanding changes
2012 2012
2013 2013 Commit changes to the given files into the repository. Unlike a
2014 2014 centralized SCM, this operation is a local operation. See
2015 2015 :hg:`push` for a way to actively distribute your changes.
2016 2016
2017 2017 If a list of files is omitted, all changes reported by :hg:`status`
2018 2018 will be committed.
2019 2019
2020 2020 If you are committing the result of a merge, do not provide any
2021 2021 filenames or -I/-X filters.
2022 2022
2023 2023 If no commit message is specified, Mercurial starts your
2024 2024 configured editor where you can enter a message. In case your
2025 2025 commit fails, you will find a backup of your message in
2026 2026 ``.hg/last-message.txt``.
2027 2027
2028 2028 The --close-branch flag can be used to mark the current branch
2029 2029 head closed. When all heads of a branch are closed, the branch
2030 2030 will be considered closed and no longer listed.
2031 2031
2032 2032 The --amend flag can be used to amend the parent of the
2033 2033 working directory with a new commit that contains the changes
2034 2034 in the parent in addition to those currently reported by :hg:`status`,
2035 2035 if there are any. The old commit is stored in a backup bundle in
2036 2036 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
2037 2037 on how to restore it).
2038 2038
2039 2039 Message, user and date are taken from the amended commit unless
2040 2040 specified. When a message isn't specified on the command line,
2041 2041 the editor will open with the message of the amended commit.
2042 2042
2043 2043 It is not possible to amend public changesets (see :hg:`help phases`)
2044 2044 or changesets that have children.
2045 2045
2046 2046 See :hg:`help dates` for a list of formats valid for -d/--date.
2047 2047
2048 2048 Returns 0 on success, 1 if nothing changed.
2049 2049
2050 2050 .. container:: verbose
2051 2051
2052 2052 Examples:
2053 2053
2054 2054 - commit all files ending in .py::
2055 2055
2056 2056 hg commit --include "set:**.py"
2057 2057
2058 2058 - commit all non-binary files::
2059 2059
2060 2060 hg commit --exclude "set:binary()"
2061 2061
2062 2062 - amend the current commit and set the date to now::
2063 2063
2064 2064 hg commit --amend --date now
2065 2065 """
2066 2066 with repo.wlock(), repo.lock():
2067 2067 return _docommit(ui, repo, *pats, **opts)
2068 2068
2069 2069
2070 2070 def _docommit(ui, repo, *pats, **opts):
2071 2071 if opts.get('interactive'):
2072 2072 opts.pop('interactive')
2073 2073 ret = cmdutil.dorecord(
2074 2074 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2075 2075 )
2076 2076 # ret can be 0 (no changes to record) or the value returned by
2077 2077 # commit(), 1 if nothing changed or None on success.
2078 2078 return 1 if ret == 0 else ret
2079 2079
2080 2080 opts = pycompat.byteskwargs(opts)
2081 2081 if opts.get(b'subrepos'):
2082 2082 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'amend'])
2083 2083 # Let --subrepos on the command line override config setting.
2084 2084 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2085 2085
2086 2086 cmdutil.checkunfinished(repo, commit=True)
2087 2087
2088 2088 branch = repo[None].branch()
2089 2089 bheads = repo.branchheads(branch)
2090 2090 tip = repo.changelog.tip()
2091 2091
2092 2092 extra = {}
2093 2093 if opts.get(b'close_branch') or opts.get(b'force_close_branch'):
2094 2094 extra[b'close'] = b'1'
2095 2095
2096 2096 if repo[b'.'].closesbranch():
2097 2097 raise error.InputError(
2098 2098 _(b'current revision is already a branch closing head')
2099 2099 )
2100 2100 elif not bheads:
2101 2101 raise error.InputError(
2102 2102 _(b'branch "%s" has no heads to close') % branch
2103 2103 )
2104 2104 elif (
2105 2105 branch == repo[b'.'].branch()
2106 2106 and repo[b'.'].node() not in bheads
2107 2107 and not opts.get(b'force_close_branch')
2108 2108 ):
2109 2109 hint = _(
2110 2110 b'use --force-close-branch to close branch from a non-head'
2111 2111 b' changeset'
2112 2112 )
2113 2113 raise error.InputError(_(b'can only close branch heads'), hint=hint)
2114 2114 elif opts.get(b'amend'):
2115 2115 if (
2116 2116 repo[b'.'].p1().branch() != branch
2117 2117 and repo[b'.'].p2().branch() != branch
2118 2118 ):
2119 2119 raise error.InputError(_(b'can only close branch heads'))
2120 2120
2121 2121 if opts.get(b'amend'):
2122 2122 if ui.configbool(b'ui', b'commitsubrepos'):
2123 2123 raise error.InputError(
2124 2124 _(b'cannot amend with ui.commitsubrepos enabled')
2125 2125 )
2126 2126
2127 2127 old = repo[b'.']
2128 2128 rewriteutil.precheck(repo, [old.rev()], b'amend')
2129 2129
2130 2130 # Currently histedit gets confused if an amend happens while histedit
2131 2131 # is in progress. Since we have a checkunfinished command, we are
2132 2132 # temporarily honoring it.
2133 2133 #
2134 2134 # Note: eventually this guard will be removed. Please do not expect
2135 2135 # this behavior to remain.
2136 2136 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2137 2137 cmdutil.checkunfinished(repo)
2138 2138
2139 2139 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2140 2140 if node == old.node():
2141 2141 ui.status(_(b"nothing changed\n"))
2142 2142 return 1
2143 2143 else:
2144 2144
2145 2145 def commitfunc(ui, repo, message, match, opts):
2146 2146 overrides = {}
2147 2147 if opts.get(b'secret'):
2148 2148 overrides[(b'phases', b'new-commit')] = b'secret'
2149 2149
2150 2150 baseui = repo.baseui
2151 2151 with baseui.configoverride(overrides, b'commit'):
2152 2152 with ui.configoverride(overrides, b'commit'):
2153 2153 editform = cmdutil.mergeeditform(
2154 2154 repo[None], b'commit.normal'
2155 2155 )
2156 2156 editor = cmdutil.getcommiteditor(
2157 2157 editform=editform, **pycompat.strkwargs(opts)
2158 2158 )
2159 2159 return repo.commit(
2160 2160 message,
2161 2161 opts.get(b'user'),
2162 2162 opts.get(b'date'),
2163 2163 match,
2164 2164 editor=editor,
2165 2165 extra=extra,
2166 2166 )
2167 2167
2168 2168 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2169 2169
2170 2170 if not node:
2171 2171 stat = cmdutil.postcommitstatus(repo, pats, opts)
2172 2172 if stat.deleted:
2173 2173 ui.status(
2174 2174 _(
2175 2175 b"nothing changed (%d missing files, see "
2176 2176 b"'hg status')\n"
2177 2177 )
2178 2178 % len(stat.deleted)
2179 2179 )
2180 2180 else:
2181 2181 ui.status(_(b"nothing changed\n"))
2182 2182 return 1
2183 2183
2184 2184 cmdutil.commitstatus(repo, node, branch, bheads, tip, opts)
2185 2185
2186 2186 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2187 2187 status(
2188 2188 ui,
2189 2189 repo,
2190 2190 modified=True,
2191 2191 added=True,
2192 2192 removed=True,
2193 2193 deleted=True,
2194 2194 unknown=True,
2195 2195 subrepos=opts.get(b'subrepos'),
2196 2196 )
2197 2197
2198 2198
2199 2199 @command(
2200 2200 b'config|showconfig|debugconfig',
2201 2201 [
2202 2202 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2203 # This is experimental because we need
2204 # * reasonable behavior around aliases,
2205 # * decide if we display [debug] [experimental] and [devel] section par
2206 # default
2207 # * some way to display "generic" config entry (the one matching
2208 # regexp,
2209 # * proper display of the different value type
2210 # * a better way to handle <DYNAMIC> values (and variable types),
2211 # * maybe some type information ?
2212 (
2213 b'',
2214 b'exp-all-known',
2215 None,
2216 _(b'show all known config option (EXPERIMENTAL)'),
2217 ),
2203 2218 (b'e', b'edit', None, _(b'edit user config')),
2204 2219 (b'l', b'local', None, _(b'edit repository config')),
2205 2220 (b'', b'source', None, _(b'show source of configuration value')),
2206 2221 (
2207 2222 b'',
2208 2223 b'shared',
2209 2224 None,
2210 2225 _(b'edit shared source repository config (EXPERIMENTAL)'),
2211 2226 ),
2212 2227 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2213 2228 (b'g', b'global', None, _(b'edit global config')),
2214 2229 ]
2215 2230 + formatteropts,
2216 2231 _(b'[-u] [NAME]...'),
2217 2232 helpcategory=command.CATEGORY_HELP,
2218 2233 optionalrepo=True,
2219 2234 intents={INTENT_READONLY},
2220 2235 )
2221 2236 def config(ui, repo, *values, **opts):
2222 2237 """show combined config settings from all hgrc files
2223 2238
2224 2239 With no arguments, print names and values of all config items.
2225 2240
2226 2241 With one argument of the form section.name, print just the value
2227 2242 of that config item.
2228 2243
2229 2244 With multiple arguments, print names and values of all config
2230 2245 items with matching section names or section.names.
2231 2246
2232 2247 With --edit, start an editor on the user-level config file. With
2233 2248 --global, edit the system-wide config file. With --local, edit the
2234 2249 repository-level config file.
2235 2250
2236 2251 With --source, the source (filename and line number) is printed
2237 2252 for each config item.
2238 2253
2239 2254 See :hg:`help config` for more information about config files.
2240 2255
2241 2256 .. container:: verbose
2242 2257
2243 2258 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2244 2259 This file is not shared across shares when in share-safe mode.
2245 2260
2246 2261 Template:
2247 2262
2248 2263 The following keywords are supported. See also :hg:`help templates`.
2249 2264
2250 2265 :name: String. Config name.
2251 2266 :source: String. Filename and line number where the item is defined.
2252 2267 :value: String. Config value.
2253 2268
2254 2269 The --shared flag can be used to edit the config file of shared source
2255 2270 repository. It only works when you have shared using the experimental
2256 2271 share safe feature.
2257 2272
2258 2273 Returns 0 on success, 1 if NAME does not exist.
2259 2274
2260 2275 """
2261 2276
2262 2277 opts = pycompat.byteskwargs(opts)
2263 2278 editopts = (b'edit', b'local', b'global', b'shared', b'non_shared')
2264 2279 if any(opts.get(o) for o in editopts):
2265 2280 cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2266 2281 if opts.get(b'local'):
2267 2282 if not repo:
2268 2283 raise error.InputError(
2269 2284 _(b"can't use --local outside a repository")
2270 2285 )
2271 2286 paths = [repo.vfs.join(b'hgrc')]
2272 2287 elif opts.get(b'global'):
2273 2288 paths = rcutil.systemrcpath()
2274 2289 elif opts.get(b'shared'):
2275 2290 if not repo.shared():
2276 2291 raise error.InputError(
2277 2292 _(b"repository is not shared; can't use --shared")
2278 2293 )
2279 2294 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2280 2295 raise error.InputError(
2281 2296 _(
2282 2297 b"share safe feature not enabled; "
2283 2298 b"unable to edit shared source repository config"
2284 2299 )
2285 2300 )
2286 2301 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2287 2302 elif opts.get(b'non_shared'):
2288 2303 paths = [repo.vfs.join(b'hgrc-not-shared')]
2289 2304 else:
2290 2305 paths = rcutil.userrcpath()
2291 2306
2292 2307 for f in paths:
2293 2308 if os.path.exists(f):
2294 2309 break
2295 2310 else:
2296 2311 if opts.get(b'global'):
2297 2312 samplehgrc = uimod.samplehgrcs[b'global']
2298 2313 elif opts.get(b'local'):
2299 2314 samplehgrc = uimod.samplehgrcs[b'local']
2300 2315 else:
2301 2316 samplehgrc = uimod.samplehgrcs[b'user']
2302 2317
2303 2318 f = paths[0]
2304 2319 fp = open(f, b"wb")
2305 2320 fp.write(util.tonativeeol(samplehgrc))
2306 2321 fp.close()
2307 2322
2308 2323 editor = ui.geteditor()
2309 2324 ui.system(
2310 2325 b"%s \"%s\"" % (editor, f),
2311 2326 onerr=error.InputError,
2312 2327 errprefix=_(b"edit failed"),
2313 2328 blockedtag=b'config_edit',
2314 2329 )
2315 2330 return
2316 2331 ui.pager(b'config')
2317 2332 fm = ui.formatter(b'config', opts)
2318 2333 for t, f in rcutil.rccomponents():
2319 2334 if t == b'path':
2320 2335 ui.debug(b'read config from: %s\n' % f)
2321 2336 elif t == b'resource':
2322 2337 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2323 2338 elif t == b'items':
2324 2339 # Don't print anything for 'items'.
2325 2340 pass
2326 2341 else:
2327 2342 raise error.ProgrammingError(b'unknown rctype: %s' % t)
2328 2343 untrusted = bool(opts.get(b'untrusted'))
2329 2344
2330 2345 selsections = selentries = []
2331 2346 if values:
2332 2347 selsections = [v for v in values if b'.' not in v]
2333 2348 selentries = [v for v in values if b'.' in v]
2334 2349 uniquesel = len(selentries) == 1 and not selsections
2335 2350 selsections = set(selsections)
2336 2351 selentries = set(selentries)
2337 2352
2338 2353 matched = False
2354 all_known = opts[b'exp_all_known']
2339 2355 show_source = ui.debugflag or opts.get(b'source')
2340 for section, name, value in ui.walkconfig(untrusted=untrusted):
2356 entries = ui.walkconfig(untrusted=untrusted, all_known=all_known)
2357 for section, name, value in entries:
2341 2358 source = ui.configsource(section, name, untrusted)
2342 2359 value = pycompat.bytestr(value)
2343 2360 defaultvalue = ui.configdefault(section, name)
2344 2361 if fm.isplain():
2345 2362 source = source or b'none'
2346 2363 value = value.replace(b'\n', b'\\n')
2347 2364 entryname = section + b'.' + name
2348 2365 if values and not (section in selsections or entryname in selentries):
2349 2366 continue
2350 2367 fm.startitem()
2351 2368 fm.condwrite(show_source, b'source', b'%s: ', source)
2352 2369 if uniquesel:
2353 2370 fm.data(name=entryname)
2354 2371 fm.write(b'value', b'%s\n', value)
2355 2372 else:
2356 2373 fm.write(b'name value', b'%s=%s\n', entryname, value)
2357 2374 if formatter.isprintable(defaultvalue):
2358 2375 fm.data(defaultvalue=defaultvalue)
2359 2376 elif isinstance(defaultvalue, list) and all(
2360 2377 formatter.isprintable(e) for e in defaultvalue
2361 2378 ):
2362 2379 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2363 2380 # TODO: no idea how to process unsupported defaultvalue types
2364 2381 matched = True
2365 2382 fm.end()
2366 2383 if matched:
2367 2384 return 0
2368 2385 return 1
2369 2386
2370 2387
2371 2388 @command(
2372 2389 b'continue',
2373 2390 dryrunopts,
2374 2391 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2375 2392 helpbasic=True,
2376 2393 )
2377 2394 def continuecmd(ui, repo, **opts):
2378 2395 """resumes an interrupted operation (EXPERIMENTAL)
2379 2396
2380 2397 Finishes a multistep operation like graft, histedit, rebase, merge,
2381 2398 and unshelve if they are in an interrupted state.
2382 2399
2383 2400 use --dry-run/-n to dry run the command.
2384 2401 """
2385 2402 dryrun = opts.get('dry_run')
2386 2403 contstate = cmdutil.getunfinishedstate(repo)
2387 2404 if not contstate:
2388 2405 raise error.StateError(_(b'no operation in progress'))
2389 2406 if not contstate.continuefunc:
2390 2407 raise error.StateError(
2391 2408 (
2392 2409 _(b"%s in progress but does not support 'hg continue'")
2393 2410 % (contstate._opname)
2394 2411 ),
2395 2412 hint=contstate.continuemsg(),
2396 2413 )
2397 2414 if dryrun:
2398 2415 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2399 2416 return
2400 2417 return contstate.continuefunc(ui, repo)
2401 2418
2402 2419
2403 2420 @command(
2404 2421 b'copy|cp',
2405 2422 [
2406 2423 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2407 2424 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2408 2425 (
2409 2426 b'',
2410 2427 b'at-rev',
2411 2428 b'',
2412 2429 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2413 2430 _(b'REV'),
2414 2431 ),
2415 2432 (
2416 2433 b'f',
2417 2434 b'force',
2418 2435 None,
2419 2436 _(b'forcibly copy over an existing managed file'),
2420 2437 ),
2421 2438 ]
2422 2439 + walkopts
2423 2440 + dryrunopts,
2424 2441 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2425 2442 helpcategory=command.CATEGORY_FILE_CONTENTS,
2426 2443 )
2427 2444 def copy(ui, repo, *pats, **opts):
2428 2445 """mark files as copied for the next commit
2429 2446
2430 2447 Mark dest as having copies of source files. If dest is a
2431 2448 directory, copies are put in that directory. If dest is a file,
2432 2449 the source must be a single file.
2433 2450
2434 2451 By default, this command copies the contents of files as they
2435 2452 exist in the working directory. If invoked with -A/--after, the
2436 2453 operation is recorded, but no copying is performed.
2437 2454
2438 2455 To undo marking a destination file as copied, use --forget. With that
2439 2456 option, all given (positional) arguments are unmarked as copies. The
2440 2457 destination file(s) will be left in place (still tracked). Note that
2441 2458 :hg:`copy --forget` behaves the same way as :hg:`rename --forget`.
2442 2459
2443 2460 This command takes effect with the next commit by default.
2444 2461
2445 2462 Returns 0 on success, 1 if errors are encountered.
2446 2463 """
2447 2464 opts = pycompat.byteskwargs(opts)
2448 2465 with repo.wlock():
2449 2466 return cmdutil.copy(ui, repo, pats, opts)
2450 2467
2451 2468
2452 2469 @command(
2453 2470 b'debugcommands',
2454 2471 [],
2455 2472 _(b'[COMMAND]'),
2456 2473 helpcategory=command.CATEGORY_HELP,
2457 2474 norepo=True,
2458 2475 )
2459 2476 def debugcommands(ui, cmd=b'', *args):
2460 2477 """list all available commands and options"""
2461 2478 for cmd, vals in sorted(pycompat.iteritems(table)):
2462 2479 cmd = cmd.split(b'|')[0]
2463 2480 opts = b', '.join([i[1] for i in vals[1]])
2464 2481 ui.write(b'%s: %s\n' % (cmd, opts))
2465 2482
2466 2483
2467 2484 @command(
2468 2485 b'debugcomplete',
2469 2486 [(b'o', b'options', None, _(b'show the command options'))],
2470 2487 _(b'[-o] CMD'),
2471 2488 helpcategory=command.CATEGORY_HELP,
2472 2489 norepo=True,
2473 2490 )
2474 2491 def debugcomplete(ui, cmd=b'', **opts):
2475 2492 """returns the completion list associated with the given command"""
2476 2493
2477 2494 if opts.get('options'):
2478 2495 options = []
2479 2496 otables = [globalopts]
2480 2497 if cmd:
2481 2498 aliases, entry = cmdutil.findcmd(cmd, table, False)
2482 2499 otables.append(entry[1])
2483 2500 for t in otables:
2484 2501 for o in t:
2485 2502 if b"(DEPRECATED)" in o[3]:
2486 2503 continue
2487 2504 if o[0]:
2488 2505 options.append(b'-%s' % o[0])
2489 2506 options.append(b'--%s' % o[1])
2490 2507 ui.write(b"%s\n" % b"\n".join(options))
2491 2508 return
2492 2509
2493 2510 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2494 2511 if ui.verbose:
2495 2512 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2496 2513 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2497 2514
2498 2515
2499 2516 @command(
2500 2517 b'diff',
2501 2518 [
2502 2519 (b'r', b'rev', [], _(b'revision (DEPRECATED)'), _(b'REV')),
2503 2520 (b'', b'from', b'', _(b'revision to diff from'), _(b'REV1')),
2504 2521 (b'', b'to', b'', _(b'revision to diff to'), _(b'REV2')),
2505 2522 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2506 2523 ]
2507 2524 + diffopts
2508 2525 + diffopts2
2509 2526 + walkopts
2510 2527 + subrepoopts,
2511 2528 _(b'[OPTION]... ([-c REV] | [--from REV1] [--to REV2]) [FILE]...'),
2512 2529 helpcategory=command.CATEGORY_FILE_CONTENTS,
2513 2530 helpbasic=True,
2514 2531 inferrepo=True,
2515 2532 intents={INTENT_READONLY},
2516 2533 )
2517 2534 def diff(ui, repo, *pats, **opts):
2518 2535 """diff repository (or selected files)
2519 2536
2520 2537 Show differences between revisions for the specified files.
2521 2538
2522 2539 Differences between files are shown using the unified diff format.
2523 2540
2524 2541 .. note::
2525 2542
2526 2543 :hg:`diff` may generate unexpected results for merges, as it will
2527 2544 default to comparing against the working directory's first
2528 2545 parent changeset if no revisions are specified.
2529 2546
2530 2547 By default, the working directory files are compared to its first parent. To
2531 2548 see the differences from another revision, use --from. To see the difference
2532 2549 to another revision, use --to. For example, :hg:`diff --from .^` will show
2533 2550 the differences from the working copy's grandparent to the working copy,
2534 2551 :hg:`diff --to .` will show the diff from the working copy to its parent
2535 2552 (i.e. the reverse of the default), and :hg:`diff --from 1.0 --to 1.2` will
2536 2553 show the diff between those two revisions.
2537 2554
2538 2555 Alternatively you can specify -c/--change with a revision to see the changes
2539 2556 in that changeset relative to its first parent (i.e. :hg:`diff -c 42` is
2540 2557 equivalent to :hg:`diff --from 42^ --to 42`)
2541 2558
2542 2559 Without the -a/--text option, diff will avoid generating diffs of
2543 2560 files it detects as binary. With -a, diff will generate a diff
2544 2561 anyway, probably with undesirable results.
2545 2562
2546 2563 Use the -g/--git option to generate diffs in the git extended diff
2547 2564 format. For more information, read :hg:`help diffs`.
2548 2565
2549 2566 .. container:: verbose
2550 2567
2551 2568 Examples:
2552 2569
2553 2570 - compare a file in the current working directory to its parent::
2554 2571
2555 2572 hg diff foo.c
2556 2573
2557 2574 - compare two historical versions of a directory, with rename info::
2558 2575
2559 2576 hg diff --git --from 1.0 --to 1.2 lib/
2560 2577
2561 2578 - get change stats relative to the last change on some date::
2562 2579
2563 2580 hg diff --stat --from "date('may 2')"
2564 2581
2565 2582 - diff all newly-added files that contain a keyword::
2566 2583
2567 2584 hg diff "set:added() and grep(GNU)"
2568 2585
2569 2586 - compare a revision and its parents::
2570 2587
2571 2588 hg diff -c 9353 # compare against first parent
2572 2589 hg diff --from 9353^ --to 9353 # same using revset syntax
2573 2590 hg diff --from 9353^2 --to 9353 # compare against the second parent
2574 2591
2575 2592 Returns 0 on success.
2576 2593 """
2577 2594
2578 2595 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2579 2596 opts = pycompat.byteskwargs(opts)
2580 2597 revs = opts.get(b'rev')
2581 2598 change = opts.get(b'change')
2582 2599 from_rev = opts.get(b'from')
2583 2600 to_rev = opts.get(b'to')
2584 2601 stat = opts.get(b'stat')
2585 2602 reverse = opts.get(b'reverse')
2586 2603
2587 2604 cmdutil.check_incompatible_arguments(opts, b'from', [b'rev', b'change'])
2588 2605 cmdutil.check_incompatible_arguments(opts, b'to', [b'rev', b'change'])
2589 2606 if change:
2590 2607 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2591 2608 ctx2 = scmutil.revsingle(repo, change, None)
2592 2609 ctx1 = logcmdutil.diff_parent(ctx2)
2593 2610 elif from_rev or to_rev:
2594 2611 repo = scmutil.unhidehashlikerevs(
2595 2612 repo, [from_rev] + [to_rev], b'nowarn'
2596 2613 )
2597 2614 ctx1 = scmutil.revsingle(repo, from_rev, None)
2598 2615 ctx2 = scmutil.revsingle(repo, to_rev, None)
2599 2616 else:
2600 2617 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2601 2618 ctx1, ctx2 = scmutil.revpair(repo, revs)
2602 2619
2603 2620 if reverse:
2604 2621 ctxleft = ctx2
2605 2622 ctxright = ctx1
2606 2623 else:
2607 2624 ctxleft = ctx1
2608 2625 ctxright = ctx2
2609 2626
2610 2627 diffopts = patch.diffallopts(ui, opts)
2611 2628 m = scmutil.match(ctx2, pats, opts)
2612 2629 m = repo.narrowmatch(m)
2613 2630 ui.pager(b'diff')
2614 2631 logcmdutil.diffordiffstat(
2615 2632 ui,
2616 2633 repo,
2617 2634 diffopts,
2618 2635 ctxleft,
2619 2636 ctxright,
2620 2637 m,
2621 2638 stat=stat,
2622 2639 listsubrepos=opts.get(b'subrepos'),
2623 2640 root=opts.get(b'root'),
2624 2641 )
2625 2642
2626 2643
2627 2644 @command(
2628 2645 b'export',
2629 2646 [
2630 2647 (
2631 2648 b'B',
2632 2649 b'bookmark',
2633 2650 b'',
2634 2651 _(b'export changes only reachable by given bookmark'),
2635 2652 _(b'BOOKMARK'),
2636 2653 ),
2637 2654 (
2638 2655 b'o',
2639 2656 b'output',
2640 2657 b'',
2641 2658 _(b'print output to file with formatted name'),
2642 2659 _(b'FORMAT'),
2643 2660 ),
2644 2661 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2645 2662 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2646 2663 ]
2647 2664 + diffopts
2648 2665 + formatteropts,
2649 2666 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2650 2667 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2651 2668 helpbasic=True,
2652 2669 intents={INTENT_READONLY},
2653 2670 )
2654 2671 def export(ui, repo, *changesets, **opts):
2655 2672 """dump the header and diffs for one or more changesets
2656 2673
2657 2674 Print the changeset header and diffs for one or more revisions.
2658 2675 If no revision is given, the parent of the working directory is used.
2659 2676
2660 2677 The information shown in the changeset header is: author, date,
2661 2678 branch name (if non-default), changeset hash, parent(s) and commit
2662 2679 comment.
2663 2680
2664 2681 .. note::
2665 2682
2666 2683 :hg:`export` may generate unexpected diff output for merge
2667 2684 changesets, as it will compare the merge changeset against its
2668 2685 first parent only.
2669 2686
2670 2687 Output may be to a file, in which case the name of the file is
2671 2688 given using a template string. See :hg:`help templates`. In addition
2672 2689 to the common template keywords, the following formatting rules are
2673 2690 supported:
2674 2691
2675 2692 :``%%``: literal "%" character
2676 2693 :``%H``: changeset hash (40 hexadecimal digits)
2677 2694 :``%N``: number of patches being generated
2678 2695 :``%R``: changeset revision number
2679 2696 :``%b``: basename of the exporting repository
2680 2697 :``%h``: short-form changeset hash (12 hexadecimal digits)
2681 2698 :``%m``: first line of the commit message (only alphanumeric characters)
2682 2699 :``%n``: zero-padded sequence number, starting at 1
2683 2700 :``%r``: zero-padded changeset revision number
2684 2701 :``\\``: literal "\\" character
2685 2702
2686 2703 Without the -a/--text option, export will avoid generating diffs
2687 2704 of files it detects as binary. With -a, export will generate a
2688 2705 diff anyway, probably with undesirable results.
2689 2706
2690 2707 With -B/--bookmark changesets reachable by the given bookmark are
2691 2708 selected.
2692 2709
2693 2710 Use the -g/--git option to generate diffs in the git extended diff
2694 2711 format. See :hg:`help diffs` for more information.
2695 2712
2696 2713 With the --switch-parent option, the diff will be against the
2697 2714 second parent. It can be useful to review a merge.
2698 2715
2699 2716 .. container:: verbose
2700 2717
2701 2718 Template:
2702 2719
2703 2720 The following keywords are supported in addition to the common template
2704 2721 keywords and functions. See also :hg:`help templates`.
2705 2722
2706 2723 :diff: String. Diff content.
2707 2724 :parents: List of strings. Parent nodes of the changeset.
2708 2725
2709 2726 Examples:
2710 2727
2711 2728 - use export and import to transplant a bugfix to the current
2712 2729 branch::
2713 2730
2714 2731 hg export -r 9353 | hg import -
2715 2732
2716 2733 - export all the changesets between two revisions to a file with
2717 2734 rename information::
2718 2735
2719 2736 hg export --git -r 123:150 > changes.txt
2720 2737
2721 2738 - split outgoing changes into a series of patches with
2722 2739 descriptive names::
2723 2740
2724 2741 hg export -r "outgoing()" -o "%n-%m.patch"
2725 2742
2726 2743 Returns 0 on success.
2727 2744 """
2728 2745 opts = pycompat.byteskwargs(opts)
2729 2746 bookmark = opts.get(b'bookmark')
2730 2747 changesets += tuple(opts.get(b'rev', []))
2731 2748
2732 2749 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2733 2750
2734 2751 if bookmark:
2735 2752 if bookmark not in repo._bookmarks:
2736 2753 raise error.InputError(_(b"bookmark '%s' not found") % bookmark)
2737 2754
2738 2755 revs = scmutil.bookmarkrevs(repo, bookmark)
2739 2756 else:
2740 2757 if not changesets:
2741 2758 changesets = [b'.']
2742 2759
2743 2760 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2744 2761 revs = scmutil.revrange(repo, changesets)
2745 2762
2746 2763 if not revs:
2747 2764 raise error.InputError(_(b"export requires at least one changeset"))
2748 2765 if len(revs) > 1:
2749 2766 ui.note(_(b'exporting patches:\n'))
2750 2767 else:
2751 2768 ui.note(_(b'exporting patch:\n'))
2752 2769
2753 2770 fntemplate = opts.get(b'output')
2754 2771 if cmdutil.isstdiofilename(fntemplate):
2755 2772 fntemplate = b''
2756 2773
2757 2774 if fntemplate:
2758 2775 fm = formatter.nullformatter(ui, b'export', opts)
2759 2776 else:
2760 2777 ui.pager(b'export')
2761 2778 fm = ui.formatter(b'export', opts)
2762 2779 with fm:
2763 2780 cmdutil.export(
2764 2781 repo,
2765 2782 revs,
2766 2783 fm,
2767 2784 fntemplate=fntemplate,
2768 2785 switch_parent=opts.get(b'switch_parent'),
2769 2786 opts=patch.diffallopts(ui, opts),
2770 2787 )
2771 2788
2772 2789
2773 2790 @command(
2774 2791 b'files',
2775 2792 [
2776 2793 (
2777 2794 b'r',
2778 2795 b'rev',
2779 2796 b'',
2780 2797 _(b'search the repository as it is in REV'),
2781 2798 _(b'REV'),
2782 2799 ),
2783 2800 (
2784 2801 b'0',
2785 2802 b'print0',
2786 2803 None,
2787 2804 _(b'end filenames with NUL, for use with xargs'),
2788 2805 ),
2789 2806 ]
2790 2807 + walkopts
2791 2808 + formatteropts
2792 2809 + subrepoopts,
2793 2810 _(b'[OPTION]... [FILE]...'),
2794 2811 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2795 2812 intents={INTENT_READONLY},
2796 2813 )
2797 2814 def files(ui, repo, *pats, **opts):
2798 2815 """list tracked files
2799 2816
2800 2817 Print files under Mercurial control in the working directory or
2801 2818 specified revision for given files (excluding removed files).
2802 2819 Files can be specified as filenames or filesets.
2803 2820
2804 2821 If no files are given to match, this command prints the names
2805 2822 of all files under Mercurial control.
2806 2823
2807 2824 .. container:: verbose
2808 2825
2809 2826 Template:
2810 2827
2811 2828 The following keywords are supported in addition to the common template
2812 2829 keywords and functions. See also :hg:`help templates`.
2813 2830
2814 2831 :flags: String. Character denoting file's symlink and executable bits.
2815 2832 :path: String. Repository-absolute path of the file.
2816 2833 :size: Integer. Size of the file in bytes.
2817 2834
2818 2835 Examples:
2819 2836
2820 2837 - list all files under the current directory::
2821 2838
2822 2839 hg files .
2823 2840
2824 2841 - shows sizes and flags for current revision::
2825 2842
2826 2843 hg files -vr .
2827 2844
2828 2845 - list all files named README::
2829 2846
2830 2847 hg files -I "**/README"
2831 2848
2832 2849 - list all binary files::
2833 2850
2834 2851 hg files "set:binary()"
2835 2852
2836 2853 - find files containing a regular expression::
2837 2854
2838 2855 hg files "set:grep('bob')"
2839 2856
2840 2857 - search tracked file contents with xargs and grep::
2841 2858
2842 2859 hg files -0 | xargs -0 grep foo
2843 2860
2844 2861 See :hg:`help patterns` and :hg:`help filesets` for more information
2845 2862 on specifying file patterns.
2846 2863
2847 2864 Returns 0 if a match is found, 1 otherwise.
2848 2865
2849 2866 """
2850 2867
2851 2868 opts = pycompat.byteskwargs(opts)
2852 2869 rev = opts.get(b'rev')
2853 2870 if rev:
2854 2871 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2855 2872 ctx = scmutil.revsingle(repo, rev, None)
2856 2873
2857 2874 end = b'\n'
2858 2875 if opts.get(b'print0'):
2859 2876 end = b'\0'
2860 2877 fmt = b'%s' + end
2861 2878
2862 2879 m = scmutil.match(ctx, pats, opts)
2863 2880 ui.pager(b'files')
2864 2881 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2865 2882 with ui.formatter(b'files', opts) as fm:
2866 2883 return cmdutil.files(
2867 2884 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2868 2885 )
2869 2886
2870 2887
2871 2888 @command(
2872 2889 b'forget',
2873 2890 [
2874 2891 (b'i', b'interactive', None, _(b'use interactive mode')),
2875 2892 ]
2876 2893 + walkopts
2877 2894 + dryrunopts,
2878 2895 _(b'[OPTION]... FILE...'),
2879 2896 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2880 2897 helpbasic=True,
2881 2898 inferrepo=True,
2882 2899 )
2883 2900 def forget(ui, repo, *pats, **opts):
2884 2901 """forget the specified files on the next commit
2885 2902
2886 2903 Mark the specified files so they will no longer be tracked
2887 2904 after the next commit.
2888 2905
2889 2906 This only removes files from the current branch, not from the
2890 2907 entire project history, and it does not delete them from the
2891 2908 working directory.
2892 2909
2893 2910 To delete the file from the working directory, see :hg:`remove`.
2894 2911
2895 2912 To undo a forget before the next commit, see :hg:`add`.
2896 2913
2897 2914 .. container:: verbose
2898 2915
2899 2916 Examples:
2900 2917
2901 2918 - forget newly-added binary files::
2902 2919
2903 2920 hg forget "set:added() and binary()"
2904 2921
2905 2922 - forget files that would be excluded by .hgignore::
2906 2923
2907 2924 hg forget "set:hgignore()"
2908 2925
2909 2926 Returns 0 on success.
2910 2927 """
2911 2928
2912 2929 opts = pycompat.byteskwargs(opts)
2913 2930 if not pats:
2914 2931 raise error.InputError(_(b'no files specified'))
2915 2932
2916 2933 m = scmutil.match(repo[None], pats, opts)
2917 2934 dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2918 2935 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2919 2936 rejected = cmdutil.forget(
2920 2937 ui,
2921 2938 repo,
2922 2939 m,
2923 2940 prefix=b"",
2924 2941 uipathfn=uipathfn,
2925 2942 explicitonly=False,
2926 2943 dryrun=dryrun,
2927 2944 interactive=interactive,
2928 2945 )[0]
2929 2946 return rejected and 1 or 0
2930 2947
2931 2948
2932 2949 @command(
2933 2950 b'graft',
2934 2951 [
2935 2952 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2936 2953 (
2937 2954 b'',
2938 2955 b'base',
2939 2956 b'',
2940 2957 _(b'base revision when doing the graft merge (ADVANCED)'),
2941 2958 _(b'REV'),
2942 2959 ),
2943 2960 (b'c', b'continue', False, _(b'resume interrupted graft')),
2944 2961 (b'', b'stop', False, _(b'stop interrupted graft')),
2945 2962 (b'', b'abort', False, _(b'abort interrupted graft')),
2946 2963 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2947 2964 (b'', b'log', None, _(b'append graft info to log message')),
2948 2965 (
2949 2966 b'',
2950 2967 b'no-commit',
2951 2968 None,
2952 2969 _(b"don't commit, just apply the changes in working directory"),
2953 2970 ),
2954 2971 (b'f', b'force', False, _(b'force graft')),
2955 2972 (
2956 2973 b'D',
2957 2974 b'currentdate',
2958 2975 False,
2959 2976 _(b'record the current date as commit date'),
2960 2977 ),
2961 2978 (
2962 2979 b'U',
2963 2980 b'currentuser',
2964 2981 False,
2965 2982 _(b'record the current user as committer'),
2966 2983 ),
2967 2984 ]
2968 2985 + commitopts2
2969 2986 + mergetoolopts
2970 2987 + dryrunopts,
2971 2988 _(b'[OPTION]... [-r REV]... REV...'),
2972 2989 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2973 2990 )
2974 2991 def graft(ui, repo, *revs, **opts):
2975 2992 """copy changes from other branches onto the current branch
2976 2993
2977 2994 This command uses Mercurial's merge logic to copy individual
2978 2995 changes from other branches without merging branches in the
2979 2996 history graph. This is sometimes known as 'backporting' or
2980 2997 'cherry-picking'. By default, graft will copy user, date, and
2981 2998 description from the source changesets.
2982 2999
2983 3000 Changesets that are ancestors of the current revision, that have
2984 3001 already been grafted, or that are merges will be skipped.
2985 3002
2986 3003 If --log is specified, log messages will have a comment appended
2987 3004 of the form::
2988 3005
2989 3006 (grafted from CHANGESETHASH)
2990 3007
2991 3008 If --force is specified, revisions will be grafted even if they
2992 3009 are already ancestors of, or have been grafted to, the destination.
2993 3010 This is useful when the revisions have since been backed out.
2994 3011
2995 3012 If a graft merge results in conflicts, the graft process is
2996 3013 interrupted so that the current merge can be manually resolved.
2997 3014 Once all conflicts are addressed, the graft process can be
2998 3015 continued with the -c/--continue option.
2999 3016
3000 3017 The -c/--continue option reapplies all the earlier options.
3001 3018
3002 3019 .. container:: verbose
3003 3020
3004 3021 The --base option exposes more of how graft internally uses merge with a
3005 3022 custom base revision. --base can be used to specify another ancestor than
3006 3023 the first and only parent.
3007 3024
3008 3025 The command::
3009 3026
3010 3027 hg graft -r 345 --base 234
3011 3028
3012 3029 is thus pretty much the same as::
3013 3030
3014 3031 hg diff --from 234 --to 345 | hg import
3015 3032
3016 3033 but using merge to resolve conflicts and track moved files.
3017 3034
3018 3035 The result of a merge can thus be backported as a single commit by
3019 3036 specifying one of the merge parents as base, and thus effectively
3020 3037 grafting the changes from the other side.
3021 3038
3022 3039 It is also possible to collapse multiple changesets and clean up history
3023 3040 by specifying another ancestor as base, much like rebase --collapse
3024 3041 --keep.
3025 3042
3026 3043 The commit message can be tweaked after the fact using commit --amend .
3027 3044
3028 3045 For using non-ancestors as the base to backout changes, see the backout
3029 3046 command and the hidden --parent option.
3030 3047
3031 3048 .. container:: verbose
3032 3049
3033 3050 Examples:
3034 3051
3035 3052 - copy a single change to the stable branch and edit its description::
3036 3053
3037 3054 hg update stable
3038 3055 hg graft --edit 9393
3039 3056
3040 3057 - graft a range of changesets with one exception, updating dates::
3041 3058
3042 3059 hg graft -D "2085::2093 and not 2091"
3043 3060
3044 3061 - continue a graft after resolving conflicts::
3045 3062
3046 3063 hg graft -c
3047 3064
3048 3065 - show the source of a grafted changeset::
3049 3066
3050 3067 hg log --debug -r .
3051 3068
3052 3069 - show revisions sorted by date::
3053 3070
3054 3071 hg log -r "sort(all(), date)"
3055 3072
3056 3073 - backport the result of a merge as a single commit::
3057 3074
3058 3075 hg graft -r 123 --base 123^
3059 3076
3060 3077 - land a feature branch as one changeset::
3061 3078
3062 3079 hg up -cr default
3063 3080 hg graft -r featureX --base "ancestor('featureX', 'default')"
3064 3081
3065 3082 See :hg:`help revisions` for more about specifying revisions.
3066 3083
3067 3084 Returns 0 on successful completion, 1 if there are unresolved files.
3068 3085 """
3069 3086 with repo.wlock():
3070 3087 return _dograft(ui, repo, *revs, **opts)
3071 3088
3072 3089
3073 3090 def _dograft(ui, repo, *revs, **opts):
3074 3091 opts = pycompat.byteskwargs(opts)
3075 3092 if revs and opts.get(b'rev'):
3076 3093 ui.warn(
3077 3094 _(
3078 3095 b'warning: inconsistent use of --rev might give unexpected '
3079 3096 b'revision ordering!\n'
3080 3097 )
3081 3098 )
3082 3099
3083 3100 revs = list(revs)
3084 3101 revs.extend(opts.get(b'rev'))
3085 3102 # a dict of data to be stored in state file
3086 3103 statedata = {}
3087 3104 # list of new nodes created by ongoing graft
3088 3105 statedata[b'newnodes'] = []
3089 3106
3090 3107 cmdutil.resolvecommitoptions(ui, opts)
3091 3108
3092 3109 editor = cmdutil.getcommiteditor(
3093 3110 editform=b'graft', **pycompat.strkwargs(opts)
3094 3111 )
3095 3112
3096 3113 cmdutil.check_at_most_one_arg(opts, b'abort', b'stop', b'continue')
3097 3114
3098 3115 cont = False
3099 3116 if opts.get(b'no_commit'):
3100 3117 cmdutil.check_incompatible_arguments(
3101 3118 opts,
3102 3119 b'no_commit',
3103 3120 [b'edit', b'currentuser', b'currentdate', b'log'],
3104 3121 )
3105 3122
3106 3123 graftstate = statemod.cmdstate(repo, b'graftstate')
3107 3124
3108 3125 if opts.get(b'stop'):
3109 3126 cmdutil.check_incompatible_arguments(
3110 3127 opts,
3111 3128 b'stop',
3112 3129 [
3113 3130 b'edit',
3114 3131 b'log',
3115 3132 b'user',
3116 3133 b'date',
3117 3134 b'currentdate',
3118 3135 b'currentuser',
3119 3136 b'rev',
3120 3137 ],
3121 3138 )
3122 3139 return _stopgraft(ui, repo, graftstate)
3123 3140 elif opts.get(b'abort'):
3124 3141 cmdutil.check_incompatible_arguments(
3125 3142 opts,
3126 3143 b'abort',
3127 3144 [
3128 3145 b'edit',
3129 3146 b'log',
3130 3147 b'user',
3131 3148 b'date',
3132 3149 b'currentdate',
3133 3150 b'currentuser',
3134 3151 b'rev',
3135 3152 ],
3136 3153 )
3137 3154 return cmdutil.abortgraft(ui, repo, graftstate)
3138 3155 elif opts.get(b'continue'):
3139 3156 cont = True
3140 3157 if revs:
3141 3158 raise error.InputError(_(b"can't specify --continue and revisions"))
3142 3159 # read in unfinished revisions
3143 3160 if graftstate.exists():
3144 3161 statedata = cmdutil.readgraftstate(repo, graftstate)
3145 3162 if statedata.get(b'date'):
3146 3163 opts[b'date'] = statedata[b'date']
3147 3164 if statedata.get(b'user'):
3148 3165 opts[b'user'] = statedata[b'user']
3149 3166 if statedata.get(b'log'):
3150 3167 opts[b'log'] = True
3151 3168 if statedata.get(b'no_commit'):
3152 3169 opts[b'no_commit'] = statedata.get(b'no_commit')
3153 3170 if statedata.get(b'base'):
3154 3171 opts[b'base'] = statedata.get(b'base')
3155 3172 nodes = statedata[b'nodes']
3156 3173 revs = [repo[node].rev() for node in nodes]
3157 3174 else:
3158 3175 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3159 3176 else:
3160 3177 if not revs:
3161 3178 raise error.InputError(_(b'no revisions specified'))
3162 3179 cmdutil.checkunfinished(repo)
3163 3180 cmdutil.bailifchanged(repo)
3164 3181 revs = scmutil.revrange(repo, revs)
3165 3182
3166 3183 skipped = set()
3167 3184 basectx = None
3168 3185 if opts.get(b'base'):
3169 3186 basectx = scmutil.revsingle(repo, opts[b'base'], None)
3170 3187 if basectx is None:
3171 3188 # check for merges
3172 3189 for rev in repo.revs(b'%ld and merge()', revs):
3173 3190 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3174 3191 skipped.add(rev)
3175 3192 revs = [r for r in revs if r not in skipped]
3176 3193 if not revs:
3177 3194 return -1
3178 3195 if basectx is not None and len(revs) != 1:
3179 3196 raise error.InputError(_(b'only one revision allowed with --base '))
3180 3197
3181 3198 # Don't check in the --continue case, in effect retaining --force across
3182 3199 # --continues. That's because without --force, any revisions we decided to
3183 3200 # skip would have been filtered out here, so they wouldn't have made their
3184 3201 # way to the graftstate. With --force, any revisions we would have otherwise
3185 3202 # skipped would not have been filtered out, and if they hadn't been applied
3186 3203 # already, they'd have been in the graftstate.
3187 3204 if not (cont or opts.get(b'force')) and basectx is None:
3188 3205 # check for ancestors of dest branch
3189 3206 ancestors = repo.revs(b'%ld & (::.)', revs)
3190 3207 for rev in ancestors:
3191 3208 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3192 3209
3193 3210 revs = [r for r in revs if r not in ancestors]
3194 3211
3195 3212 if not revs:
3196 3213 return -1
3197 3214
3198 3215 # analyze revs for earlier grafts
3199 3216 ids = {}
3200 3217 for ctx in repo.set(b"%ld", revs):
3201 3218 ids[ctx.hex()] = ctx.rev()
3202 3219 n = ctx.extra().get(b'source')
3203 3220 if n:
3204 3221 ids[n] = ctx.rev()
3205 3222
3206 3223 # check ancestors for earlier grafts
3207 3224 ui.debug(b'scanning for duplicate grafts\n')
3208 3225
3209 3226 # The only changesets we can be sure doesn't contain grafts of any
3210 3227 # revs, are the ones that are common ancestors of *all* revs:
3211 3228 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3212 3229 ctx = repo[rev]
3213 3230 n = ctx.extra().get(b'source')
3214 3231 if n in ids:
3215 3232 try:
3216 3233 r = repo[n].rev()
3217 3234 except error.RepoLookupError:
3218 3235 r = None
3219 3236 if r in revs:
3220 3237 ui.warn(
3221 3238 _(
3222 3239 b'skipping revision %d:%s '
3223 3240 b'(already grafted to %d:%s)\n'
3224 3241 )
3225 3242 % (r, repo[r], rev, ctx)
3226 3243 )
3227 3244 revs.remove(r)
3228 3245 elif ids[n] in revs:
3229 3246 if r is None:
3230 3247 ui.warn(
3231 3248 _(
3232 3249 b'skipping already grafted revision %d:%s '
3233 3250 b'(%d:%s also has unknown origin %s)\n'
3234 3251 )
3235 3252 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3236 3253 )
3237 3254 else:
3238 3255 ui.warn(
3239 3256 _(
3240 3257 b'skipping already grafted revision %d:%s '
3241 3258 b'(%d:%s also has origin %d:%s)\n'
3242 3259 )
3243 3260 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3244 3261 )
3245 3262 revs.remove(ids[n])
3246 3263 elif ctx.hex() in ids:
3247 3264 r = ids[ctx.hex()]
3248 3265 if r in revs:
3249 3266 ui.warn(
3250 3267 _(
3251 3268 b'skipping already grafted revision %d:%s '
3252 3269 b'(was grafted from %d:%s)\n'
3253 3270 )
3254 3271 % (r, repo[r], rev, ctx)
3255 3272 )
3256 3273 revs.remove(r)
3257 3274 if not revs:
3258 3275 return -1
3259 3276
3260 3277 if opts.get(b'no_commit'):
3261 3278 statedata[b'no_commit'] = True
3262 3279 if opts.get(b'base'):
3263 3280 statedata[b'base'] = opts[b'base']
3264 3281 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3265 3282 desc = b'%d:%s "%s"' % (
3266 3283 ctx.rev(),
3267 3284 ctx,
3268 3285 ctx.description().split(b'\n', 1)[0],
3269 3286 )
3270 3287 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3271 3288 if names:
3272 3289 desc += b' (%s)' % b' '.join(names)
3273 3290 ui.status(_(b'grafting %s\n') % desc)
3274 3291 if opts.get(b'dry_run'):
3275 3292 continue
3276 3293
3277 3294 source = ctx.extra().get(b'source')
3278 3295 extra = {}
3279 3296 if source:
3280 3297 extra[b'source'] = source
3281 3298 extra[b'intermediate-source'] = ctx.hex()
3282 3299 else:
3283 3300 extra[b'source'] = ctx.hex()
3284 3301 user = ctx.user()
3285 3302 if opts.get(b'user'):
3286 3303 user = opts[b'user']
3287 3304 statedata[b'user'] = user
3288 3305 date = ctx.date()
3289 3306 if opts.get(b'date'):
3290 3307 date = opts[b'date']
3291 3308 statedata[b'date'] = date
3292 3309 message = ctx.description()
3293 3310 if opts.get(b'log'):
3294 3311 message += b'\n(grafted from %s)' % ctx.hex()
3295 3312 statedata[b'log'] = True
3296 3313
3297 3314 # we don't merge the first commit when continuing
3298 3315 if not cont:
3299 3316 # perform the graft merge with p1(rev) as 'ancestor'
3300 3317 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
3301 3318 base = ctx.p1() if basectx is None else basectx
3302 3319 with ui.configoverride(overrides, b'graft'):
3303 3320 stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3304 3321 # report any conflicts
3305 3322 if stats.unresolvedcount > 0:
3306 3323 # write out state for --continue
3307 3324 nodes = [repo[rev].hex() for rev in revs[pos:]]
3308 3325 statedata[b'nodes'] = nodes
3309 3326 stateversion = 1
3310 3327 graftstate.save(stateversion, statedata)
3311 3328 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3312 3329 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3313 3330 return 1
3314 3331 else:
3315 3332 cont = False
3316 3333
3317 3334 # commit if --no-commit is false
3318 3335 if not opts.get(b'no_commit'):
3319 3336 node = repo.commit(
3320 3337 text=message, user=user, date=date, extra=extra, editor=editor
3321 3338 )
3322 3339 if node is None:
3323 3340 ui.warn(
3324 3341 _(b'note: graft of %d:%s created no changes to commit\n')
3325 3342 % (ctx.rev(), ctx)
3326 3343 )
3327 3344 # checking that newnodes exist because old state files won't have it
3328 3345 elif statedata.get(b'newnodes') is not None:
3329 3346 nn = statedata[b'newnodes'] # type: List[bytes]
3330 3347 nn.append(node)
3331 3348
3332 3349 # remove state when we complete successfully
3333 3350 if not opts.get(b'dry_run'):
3334 3351 graftstate.delete()
3335 3352
3336 3353 return 0
3337 3354
3338 3355
3339 3356 def _stopgraft(ui, repo, graftstate):
3340 3357 """stop the interrupted graft"""
3341 3358 if not graftstate.exists():
3342 3359 raise error.StateError(_(b"no interrupted graft found"))
3343 3360 pctx = repo[b'.']
3344 3361 mergemod.clean_update(pctx)
3345 3362 graftstate.delete()
3346 3363 ui.status(_(b"stopped the interrupted graft\n"))
3347 3364 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3348 3365 return 0
3349 3366
3350 3367
3351 3368 statemod.addunfinished(
3352 3369 b'graft',
3353 3370 fname=b'graftstate',
3354 3371 clearable=True,
3355 3372 stopflag=True,
3356 3373 continueflag=True,
3357 3374 abortfunc=cmdutil.hgabortgraft,
3358 3375 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3359 3376 )
3360 3377
3361 3378
3362 3379 @command(
3363 3380 b'grep',
3364 3381 [
3365 3382 (b'0', b'print0', None, _(b'end fields with NUL')),
3366 3383 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3367 3384 (
3368 3385 b'',
3369 3386 b'diff',
3370 3387 None,
3371 3388 _(
3372 3389 b'search revision differences for when the pattern was added '
3373 3390 b'or removed'
3374 3391 ),
3375 3392 ),
3376 3393 (b'a', b'text', None, _(b'treat all files as text')),
3377 3394 (
3378 3395 b'f',
3379 3396 b'follow',
3380 3397 None,
3381 3398 _(
3382 3399 b'follow changeset history,'
3383 3400 b' or file history across copies and renames'
3384 3401 ),
3385 3402 ),
3386 3403 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3387 3404 (
3388 3405 b'l',
3389 3406 b'files-with-matches',
3390 3407 None,
3391 3408 _(b'print only filenames and revisions that match'),
3392 3409 ),
3393 3410 (b'n', b'line-number', None, _(b'print matching line numbers')),
3394 3411 (
3395 3412 b'r',
3396 3413 b'rev',
3397 3414 [],
3398 3415 _(b'search files changed within revision range'),
3399 3416 _(b'REV'),
3400 3417 ),
3401 3418 (
3402 3419 b'',
3403 3420 b'all-files',
3404 3421 None,
3405 3422 _(
3406 3423 b'include all files in the changeset while grepping (DEPRECATED)'
3407 3424 ),
3408 3425 ),
3409 3426 (b'u', b'user', None, _(b'list the author (long with -v)')),
3410 3427 (b'd', b'date', None, _(b'list the date (short with -q)')),
3411 3428 ]
3412 3429 + formatteropts
3413 3430 + walkopts,
3414 3431 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3415 3432 helpcategory=command.CATEGORY_FILE_CONTENTS,
3416 3433 inferrepo=True,
3417 3434 intents={INTENT_READONLY},
3418 3435 )
3419 3436 def grep(ui, repo, pattern, *pats, **opts):
3420 3437 """search for a pattern in specified files
3421 3438
3422 3439 Search the working directory or revision history for a regular
3423 3440 expression in the specified files for the entire repository.
3424 3441
3425 3442 By default, grep searches the repository files in the working
3426 3443 directory and prints the files where it finds a match. To specify
3427 3444 historical revisions instead of the working directory, use the
3428 3445 --rev flag.
3429 3446
3430 3447 To search instead historical revision differences that contains a
3431 3448 change in match status ("-" for a match that becomes a non-match,
3432 3449 or "+" for a non-match that becomes a match), use the --diff flag.
3433 3450
3434 3451 PATTERN can be any Python (roughly Perl-compatible) regular
3435 3452 expression.
3436 3453
3437 3454 If no FILEs are specified and the --rev flag isn't supplied, all
3438 3455 files in the working directory are searched. When using the --rev
3439 3456 flag and specifying FILEs, use the --follow argument to also
3440 3457 follow the specified FILEs across renames and copies.
3441 3458
3442 3459 .. container:: verbose
3443 3460
3444 3461 Template:
3445 3462
3446 3463 The following keywords are supported in addition to the common template
3447 3464 keywords and functions. See also :hg:`help templates`.
3448 3465
3449 3466 :change: String. Character denoting insertion ``+`` or removal ``-``.
3450 3467 Available if ``--diff`` is specified.
3451 3468 :lineno: Integer. Line number of the match.
3452 3469 :path: String. Repository-absolute path of the file.
3453 3470 :texts: List of text chunks.
3454 3471
3455 3472 And each entry of ``{texts}`` provides the following sub-keywords.
3456 3473
3457 3474 :matched: Boolean. True if the chunk matches the specified pattern.
3458 3475 :text: String. Chunk content.
3459 3476
3460 3477 See :hg:`help templates.operators` for the list expansion syntax.
3461 3478
3462 3479 Returns 0 if a match is found, 1 otherwise.
3463 3480
3464 3481 """
3465 3482 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3466 3483 opts = pycompat.byteskwargs(opts)
3467 3484 diff = opts.get(b'all') or opts.get(b'diff')
3468 3485 follow = opts.get(b'follow')
3469 3486 if opts.get(b'all_files') is None and not diff:
3470 3487 opts[b'all_files'] = True
3471 3488 plaingrep = (
3472 3489 opts.get(b'all_files')
3473 3490 and not opts.get(b'rev')
3474 3491 and not opts.get(b'follow')
3475 3492 )
3476 3493 all_files = opts.get(b'all_files')
3477 3494 if plaingrep:
3478 3495 opts[b'rev'] = [b'wdir()']
3479 3496
3480 3497 reflags = re.M
3481 3498 if opts.get(b'ignore_case'):
3482 3499 reflags |= re.I
3483 3500 try:
3484 3501 regexp = util.re.compile(pattern, reflags)
3485 3502 except re.error as inst:
3486 3503 ui.warn(
3487 3504 _(b"grep: invalid match pattern: %s\n")
3488 3505 % stringutil.forcebytestr(inst)
3489 3506 )
3490 3507 return 1
3491 3508 sep, eol = b':', b'\n'
3492 3509 if opts.get(b'print0'):
3493 3510 sep = eol = b'\0'
3494 3511
3495 3512 searcher = grepmod.grepsearcher(
3496 3513 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3497 3514 )
3498 3515
3499 3516 getfile = searcher._getfile
3500 3517
3501 3518 uipathfn = scmutil.getuipathfn(repo)
3502 3519
3503 3520 def display(fm, fn, ctx, pstates, states):
3504 3521 rev = scmutil.intrev(ctx)
3505 3522 if fm.isplain():
3506 3523 formatuser = ui.shortuser
3507 3524 else:
3508 3525 formatuser = pycompat.bytestr
3509 3526 if ui.quiet:
3510 3527 datefmt = b'%Y-%m-%d'
3511 3528 else:
3512 3529 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3513 3530 found = False
3514 3531
3515 3532 @util.cachefunc
3516 3533 def binary():
3517 3534 flog = getfile(fn)
3518 3535 try:
3519 3536 return stringutil.binary(flog.read(ctx.filenode(fn)))
3520 3537 except error.WdirUnsupported:
3521 3538 return ctx[fn].isbinary()
3522 3539
3523 3540 fieldnamemap = {b'linenumber': b'lineno'}
3524 3541 if diff:
3525 3542 iter = grepmod.difflinestates(pstates, states)
3526 3543 else:
3527 3544 iter = [(b'', l) for l in states]
3528 3545 for change, l in iter:
3529 3546 fm.startitem()
3530 3547 fm.context(ctx=ctx)
3531 3548 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3532 3549 fm.plain(uipathfn(fn), label=b'grep.filename')
3533 3550
3534 3551 cols = [
3535 3552 (b'rev', b'%d', rev, not plaingrep, b''),
3536 3553 (
3537 3554 b'linenumber',
3538 3555 b'%d',
3539 3556 l.linenum,
3540 3557 opts.get(b'line_number'),
3541 3558 b'',
3542 3559 ),
3543 3560 ]
3544 3561 if diff:
3545 3562 cols.append(
3546 3563 (
3547 3564 b'change',
3548 3565 b'%s',
3549 3566 change,
3550 3567 True,
3551 3568 b'grep.inserted '
3552 3569 if change == b'+'
3553 3570 else b'grep.deleted ',
3554 3571 )
3555 3572 )
3556 3573 cols.extend(
3557 3574 [
3558 3575 (
3559 3576 b'user',
3560 3577 b'%s',
3561 3578 formatuser(ctx.user()),
3562 3579 opts.get(b'user'),
3563 3580 b'',
3564 3581 ),
3565 3582 (
3566 3583 b'date',
3567 3584 b'%s',
3568 3585 fm.formatdate(ctx.date(), datefmt),
3569 3586 opts.get(b'date'),
3570 3587 b'',
3571 3588 ),
3572 3589 ]
3573 3590 )
3574 3591 for name, fmt, data, cond, extra_label in cols:
3575 3592 if cond:
3576 3593 fm.plain(sep, label=b'grep.sep')
3577 3594 field = fieldnamemap.get(name, name)
3578 3595 label = extra_label + (b'grep.%s' % name)
3579 3596 fm.condwrite(cond, field, fmt, data, label=label)
3580 3597 if not opts.get(b'files_with_matches'):
3581 3598 fm.plain(sep, label=b'grep.sep')
3582 3599 if not opts.get(b'text') and binary():
3583 3600 fm.plain(_(b" Binary file matches"))
3584 3601 else:
3585 3602 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3586 3603 fm.plain(eol)
3587 3604 found = True
3588 3605 if opts.get(b'files_with_matches'):
3589 3606 break
3590 3607 return found
3591 3608
3592 3609 def displaymatches(fm, l):
3593 3610 p = 0
3594 3611 for s, e in l.findpos(regexp):
3595 3612 if p < s:
3596 3613 fm.startitem()
3597 3614 fm.write(b'text', b'%s', l.line[p:s])
3598 3615 fm.data(matched=False)
3599 3616 fm.startitem()
3600 3617 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3601 3618 fm.data(matched=True)
3602 3619 p = e
3603 3620 if p < len(l.line):
3604 3621 fm.startitem()
3605 3622 fm.write(b'text', b'%s', l.line[p:])
3606 3623 fm.data(matched=False)
3607 3624 fm.end()
3608 3625
3609 3626 found = False
3610 3627
3611 3628 wopts = logcmdutil.walkopts(
3612 3629 pats=pats,
3613 3630 opts=opts,
3614 3631 revspec=opts[b'rev'],
3615 3632 include_pats=opts[b'include'],
3616 3633 exclude_pats=opts[b'exclude'],
3617 3634 follow=follow,
3618 3635 force_changelog_traversal=all_files,
3619 3636 filter_revisions_by_pats=not all_files,
3620 3637 )
3621 3638 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3622 3639
3623 3640 ui.pager(b'grep')
3624 3641 fm = ui.formatter(b'grep', opts)
3625 3642 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3626 3643 r = display(fm, fn, ctx, pstates, states)
3627 3644 found = found or r
3628 3645 if r and not diff and not all_files:
3629 3646 searcher.skipfile(fn, ctx.rev())
3630 3647 fm.end()
3631 3648
3632 3649 return not found
3633 3650
3634 3651
3635 3652 @command(
3636 3653 b'heads',
3637 3654 [
3638 3655 (
3639 3656 b'r',
3640 3657 b'rev',
3641 3658 b'',
3642 3659 _(b'show only heads which are descendants of STARTREV'),
3643 3660 _(b'STARTREV'),
3644 3661 ),
3645 3662 (b't', b'topo', False, _(b'show topological heads only')),
3646 3663 (
3647 3664 b'a',
3648 3665 b'active',
3649 3666 False,
3650 3667 _(b'show active branchheads only (DEPRECATED)'),
3651 3668 ),
3652 3669 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3653 3670 ]
3654 3671 + templateopts,
3655 3672 _(b'[-ct] [-r STARTREV] [REV]...'),
3656 3673 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3657 3674 intents={INTENT_READONLY},
3658 3675 )
3659 3676 def heads(ui, repo, *branchrevs, **opts):
3660 3677 """show branch heads
3661 3678
3662 3679 With no arguments, show all open branch heads in the repository.
3663 3680 Branch heads are changesets that have no descendants on the
3664 3681 same branch. They are where development generally takes place and
3665 3682 are the usual targets for update and merge operations.
3666 3683
3667 3684 If one or more REVs are given, only open branch heads on the
3668 3685 branches associated with the specified changesets are shown. This
3669 3686 means that you can use :hg:`heads .` to see the heads on the
3670 3687 currently checked-out branch.
3671 3688
3672 3689 If -c/--closed is specified, also show branch heads marked closed
3673 3690 (see :hg:`commit --close-branch`).
3674 3691
3675 3692 If STARTREV is specified, only those heads that are descendants of
3676 3693 STARTREV will be displayed.
3677 3694
3678 3695 If -t/--topo is specified, named branch mechanics will be ignored and only
3679 3696 topological heads (changesets with no children) will be shown.
3680 3697
3681 3698 Returns 0 if matching heads are found, 1 if not.
3682 3699 """
3683 3700
3684 3701 opts = pycompat.byteskwargs(opts)
3685 3702 start = None
3686 3703 rev = opts.get(b'rev')
3687 3704 if rev:
3688 3705 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3689 3706 start = scmutil.revsingle(repo, rev, None).node()
3690 3707
3691 3708 if opts.get(b'topo'):
3692 3709 heads = [repo[h] for h in repo.heads(start)]
3693 3710 else:
3694 3711 heads = []
3695 3712 for branch in repo.branchmap():
3696 3713 heads += repo.branchheads(branch, start, opts.get(b'closed'))
3697 3714 heads = [repo[h] for h in heads]
3698 3715
3699 3716 if branchrevs:
3700 3717 branches = {
3701 3718 repo[r].branch() for r in scmutil.revrange(repo, branchrevs)
3702 3719 }
3703 3720 heads = [h for h in heads if h.branch() in branches]
3704 3721
3705 3722 if opts.get(b'active') and branchrevs:
3706 3723 dagheads = repo.heads(start)
3707 3724 heads = [h for h in heads if h.node() in dagheads]
3708 3725
3709 3726 if branchrevs:
3710 3727 haveheads = {h.branch() for h in heads}
3711 3728 if branches - haveheads:
3712 3729 headless = b', '.join(b for b in branches - haveheads)
3713 3730 msg = _(b'no open branch heads found on branches %s')
3714 3731 if opts.get(b'rev'):
3715 3732 msg += _(b' (started at %s)') % opts[b'rev']
3716 3733 ui.warn((msg + b'\n') % headless)
3717 3734
3718 3735 if not heads:
3719 3736 return 1
3720 3737
3721 3738 ui.pager(b'heads')
3722 3739 heads = sorted(heads, key=lambda x: -(x.rev()))
3723 3740 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3724 3741 for ctx in heads:
3725 3742 displayer.show(ctx)
3726 3743 displayer.close()
3727 3744
3728 3745
3729 3746 @command(
3730 3747 b'help',
3731 3748 [
3732 3749 (b'e', b'extension', None, _(b'show only help for extensions')),
3733 3750 (b'c', b'command', None, _(b'show only help for commands')),
3734 3751 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3735 3752 (
3736 3753 b's',
3737 3754 b'system',
3738 3755 [],
3739 3756 _(b'show help for specific platform(s)'),
3740 3757 _(b'PLATFORM'),
3741 3758 ),
3742 3759 ],
3743 3760 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3744 3761 helpcategory=command.CATEGORY_HELP,
3745 3762 norepo=True,
3746 3763 intents={INTENT_READONLY},
3747 3764 )
3748 3765 def help_(ui, name=None, **opts):
3749 3766 """show help for a given topic or a help overview
3750 3767
3751 3768 With no arguments, print a list of commands with short help messages.
3752 3769
3753 3770 Given a topic, extension, or command name, print help for that
3754 3771 topic.
3755 3772
3756 3773 Returns 0 if successful.
3757 3774 """
3758 3775
3759 3776 keep = opts.get('system') or []
3760 3777 if len(keep) == 0:
3761 3778 if pycompat.sysplatform.startswith(b'win'):
3762 3779 keep.append(b'windows')
3763 3780 elif pycompat.sysplatform == b'OpenVMS':
3764 3781 keep.append(b'vms')
3765 3782 elif pycompat.sysplatform == b'plan9':
3766 3783 keep.append(b'plan9')
3767 3784 else:
3768 3785 keep.append(b'unix')
3769 3786 keep.append(pycompat.sysplatform.lower())
3770 3787 if ui.verbose:
3771 3788 keep.append(b'verbose')
3772 3789
3773 3790 commands = sys.modules[__name__]
3774 3791 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3775 3792 ui.pager(b'help')
3776 3793 ui.write(formatted)
3777 3794
3778 3795
3779 3796 @command(
3780 3797 b'identify|id',
3781 3798 [
3782 3799 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3783 3800 (b'n', b'num', None, _(b'show local revision number')),
3784 3801 (b'i', b'id', None, _(b'show global revision id')),
3785 3802 (b'b', b'branch', None, _(b'show branch')),
3786 3803 (b't', b'tags', None, _(b'show tags')),
3787 3804 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3788 3805 ]
3789 3806 + remoteopts
3790 3807 + formatteropts,
3791 3808 _(b'[-nibtB] [-r REV] [SOURCE]'),
3792 3809 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3793 3810 optionalrepo=True,
3794 3811 intents={INTENT_READONLY},
3795 3812 )
3796 3813 def identify(
3797 3814 ui,
3798 3815 repo,
3799 3816 source=None,
3800 3817 rev=None,
3801 3818 num=None,
3802 3819 id=None,
3803 3820 branch=None,
3804 3821 tags=None,
3805 3822 bookmarks=None,
3806 3823 **opts
3807 3824 ):
3808 3825 """identify the working directory or specified revision
3809 3826
3810 3827 Print a summary identifying the repository state at REV using one or
3811 3828 two parent hash identifiers, followed by a "+" if the working
3812 3829 directory has uncommitted changes, the branch name (if not default),
3813 3830 a list of tags, and a list of bookmarks.
3814 3831
3815 3832 When REV is not given, print a summary of the current state of the
3816 3833 repository including the working directory. Specify -r. to get information
3817 3834 of the working directory parent without scanning uncommitted changes.
3818 3835
3819 3836 Specifying a path to a repository root or Mercurial bundle will
3820 3837 cause lookup to operate on that repository/bundle.
3821 3838
3822 3839 .. container:: verbose
3823 3840
3824 3841 Template:
3825 3842
3826 3843 The following keywords are supported in addition to the common template
3827 3844 keywords and functions. See also :hg:`help templates`.
3828 3845
3829 3846 :dirty: String. Character ``+`` denoting if the working directory has
3830 3847 uncommitted changes.
3831 3848 :id: String. One or two nodes, optionally followed by ``+``.
3832 3849 :parents: List of strings. Parent nodes of the changeset.
3833 3850
3834 3851 Examples:
3835 3852
3836 3853 - generate a build identifier for the working directory::
3837 3854
3838 3855 hg id --id > build-id.dat
3839 3856
3840 3857 - find the revision corresponding to a tag::
3841 3858
3842 3859 hg id -n -r 1.3
3843 3860
3844 3861 - check the most recent revision of a remote repository::
3845 3862
3846 3863 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3847 3864
3848 3865 See :hg:`log` for generating more information about specific revisions,
3849 3866 including full hash identifiers.
3850 3867
3851 3868 Returns 0 if successful.
3852 3869 """
3853 3870
3854 3871 opts = pycompat.byteskwargs(opts)
3855 3872 if not repo and not source:
3856 3873 raise error.InputError(
3857 3874 _(b"there is no Mercurial repository here (.hg not found)")
3858 3875 )
3859 3876
3860 3877 default = not (num or id or branch or tags or bookmarks)
3861 3878 output = []
3862 3879 revs = []
3863 3880
3864 3881 peer = None
3865 3882 try:
3866 3883 if source:
3867 3884 source, branches = urlutil.get_unique_pull_path(
3868 3885 b'identify', repo, ui, source
3869 3886 )
3870 3887 # only pass ui when no repo
3871 3888 peer = hg.peer(repo or ui, opts, source)
3872 3889 repo = peer.local()
3873 3890 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3874 3891
3875 3892 fm = ui.formatter(b'identify', opts)
3876 3893 fm.startitem()
3877 3894
3878 3895 if not repo:
3879 3896 if num or branch or tags:
3880 3897 raise error.InputError(
3881 3898 _(b"can't query remote revision number, branch, or tags")
3882 3899 )
3883 3900 if not rev and revs:
3884 3901 rev = revs[0]
3885 3902 if not rev:
3886 3903 rev = b"tip"
3887 3904
3888 3905 remoterev = peer.lookup(rev)
3889 3906 hexrev = fm.hexfunc(remoterev)
3890 3907 if default or id:
3891 3908 output = [hexrev]
3892 3909 fm.data(id=hexrev)
3893 3910
3894 3911 @util.cachefunc
3895 3912 def getbms():
3896 3913 bms = []
3897 3914
3898 3915 if b'bookmarks' in peer.listkeys(b'namespaces'):
3899 3916 hexremoterev = hex(remoterev)
3900 3917 bms = [
3901 3918 bm
3902 3919 for bm, bmr in pycompat.iteritems(
3903 3920 peer.listkeys(b'bookmarks')
3904 3921 )
3905 3922 if bmr == hexremoterev
3906 3923 ]
3907 3924
3908 3925 return sorted(bms)
3909 3926
3910 3927 if fm.isplain():
3911 3928 if bookmarks:
3912 3929 output.extend(getbms())
3913 3930 elif default and not ui.quiet:
3914 3931 # multiple bookmarks for a single parent separated by '/'
3915 3932 bm = b'/'.join(getbms())
3916 3933 if bm:
3917 3934 output.append(bm)
3918 3935 else:
3919 3936 fm.data(node=hex(remoterev))
3920 3937 if bookmarks or b'bookmarks' in fm.datahint():
3921 3938 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3922 3939 else:
3923 3940 if rev:
3924 3941 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3925 3942 ctx = scmutil.revsingle(repo, rev, None)
3926 3943
3927 3944 if ctx.rev() is None:
3928 3945 ctx = repo[None]
3929 3946 parents = ctx.parents()
3930 3947 taglist = []
3931 3948 for p in parents:
3932 3949 taglist.extend(p.tags())
3933 3950
3934 3951 dirty = b""
3935 3952 if ctx.dirty(missing=True, merge=False, branch=False):
3936 3953 dirty = b'+'
3937 3954 fm.data(dirty=dirty)
3938 3955
3939 3956 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3940 3957 if default or id:
3941 3958 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3942 3959 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3943 3960
3944 3961 if num:
3945 3962 numoutput = [b"%d" % p.rev() for p in parents]
3946 3963 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3947 3964
3948 3965 fm.data(
3949 3966 parents=fm.formatlist(
3950 3967 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3951 3968 )
3952 3969 )
3953 3970 else:
3954 3971 hexoutput = fm.hexfunc(ctx.node())
3955 3972 if default or id:
3956 3973 output = [hexoutput]
3957 3974 fm.data(id=hexoutput)
3958 3975
3959 3976 if num:
3960 3977 output.append(pycompat.bytestr(ctx.rev()))
3961 3978 taglist = ctx.tags()
3962 3979
3963 3980 if default and not ui.quiet:
3964 3981 b = ctx.branch()
3965 3982 if b != b'default':
3966 3983 output.append(b"(%s)" % b)
3967 3984
3968 3985 # multiple tags for a single parent separated by '/'
3969 3986 t = b'/'.join(taglist)
3970 3987 if t:
3971 3988 output.append(t)
3972 3989
3973 3990 # multiple bookmarks for a single parent separated by '/'
3974 3991 bm = b'/'.join(ctx.bookmarks())
3975 3992 if bm:
3976 3993 output.append(bm)
3977 3994 else:
3978 3995 if branch:
3979 3996 output.append(ctx.branch())
3980 3997
3981 3998 if tags:
3982 3999 output.extend(taglist)
3983 4000
3984 4001 if bookmarks:
3985 4002 output.extend(ctx.bookmarks())
3986 4003
3987 4004 fm.data(node=ctx.hex())
3988 4005 fm.data(branch=ctx.branch())
3989 4006 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
3990 4007 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
3991 4008 fm.context(ctx=ctx)
3992 4009
3993 4010 fm.plain(b"%s\n" % b' '.join(output))
3994 4011 fm.end()
3995 4012 finally:
3996 4013 if peer:
3997 4014 peer.close()
3998 4015
3999 4016
4000 4017 @command(
4001 4018 b'import|patch',
4002 4019 [
4003 4020 (
4004 4021 b'p',
4005 4022 b'strip',
4006 4023 1,
4007 4024 _(
4008 4025 b'directory strip option for patch. This has the same '
4009 4026 b'meaning as the corresponding patch option'
4010 4027 ),
4011 4028 _(b'NUM'),
4012 4029 ),
4013 4030 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4014 4031 (b'', b'secret', None, _(b'use the secret phase for committing')),
4015 4032 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4016 4033 (
4017 4034 b'f',
4018 4035 b'force',
4019 4036 None,
4020 4037 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4021 4038 ),
4022 4039 (
4023 4040 b'',
4024 4041 b'no-commit',
4025 4042 None,
4026 4043 _(b"don't commit, just update the working directory"),
4027 4044 ),
4028 4045 (
4029 4046 b'',
4030 4047 b'bypass',
4031 4048 None,
4032 4049 _(b"apply patch without touching the working directory"),
4033 4050 ),
4034 4051 (b'', b'partial', None, _(b'commit even if some hunks fail')),
4035 4052 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4036 4053 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4037 4054 (
4038 4055 b'',
4039 4056 b'import-branch',
4040 4057 None,
4041 4058 _(b'use any branch information in patch (implied by --exact)'),
4042 4059 ),
4043 4060 ]
4044 4061 + commitopts
4045 4062 + commitopts2
4046 4063 + similarityopts,
4047 4064 _(b'[OPTION]... PATCH...'),
4048 4065 helpcategory=command.CATEGORY_IMPORT_EXPORT,
4049 4066 )
4050 4067 def import_(ui, repo, patch1=None, *patches, **opts):
4051 4068 """import an ordered set of patches
4052 4069
4053 4070 Import a list of patches and commit them individually (unless
4054 4071 --no-commit is specified).
4055 4072
4056 4073 To read a patch from standard input (stdin), use "-" as the patch
4057 4074 name. If a URL is specified, the patch will be downloaded from
4058 4075 there.
4059 4076
4060 4077 Import first applies changes to the working directory (unless
4061 4078 --bypass is specified), import will abort if there are outstanding
4062 4079 changes.
4063 4080
4064 4081 Use --bypass to apply and commit patches directly to the
4065 4082 repository, without affecting the working directory. Without
4066 4083 --exact, patches will be applied on top of the working directory
4067 4084 parent revision.
4068 4085
4069 4086 You can import a patch straight from a mail message. Even patches
4070 4087 as attachments work (to use the body part, it must have type
4071 4088 text/plain or text/x-patch). From and Subject headers of email
4072 4089 message are used as default committer and commit message. All
4073 4090 text/plain body parts before first diff are added to the commit
4074 4091 message.
4075 4092
4076 4093 If the imported patch was generated by :hg:`export`, user and
4077 4094 description from patch override values from message headers and
4078 4095 body. Values given on command line with -m/--message and -u/--user
4079 4096 override these.
4080 4097
4081 4098 If --exact is specified, import will set the working directory to
4082 4099 the parent of each patch before applying it, and will abort if the
4083 4100 resulting changeset has a different ID than the one recorded in
4084 4101 the patch. This will guard against various ways that portable
4085 4102 patch formats and mail systems might fail to transfer Mercurial
4086 4103 data or metadata. See :hg:`bundle` for lossless transmission.
4087 4104
4088 4105 Use --partial to ensure a changeset will be created from the patch
4089 4106 even if some hunks fail to apply. Hunks that fail to apply will be
4090 4107 written to a <target-file>.rej file. Conflicts can then be resolved
4091 4108 by hand before :hg:`commit --amend` is run to update the created
4092 4109 changeset. This flag exists to let people import patches that
4093 4110 partially apply without losing the associated metadata (author,
4094 4111 date, description, ...).
4095 4112
4096 4113 .. note::
4097 4114
4098 4115 When no hunks apply cleanly, :hg:`import --partial` will create
4099 4116 an empty changeset, importing only the patch metadata.
4100 4117
4101 4118 With -s/--similarity, hg will attempt to discover renames and
4102 4119 copies in the patch in the same way as :hg:`addremove`.
4103 4120
4104 4121 It is possible to use external patch programs to perform the patch
4105 4122 by setting the ``ui.patch`` configuration option. For the default
4106 4123 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4107 4124 See :hg:`help config` for more information about configuration
4108 4125 files and how to use these options.
4109 4126
4110 4127 See :hg:`help dates` for a list of formats valid for -d/--date.
4111 4128
4112 4129 .. container:: verbose
4113 4130
4114 4131 Examples:
4115 4132
4116 4133 - import a traditional patch from a website and detect renames::
4117 4134
4118 4135 hg import -s 80 http://example.com/bugfix.patch
4119 4136
4120 4137 - import a changeset from an hgweb server::
4121 4138
4122 4139 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4123 4140
4124 4141 - import all the patches in an Unix-style mbox::
4125 4142
4126 4143 hg import incoming-patches.mbox
4127 4144
4128 4145 - import patches from stdin::
4129 4146
4130 4147 hg import -
4131 4148
4132 4149 - attempt to exactly restore an exported changeset (not always
4133 4150 possible)::
4134 4151
4135 4152 hg import --exact proposed-fix.patch
4136 4153
4137 4154 - use an external tool to apply a patch which is too fuzzy for
4138 4155 the default internal tool.
4139 4156
4140 4157 hg import --config ui.patch="patch --merge" fuzzy.patch
4141 4158
4142 4159 - change the default fuzzing from 2 to a less strict 7
4143 4160
4144 4161 hg import --config ui.fuzz=7 fuzz.patch
4145 4162
4146 4163 Returns 0 on success, 1 on partial success (see --partial).
4147 4164 """
4148 4165
4149 4166 cmdutil.check_incompatible_arguments(
4150 4167 opts, 'no_commit', ['bypass', 'secret']
4151 4168 )
4152 4169 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
4153 4170 opts = pycompat.byteskwargs(opts)
4154 4171 if not patch1:
4155 4172 raise error.InputError(_(b'need at least one patch to import'))
4156 4173
4157 4174 patches = (patch1,) + patches
4158 4175
4159 4176 date = opts.get(b'date')
4160 4177 if date:
4161 4178 opts[b'date'] = dateutil.parsedate(date)
4162 4179
4163 4180 exact = opts.get(b'exact')
4164 4181 update = not opts.get(b'bypass')
4165 4182 try:
4166 4183 sim = float(opts.get(b'similarity') or 0)
4167 4184 except ValueError:
4168 4185 raise error.InputError(_(b'similarity must be a number'))
4169 4186 if sim < 0 or sim > 100:
4170 4187 raise error.InputError(_(b'similarity must be between 0 and 100'))
4171 4188 if sim and not update:
4172 4189 raise error.InputError(_(b'cannot use --similarity with --bypass'))
4173 4190
4174 4191 base = opts[b"base"]
4175 4192 msgs = []
4176 4193 ret = 0
4177 4194
4178 4195 with repo.wlock():
4179 4196 if update:
4180 4197 cmdutil.checkunfinished(repo)
4181 4198 if exact or not opts.get(b'force'):
4182 4199 cmdutil.bailifchanged(repo)
4183 4200
4184 4201 if not opts.get(b'no_commit'):
4185 4202 lock = repo.lock
4186 4203 tr = lambda: repo.transaction(b'import')
4187 4204 dsguard = util.nullcontextmanager
4188 4205 else:
4189 4206 lock = util.nullcontextmanager
4190 4207 tr = util.nullcontextmanager
4191 4208 dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4192 4209 with lock(), tr(), dsguard():
4193 4210 parents = repo[None].parents()
4194 4211 for patchurl in patches:
4195 4212 if patchurl == b'-':
4196 4213 ui.status(_(b'applying patch from stdin\n'))
4197 4214 patchfile = ui.fin
4198 4215 patchurl = b'stdin' # for error message
4199 4216 else:
4200 4217 patchurl = os.path.join(base, patchurl)
4201 4218 ui.status(_(b'applying %s\n') % patchurl)
4202 4219 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4203 4220
4204 4221 haspatch = False
4205 4222 for hunk in patch.split(patchfile):
4206 4223 with patch.extract(ui, hunk) as patchdata:
4207 4224 msg, node, rej = cmdutil.tryimportone(
4208 4225 ui, repo, patchdata, parents, opts, msgs, hg.clean
4209 4226 )
4210 4227 if msg:
4211 4228 haspatch = True
4212 4229 ui.note(msg + b'\n')
4213 4230 if update or exact:
4214 4231 parents = repo[None].parents()
4215 4232 else:
4216 4233 parents = [repo[node]]
4217 4234 if rej:
4218 4235 ui.write_err(_(b"patch applied partially\n"))
4219 4236 ui.write_err(
4220 4237 _(
4221 4238 b"(fix the .rej files and run "
4222 4239 b"`hg commit --amend`)\n"
4223 4240 )
4224 4241 )
4225 4242 ret = 1
4226 4243 break
4227 4244
4228 4245 if not haspatch:
4229 4246 raise error.InputError(_(b'%s: no diffs found') % patchurl)
4230 4247
4231 4248 if msgs:
4232 4249 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4233 4250 return ret
4234 4251
4235 4252
4236 4253 @command(
4237 4254 b'incoming|in',
4238 4255 [
4239 4256 (
4240 4257 b'f',
4241 4258 b'force',
4242 4259 None,
4243 4260 _(b'run even if remote repository is unrelated'),
4244 4261 ),
4245 4262 (b'n', b'newest-first', None, _(b'show newest record first')),
4246 4263 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4247 4264 (
4248 4265 b'r',
4249 4266 b'rev',
4250 4267 [],
4251 4268 _(b'a remote changeset intended to be added'),
4252 4269 _(b'REV'),
4253 4270 ),
4254 4271 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4255 4272 (
4256 4273 b'b',
4257 4274 b'branch',
4258 4275 [],
4259 4276 _(b'a specific branch you would like to pull'),
4260 4277 _(b'BRANCH'),
4261 4278 ),
4262 4279 ]
4263 4280 + logopts
4264 4281 + remoteopts
4265 4282 + subrepoopts,
4266 4283 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4267 4284 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4268 4285 )
4269 4286 def incoming(ui, repo, source=b"default", **opts):
4270 4287 """show new changesets found in source
4271 4288
4272 4289 Show new changesets found in the specified path/URL or the default
4273 4290 pull location. These are the changesets that would have been pulled
4274 4291 by :hg:`pull` at the time you issued this command.
4275 4292
4276 4293 See pull for valid source format details.
4277 4294
4278 4295 .. container:: verbose
4279 4296
4280 4297 With -B/--bookmarks, the result of bookmark comparison between
4281 4298 local and remote repositories is displayed. With -v/--verbose,
4282 4299 status is also displayed for each bookmark like below::
4283 4300
4284 4301 BM1 01234567890a added
4285 4302 BM2 1234567890ab advanced
4286 4303 BM3 234567890abc diverged
4287 4304 BM4 34567890abcd changed
4288 4305
4289 4306 The action taken locally when pulling depends on the
4290 4307 status of each bookmark:
4291 4308
4292 4309 :``added``: pull will create it
4293 4310 :``advanced``: pull will update it
4294 4311 :``diverged``: pull will create a divergent bookmark
4295 4312 :``changed``: result depends on remote changesets
4296 4313
4297 4314 From the point of view of pulling behavior, bookmark
4298 4315 existing only in the remote repository are treated as ``added``,
4299 4316 even if it is in fact locally deleted.
4300 4317
4301 4318 .. container:: verbose
4302 4319
4303 4320 For remote repository, using --bundle avoids downloading the
4304 4321 changesets twice if the incoming is followed by a pull.
4305 4322
4306 4323 Examples:
4307 4324
4308 4325 - show incoming changes with patches and full description::
4309 4326
4310 4327 hg incoming -vp
4311 4328
4312 4329 - show incoming changes excluding merges, store a bundle::
4313 4330
4314 4331 hg in -vpM --bundle incoming.hg
4315 4332 hg pull incoming.hg
4316 4333
4317 4334 - briefly list changes inside a bundle::
4318 4335
4319 4336 hg in changes.hg -T "{desc|firstline}\\n"
4320 4337
4321 4338 Returns 0 if there are incoming changes, 1 otherwise.
4322 4339 """
4323 4340 opts = pycompat.byteskwargs(opts)
4324 4341 if opts.get(b'graph'):
4325 4342 logcmdutil.checkunsupportedgraphflags([], opts)
4326 4343
4327 4344 def display(other, chlist, displayer):
4328 4345 revdag = logcmdutil.graphrevs(other, chlist, opts)
4329 4346 logcmdutil.displaygraph(
4330 4347 ui, repo, revdag, displayer, graphmod.asciiedges
4331 4348 )
4332 4349
4333 4350 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4334 4351 return 0
4335 4352
4336 4353 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4337 4354
4338 4355 if opts.get(b'bookmarks'):
4339 4356 srcs = urlutil.get_pull_paths(repo, ui, [source], opts.get(b'branch'))
4340 4357 for source, branches in srcs:
4341 4358 other = hg.peer(repo, opts, source)
4342 4359 try:
4343 4360 if b'bookmarks' not in other.listkeys(b'namespaces'):
4344 4361 ui.warn(_(b"remote doesn't support bookmarks\n"))
4345 4362 return 0
4346 4363 ui.pager(b'incoming')
4347 4364 ui.status(
4348 4365 _(b'comparing with %s\n') % urlutil.hidepassword(source)
4349 4366 )
4350 4367 return bookmarks.incoming(ui, repo, other)
4351 4368 finally:
4352 4369 other.close()
4353 4370
4354 4371 return hg.incoming(ui, repo, source, opts)
4355 4372
4356 4373
4357 4374 @command(
4358 4375 b'init',
4359 4376 remoteopts,
4360 4377 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4361 4378 helpcategory=command.CATEGORY_REPO_CREATION,
4362 4379 helpbasic=True,
4363 4380 norepo=True,
4364 4381 )
4365 4382 def init(ui, dest=b".", **opts):
4366 4383 """create a new repository in the given directory
4367 4384
4368 4385 Initialize a new repository in the given directory. If the given
4369 4386 directory does not exist, it will be created.
4370 4387
4371 4388 If no directory is given, the current directory is used.
4372 4389
4373 4390 It is possible to specify an ``ssh://`` URL as the destination.
4374 4391 See :hg:`help urls` for more information.
4375 4392
4376 4393 Returns 0 on success.
4377 4394 """
4378 4395 opts = pycompat.byteskwargs(opts)
4379 4396 path = urlutil.get_clone_path(ui, dest)[1]
4380 4397 peer = hg.peer(ui, opts, path, create=True)
4381 4398 peer.close()
4382 4399
4383 4400
4384 4401 @command(
4385 4402 b'locate',
4386 4403 [
4387 4404 (
4388 4405 b'r',
4389 4406 b'rev',
4390 4407 b'',
4391 4408 _(b'search the repository as it is in REV'),
4392 4409 _(b'REV'),
4393 4410 ),
4394 4411 (
4395 4412 b'0',
4396 4413 b'print0',
4397 4414 None,
4398 4415 _(b'end filenames with NUL, for use with xargs'),
4399 4416 ),
4400 4417 (
4401 4418 b'f',
4402 4419 b'fullpath',
4403 4420 None,
4404 4421 _(b'print complete paths from the filesystem root'),
4405 4422 ),
4406 4423 ]
4407 4424 + walkopts,
4408 4425 _(b'[OPTION]... [PATTERN]...'),
4409 4426 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4410 4427 )
4411 4428 def locate(ui, repo, *pats, **opts):
4412 4429 """locate files matching specific patterns (DEPRECATED)
4413 4430
4414 4431 Print files under Mercurial control in the working directory whose
4415 4432 names match the given patterns.
4416 4433
4417 4434 By default, this command searches all directories in the working
4418 4435 directory. To search just the current directory and its
4419 4436 subdirectories, use "--include .".
4420 4437
4421 4438 If no patterns are given to match, this command prints the names
4422 4439 of all files under Mercurial control in the working directory.
4423 4440
4424 4441 If you want to feed the output of this command into the "xargs"
4425 4442 command, use the -0 option to both this command and "xargs". This
4426 4443 will avoid the problem of "xargs" treating single filenames that
4427 4444 contain whitespace as multiple filenames.
4428 4445
4429 4446 See :hg:`help files` for a more versatile command.
4430 4447
4431 4448 Returns 0 if a match is found, 1 otherwise.
4432 4449 """
4433 4450 opts = pycompat.byteskwargs(opts)
4434 4451 if opts.get(b'print0'):
4435 4452 end = b'\0'
4436 4453 else:
4437 4454 end = b'\n'
4438 4455 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
4439 4456
4440 4457 ret = 1
4441 4458 m = scmutil.match(
4442 4459 ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4443 4460 )
4444 4461
4445 4462 ui.pager(b'locate')
4446 4463 if ctx.rev() is None:
4447 4464 # When run on the working copy, "locate" includes removed files, so
4448 4465 # we get the list of files from the dirstate.
4449 4466 filesgen = sorted(repo.dirstate.matches(m))
4450 4467 else:
4451 4468 filesgen = ctx.matches(m)
4452 4469 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4453 4470 for abs in filesgen:
4454 4471 if opts.get(b'fullpath'):
4455 4472 ui.write(repo.wjoin(abs), end)
4456 4473 else:
4457 4474 ui.write(uipathfn(abs), end)
4458 4475 ret = 0
4459 4476
4460 4477 return ret
4461 4478
4462 4479
4463 4480 @command(
4464 4481 b'log|history',
4465 4482 [
4466 4483 (
4467 4484 b'f',
4468 4485 b'follow',
4469 4486 None,
4470 4487 _(
4471 4488 b'follow changeset history, or file history across copies and renames'
4472 4489 ),
4473 4490 ),
4474 4491 (
4475 4492 b'',
4476 4493 b'follow-first',
4477 4494 None,
4478 4495 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4479 4496 ),
4480 4497 (
4481 4498 b'd',
4482 4499 b'date',
4483 4500 b'',
4484 4501 _(b'show revisions matching date spec'),
4485 4502 _(b'DATE'),
4486 4503 ),
4487 4504 (b'C', b'copies', None, _(b'show copied files')),
4488 4505 (
4489 4506 b'k',
4490 4507 b'keyword',
4491 4508 [],
4492 4509 _(b'do case-insensitive search for a given text'),
4493 4510 _(b'TEXT'),
4494 4511 ),
4495 4512 (
4496 4513 b'r',
4497 4514 b'rev',
4498 4515 [],
4499 4516 _(b'revisions to select or follow from'),
4500 4517 _(b'REV'),
4501 4518 ),
4502 4519 (
4503 4520 b'L',
4504 4521 b'line-range',
4505 4522 [],
4506 4523 _(b'follow line range of specified file (EXPERIMENTAL)'),
4507 4524 _(b'FILE,RANGE'),
4508 4525 ),
4509 4526 (
4510 4527 b'',
4511 4528 b'removed',
4512 4529 None,
4513 4530 _(b'include revisions where files were removed'),
4514 4531 ),
4515 4532 (
4516 4533 b'm',
4517 4534 b'only-merges',
4518 4535 None,
4519 4536 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4520 4537 ),
4521 4538 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4522 4539 (
4523 4540 b'',
4524 4541 b'only-branch',
4525 4542 [],
4526 4543 _(
4527 4544 b'show only changesets within the given named branch (DEPRECATED)'
4528 4545 ),
4529 4546 _(b'BRANCH'),
4530 4547 ),
4531 4548 (
4532 4549 b'b',
4533 4550 b'branch',
4534 4551 [],
4535 4552 _(b'show changesets within the given named branch'),
4536 4553 _(b'BRANCH'),
4537 4554 ),
4538 4555 (
4539 4556 b'B',
4540 4557 b'bookmark',
4541 4558 [],
4542 4559 _(b"show changesets within the given bookmark"),
4543 4560 _(b'BOOKMARK'),
4544 4561 ),
4545 4562 (
4546 4563 b'P',
4547 4564 b'prune',
4548 4565 [],
4549 4566 _(b'do not display revision or any of its ancestors'),
4550 4567 _(b'REV'),
4551 4568 ),
4552 4569 ]
4553 4570 + logopts
4554 4571 + walkopts,
4555 4572 _(b'[OPTION]... [FILE]'),
4556 4573 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4557 4574 helpbasic=True,
4558 4575 inferrepo=True,
4559 4576 intents={INTENT_READONLY},
4560 4577 )
4561 4578 def log(ui, repo, *pats, **opts):
4562 4579 """show revision history of entire repository or files
4563 4580
4564 4581 Print the revision history of the specified files or the entire
4565 4582 project.
4566 4583
4567 4584 If no revision range is specified, the default is ``tip:0`` unless
4568 4585 --follow is set.
4569 4586
4570 4587 File history is shown without following rename or copy history of
4571 4588 files. Use -f/--follow with a filename to follow history across
4572 4589 renames and copies. --follow without a filename will only show
4573 4590 ancestors of the starting revisions. The starting revisions can be
4574 4591 specified by -r/--rev, which default to the working directory parent.
4575 4592
4576 4593 By default this command prints revision number and changeset id,
4577 4594 tags, non-trivial parents, user, date and time, and a summary for
4578 4595 each commit. When the -v/--verbose switch is used, the list of
4579 4596 changed files and full commit message are shown.
4580 4597
4581 4598 With --graph the revisions are shown as an ASCII art DAG with the most
4582 4599 recent changeset at the top.
4583 4600 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4584 4601 involved in an unresolved merge conflict, '_' closes a branch,
4585 4602 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4586 4603 changeset from the lines below is a parent of the 'o' merge on the same
4587 4604 line.
4588 4605 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4589 4606 of a '|' indicates one or more revisions in a path are omitted.
4590 4607
4591 4608 .. container:: verbose
4592 4609
4593 4610 Use -L/--line-range FILE,M:N options to follow the history of lines
4594 4611 from M to N in FILE. With -p/--patch only diff hunks affecting
4595 4612 specified line range will be shown. This option requires --follow;
4596 4613 it can be specified multiple times. Currently, this option is not
4597 4614 compatible with --graph. This option is experimental.
4598 4615
4599 4616 .. note::
4600 4617
4601 4618 :hg:`log --patch` may generate unexpected diff output for merge
4602 4619 changesets, as it will only compare the merge changeset against
4603 4620 its first parent. Also, only files different from BOTH parents
4604 4621 will appear in files:.
4605 4622
4606 4623 .. note::
4607 4624
4608 4625 For performance reasons, :hg:`log FILE` may omit duplicate changes
4609 4626 made on branches and will not show removals or mode changes. To
4610 4627 see all such changes, use the --removed switch.
4611 4628
4612 4629 .. container:: verbose
4613 4630
4614 4631 .. note::
4615 4632
4616 4633 The history resulting from -L/--line-range options depends on diff
4617 4634 options; for instance if white-spaces are ignored, respective changes
4618 4635 with only white-spaces in specified line range will not be listed.
4619 4636
4620 4637 .. container:: verbose
4621 4638
4622 4639 Some examples:
4623 4640
4624 4641 - changesets with full descriptions and file lists::
4625 4642
4626 4643 hg log -v
4627 4644
4628 4645 - changesets ancestral to the working directory::
4629 4646
4630 4647 hg log -f
4631 4648
4632 4649 - last 10 commits on the current branch::
4633 4650
4634 4651 hg log -l 10 -b .
4635 4652
4636 4653 - changesets showing all modifications of a file, including removals::
4637 4654
4638 4655 hg log --removed file.c
4639 4656
4640 4657 - all changesets that touch a directory, with diffs, excluding merges::
4641 4658
4642 4659 hg log -Mp lib/
4643 4660
4644 4661 - all revision numbers that match a keyword::
4645 4662
4646 4663 hg log -k bug --template "{rev}\\n"
4647 4664
4648 4665 - the full hash identifier of the working directory parent::
4649 4666
4650 4667 hg log -r . --template "{node}\\n"
4651 4668
4652 4669 - list available log templates::
4653 4670
4654 4671 hg log -T list
4655 4672
4656 4673 - check if a given changeset is included in a tagged release::
4657 4674
4658 4675 hg log -r "a21ccf and ancestor(1.9)"
4659 4676
4660 4677 - find all changesets by some user in a date range::
4661 4678
4662 4679 hg log -k alice -d "may 2008 to jul 2008"
4663 4680
4664 4681 - summary of all changesets after the last tag::
4665 4682
4666 4683 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4667 4684
4668 4685 - changesets touching lines 13 to 23 for file.c::
4669 4686
4670 4687 hg log -L file.c,13:23
4671 4688
4672 4689 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4673 4690 main.c with patch::
4674 4691
4675 4692 hg log -L file.c,13:23 -L main.c,2:6 -p
4676 4693
4677 4694 See :hg:`help dates` for a list of formats valid for -d/--date.
4678 4695
4679 4696 See :hg:`help revisions` for more about specifying and ordering
4680 4697 revisions.
4681 4698
4682 4699 See :hg:`help templates` for more about pre-packaged styles and
4683 4700 specifying custom templates. The default template used by the log
4684 4701 command can be customized via the ``command-templates.log`` configuration
4685 4702 setting.
4686 4703
4687 4704 Returns 0 on success.
4688 4705
4689 4706 """
4690 4707 opts = pycompat.byteskwargs(opts)
4691 4708 linerange = opts.get(b'line_range')
4692 4709
4693 4710 if linerange and not opts.get(b'follow'):
4694 4711 raise error.InputError(_(b'--line-range requires --follow'))
4695 4712
4696 4713 if linerange and pats:
4697 4714 # TODO: take pats as patterns with no line-range filter
4698 4715 raise error.InputError(
4699 4716 _(b'FILE arguments are not compatible with --line-range option')
4700 4717 )
4701 4718
4702 4719 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4703 4720 walk_opts = logcmdutil.parseopts(ui, pats, opts)
4704 4721 revs, differ = logcmdutil.getrevs(repo, walk_opts)
4705 4722 if linerange:
4706 4723 # TODO: should follow file history from logcmdutil._initialrevs(),
4707 4724 # then filter the result by logcmdutil._makerevset() and --limit
4708 4725 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4709 4726
4710 4727 getcopies = None
4711 4728 if opts.get(b'copies'):
4712 4729 endrev = None
4713 4730 if revs:
4714 4731 endrev = revs.max() + 1
4715 4732 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4716 4733
4717 4734 ui.pager(b'log')
4718 4735 displayer = logcmdutil.changesetdisplayer(
4719 4736 ui, repo, opts, differ, buffered=True
4720 4737 )
4721 4738 if opts.get(b'graph'):
4722 4739 displayfn = logcmdutil.displaygraphrevs
4723 4740 else:
4724 4741 displayfn = logcmdutil.displayrevs
4725 4742 displayfn(ui, repo, revs, displayer, getcopies)
4726 4743
4727 4744
4728 4745 @command(
4729 4746 b'manifest',
4730 4747 [
4731 4748 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4732 4749 (b'', b'all', False, _(b"list files from all revisions")),
4733 4750 ]
4734 4751 + formatteropts,
4735 4752 _(b'[-r REV]'),
4736 4753 helpcategory=command.CATEGORY_MAINTENANCE,
4737 4754 intents={INTENT_READONLY},
4738 4755 )
4739 4756 def manifest(ui, repo, node=None, rev=None, **opts):
4740 4757 """output the current or given revision of the project manifest
4741 4758
4742 4759 Print a list of version controlled files for the given revision.
4743 4760 If no revision is given, the first parent of the working directory
4744 4761 is used, or the null revision if no revision is checked out.
4745 4762
4746 4763 With -v, print file permissions, symlink and executable bits.
4747 4764 With --debug, print file revision hashes.
4748 4765
4749 4766 If option --all is specified, the list of all files from all revisions
4750 4767 is printed. This includes deleted and renamed files.
4751 4768
4752 4769 Returns 0 on success.
4753 4770 """
4754 4771 opts = pycompat.byteskwargs(opts)
4755 4772 fm = ui.formatter(b'manifest', opts)
4756 4773
4757 4774 if opts.get(b'all'):
4758 4775 if rev or node:
4759 4776 raise error.InputError(_(b"can't specify a revision with --all"))
4760 4777
4761 4778 res = set()
4762 4779 for rev in repo:
4763 4780 ctx = repo[rev]
4764 4781 res |= set(ctx.files())
4765 4782
4766 4783 ui.pager(b'manifest')
4767 4784 for f in sorted(res):
4768 4785 fm.startitem()
4769 4786 fm.write(b"path", b'%s\n', f)
4770 4787 fm.end()
4771 4788 return
4772 4789
4773 4790 if rev and node:
4774 4791 raise error.InputError(_(b"please specify just one revision"))
4775 4792
4776 4793 if not node:
4777 4794 node = rev
4778 4795
4779 4796 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4780 4797 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4781 4798 if node:
4782 4799 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4783 4800 ctx = scmutil.revsingle(repo, node)
4784 4801 mf = ctx.manifest()
4785 4802 ui.pager(b'manifest')
4786 4803 for f in ctx:
4787 4804 fm.startitem()
4788 4805 fm.context(ctx=ctx)
4789 4806 fl = ctx[f].flags()
4790 4807 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4791 4808 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4792 4809 fm.write(b'path', b'%s\n', f)
4793 4810 fm.end()
4794 4811
4795 4812
4796 4813 @command(
4797 4814 b'merge',
4798 4815 [
4799 4816 (
4800 4817 b'f',
4801 4818 b'force',
4802 4819 None,
4803 4820 _(b'force a merge including outstanding changes (DEPRECATED)'),
4804 4821 ),
4805 4822 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4806 4823 (
4807 4824 b'P',
4808 4825 b'preview',
4809 4826 None,
4810 4827 _(b'review revisions to merge (no merge is performed)'),
4811 4828 ),
4812 4829 (b'', b'abort', None, _(b'abort the ongoing merge')),
4813 4830 ]
4814 4831 + mergetoolopts,
4815 4832 _(b'[-P] [[-r] REV]'),
4816 4833 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4817 4834 helpbasic=True,
4818 4835 )
4819 4836 def merge(ui, repo, node=None, **opts):
4820 4837 """merge another revision into working directory
4821 4838
4822 4839 The current working directory is updated with all changes made in
4823 4840 the requested revision since the last common predecessor revision.
4824 4841
4825 4842 Files that changed between either parent are marked as changed for
4826 4843 the next commit and a commit must be performed before any further
4827 4844 updates to the repository are allowed. The next commit will have
4828 4845 two parents.
4829 4846
4830 4847 ``--tool`` can be used to specify the merge tool used for file
4831 4848 merges. It overrides the HGMERGE environment variable and your
4832 4849 configuration files. See :hg:`help merge-tools` for options.
4833 4850
4834 4851 If no revision is specified, the working directory's parent is a
4835 4852 head revision, and the current branch contains exactly one other
4836 4853 head, the other head is merged with by default. Otherwise, an
4837 4854 explicit revision with which to merge must be provided.
4838 4855
4839 4856 See :hg:`help resolve` for information on handling file conflicts.
4840 4857
4841 4858 To undo an uncommitted merge, use :hg:`merge --abort` which
4842 4859 will check out a clean copy of the original merge parent, losing
4843 4860 all changes.
4844 4861
4845 4862 Returns 0 on success, 1 if there are unresolved files.
4846 4863 """
4847 4864
4848 4865 opts = pycompat.byteskwargs(opts)
4849 4866 abort = opts.get(b'abort')
4850 4867 if abort and repo.dirstate.p2() == repo.nullid:
4851 4868 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4852 4869 cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4853 4870 if abort:
4854 4871 state = cmdutil.getunfinishedstate(repo)
4855 4872 if state and state._opname != b'merge':
4856 4873 raise error.StateError(
4857 4874 _(b'cannot abort merge with %s in progress') % (state._opname),
4858 4875 hint=state.hint(),
4859 4876 )
4860 4877 if node:
4861 4878 raise error.InputError(_(b"cannot specify a node with --abort"))
4862 4879 return hg.abortmerge(repo.ui, repo)
4863 4880
4864 4881 if opts.get(b'rev') and node:
4865 4882 raise error.InputError(_(b"please specify just one revision"))
4866 4883 if not node:
4867 4884 node = opts.get(b'rev')
4868 4885
4869 4886 if node:
4870 4887 ctx = scmutil.revsingle(repo, node)
4871 4888 else:
4872 4889 if ui.configbool(b'commands', b'merge.require-rev'):
4873 4890 raise error.InputError(
4874 4891 _(
4875 4892 b'configuration requires specifying revision to merge '
4876 4893 b'with'
4877 4894 )
4878 4895 )
4879 4896 ctx = repo[destutil.destmerge(repo)]
4880 4897
4881 4898 if ctx.node() is None:
4882 4899 raise error.InputError(
4883 4900 _(b'merging with the working copy has no effect')
4884 4901 )
4885 4902
4886 4903 if opts.get(b'preview'):
4887 4904 # find nodes that are ancestors of p2 but not of p1
4888 4905 p1 = repo[b'.'].node()
4889 4906 p2 = ctx.node()
4890 4907 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4891 4908
4892 4909 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4893 4910 for node in nodes:
4894 4911 displayer.show(repo[node])
4895 4912 displayer.close()
4896 4913 return 0
4897 4914
4898 4915 # ui.forcemerge is an internal variable, do not document
4899 4916 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4900 4917 with ui.configoverride(overrides, b'merge'):
4901 4918 force = opts.get(b'force')
4902 4919 labels = [b'working copy', b'merge rev']
4903 4920 return hg.merge(ctx, force=force, labels=labels)
4904 4921
4905 4922
4906 4923 statemod.addunfinished(
4907 4924 b'merge',
4908 4925 fname=None,
4909 4926 clearable=True,
4910 4927 allowcommit=True,
4911 4928 cmdmsg=_(b'outstanding uncommitted merge'),
4912 4929 abortfunc=hg.abortmerge,
4913 4930 statushint=_(
4914 4931 b'To continue: hg commit\nTo abort: hg merge --abort'
4915 4932 ),
4916 4933 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4917 4934 )
4918 4935
4919 4936
4920 4937 @command(
4921 4938 b'outgoing|out',
4922 4939 [
4923 4940 (
4924 4941 b'f',
4925 4942 b'force',
4926 4943 None,
4927 4944 _(b'run even when the destination is unrelated'),
4928 4945 ),
4929 4946 (
4930 4947 b'r',
4931 4948 b'rev',
4932 4949 [],
4933 4950 _(b'a changeset intended to be included in the destination'),
4934 4951 _(b'REV'),
4935 4952 ),
4936 4953 (b'n', b'newest-first', None, _(b'show newest record first')),
4937 4954 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4938 4955 (
4939 4956 b'b',
4940 4957 b'branch',
4941 4958 [],
4942 4959 _(b'a specific branch you would like to push'),
4943 4960 _(b'BRANCH'),
4944 4961 ),
4945 4962 ]
4946 4963 + logopts
4947 4964 + remoteopts
4948 4965 + subrepoopts,
4949 4966 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]...'),
4950 4967 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4951 4968 )
4952 4969 def outgoing(ui, repo, *dests, **opts):
4953 4970 """show changesets not found in the destination
4954 4971
4955 4972 Show changesets not found in the specified destination repository
4956 4973 or the default push location. These are the changesets that would
4957 4974 be pushed if a push was requested.
4958 4975
4959 4976 See pull for details of valid destination formats.
4960 4977
4961 4978 .. container:: verbose
4962 4979
4963 4980 With -B/--bookmarks, the result of bookmark comparison between
4964 4981 local and remote repositories is displayed. With -v/--verbose,
4965 4982 status is also displayed for each bookmark like below::
4966 4983
4967 4984 BM1 01234567890a added
4968 4985 BM2 deleted
4969 4986 BM3 234567890abc advanced
4970 4987 BM4 34567890abcd diverged
4971 4988 BM5 4567890abcde changed
4972 4989
4973 4990 The action taken when pushing depends on the
4974 4991 status of each bookmark:
4975 4992
4976 4993 :``added``: push with ``-B`` will create it
4977 4994 :``deleted``: push with ``-B`` will delete it
4978 4995 :``advanced``: push will update it
4979 4996 :``diverged``: push with ``-B`` will update it
4980 4997 :``changed``: push with ``-B`` will update it
4981 4998
4982 4999 From the point of view of pushing behavior, bookmarks
4983 5000 existing only in the remote repository are treated as
4984 5001 ``deleted``, even if it is in fact added remotely.
4985 5002
4986 5003 Returns 0 if there are outgoing changes, 1 otherwise.
4987 5004 """
4988 5005 opts = pycompat.byteskwargs(opts)
4989 5006 if opts.get(b'bookmarks'):
4990 5007 for path in urlutil.get_push_paths(repo, ui, dests):
4991 5008 dest = path.pushloc or path.loc
4992 5009 other = hg.peer(repo, opts, dest)
4993 5010 try:
4994 5011 if b'bookmarks' not in other.listkeys(b'namespaces'):
4995 5012 ui.warn(_(b"remote doesn't support bookmarks\n"))
4996 5013 return 0
4997 5014 ui.status(
4998 5015 _(b'comparing with %s\n') % urlutil.hidepassword(dest)
4999 5016 )
5000 5017 ui.pager(b'outgoing')
5001 5018 return bookmarks.outgoing(ui, repo, other)
5002 5019 finally:
5003 5020 other.close()
5004 5021
5005 5022 return hg.outgoing(ui, repo, dests, opts)
5006 5023
5007 5024
5008 5025 @command(
5009 5026 b'parents',
5010 5027 [
5011 5028 (
5012 5029 b'r',
5013 5030 b'rev',
5014 5031 b'',
5015 5032 _(b'show parents of the specified revision'),
5016 5033 _(b'REV'),
5017 5034 ),
5018 5035 ]
5019 5036 + templateopts,
5020 5037 _(b'[-r REV] [FILE]'),
5021 5038 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5022 5039 inferrepo=True,
5023 5040 )
5024 5041 def parents(ui, repo, file_=None, **opts):
5025 5042 """show the parents of the working directory or revision (DEPRECATED)
5026 5043
5027 5044 Print the working directory's parent revisions. If a revision is
5028 5045 given via -r/--rev, the parent of that revision will be printed.
5029 5046 If a file argument is given, the revision in which the file was
5030 5047 last changed (before the working directory revision or the
5031 5048 argument to --rev if given) is printed.
5032 5049
5033 5050 This command is equivalent to::
5034 5051
5035 5052 hg log -r "p1()+p2()" or
5036 5053 hg log -r "p1(REV)+p2(REV)" or
5037 5054 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5038 5055 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5039 5056
5040 5057 See :hg:`summary` and :hg:`help revsets` for related information.
5041 5058
5042 5059 Returns 0 on success.
5043 5060 """
5044 5061
5045 5062 opts = pycompat.byteskwargs(opts)
5046 5063 rev = opts.get(b'rev')
5047 5064 if rev:
5048 5065 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5049 5066 ctx = scmutil.revsingle(repo, rev, None)
5050 5067
5051 5068 if file_:
5052 5069 m = scmutil.match(ctx, (file_,), opts)
5053 5070 if m.anypats() or len(m.files()) != 1:
5054 5071 raise error.InputError(_(b'can only specify an explicit filename'))
5055 5072 file_ = m.files()[0]
5056 5073 filenodes = []
5057 5074 for cp in ctx.parents():
5058 5075 if not cp:
5059 5076 continue
5060 5077 try:
5061 5078 filenodes.append(cp.filenode(file_))
5062 5079 except error.LookupError:
5063 5080 pass
5064 5081 if not filenodes:
5065 5082 raise error.InputError(_(b"'%s' not found in manifest") % file_)
5066 5083 p = []
5067 5084 for fn in filenodes:
5068 5085 fctx = repo.filectx(file_, fileid=fn)
5069 5086 p.append(fctx.node())
5070 5087 else:
5071 5088 p = [cp.node() for cp in ctx.parents()]
5072 5089
5073 5090 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5074 5091 for n in p:
5075 5092 if n != repo.nullid:
5076 5093 displayer.show(repo[n])
5077 5094 displayer.close()
5078 5095
5079 5096
5080 5097 @command(
5081 5098 b'paths',
5082 5099 formatteropts,
5083 5100 _(b'[NAME]'),
5084 5101 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5085 5102 optionalrepo=True,
5086 5103 intents={INTENT_READONLY},
5087 5104 )
5088 5105 def paths(ui, repo, search=None, **opts):
5089 5106 """show aliases for remote repositories
5090 5107
5091 5108 Show definition of symbolic path name NAME. If no name is given,
5092 5109 show definition of all available names.
5093 5110
5094 5111 Option -q/--quiet suppresses all output when searching for NAME
5095 5112 and shows only the path names when listing all definitions.
5096 5113
5097 5114 Path names are defined in the [paths] section of your
5098 5115 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5099 5116 repository, ``.hg/hgrc`` is used, too.
5100 5117
5101 5118 The path names ``default`` and ``default-push`` have a special
5102 5119 meaning. When performing a push or pull operation, they are used
5103 5120 as fallbacks if no location is specified on the command-line.
5104 5121 When ``default-push`` is set, it will be used for push and
5105 5122 ``default`` will be used for pull; otherwise ``default`` is used
5106 5123 as the fallback for both. When cloning a repository, the clone
5107 5124 source is written as ``default`` in ``.hg/hgrc``.
5108 5125
5109 5126 .. note::
5110 5127
5111 5128 ``default`` and ``default-push`` apply to all inbound (e.g.
5112 5129 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5113 5130 and :hg:`bundle`) operations.
5114 5131
5115 5132 See :hg:`help urls` for more information.
5116 5133
5117 5134 .. container:: verbose
5118 5135
5119 5136 Template:
5120 5137
5121 5138 The following keywords are supported. See also :hg:`help templates`.
5122 5139
5123 5140 :name: String. Symbolic name of the path alias.
5124 5141 :pushurl: String. URL for push operations.
5125 5142 :url: String. URL or directory path for the other operations.
5126 5143
5127 5144 Returns 0 on success.
5128 5145 """
5129 5146
5130 5147 opts = pycompat.byteskwargs(opts)
5131 5148
5132 5149 pathitems = urlutil.list_paths(ui, search)
5133 5150 ui.pager(b'paths')
5134 5151
5135 5152 fm = ui.formatter(b'paths', opts)
5136 5153 if fm.isplain():
5137 5154 hidepassword = urlutil.hidepassword
5138 5155 else:
5139 5156 hidepassword = bytes
5140 5157 if ui.quiet:
5141 5158 namefmt = b'%s\n'
5142 5159 else:
5143 5160 namefmt = b'%s = '
5144 5161 showsubopts = not search and not ui.quiet
5145 5162
5146 5163 for name, path in pathitems:
5147 5164 fm.startitem()
5148 5165 fm.condwrite(not search, b'name', namefmt, name)
5149 5166 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5150 5167 for subopt, value in sorted(path.suboptions.items()):
5151 5168 assert subopt not in (b'name', b'url')
5152 5169 if showsubopts:
5153 5170 fm.plain(b'%s:%s = ' % (name, subopt))
5154 5171 if isinstance(value, bool):
5155 5172 if value:
5156 5173 value = b'yes'
5157 5174 else:
5158 5175 value = b'no'
5159 5176 fm.condwrite(showsubopts, subopt, b'%s\n', value)
5160 5177
5161 5178 fm.end()
5162 5179
5163 5180 if search and not pathitems:
5164 5181 if not ui.quiet:
5165 5182 ui.warn(_(b"not found!\n"))
5166 5183 return 1
5167 5184 else:
5168 5185 return 0
5169 5186
5170 5187
5171 5188 @command(
5172 5189 b'phase',
5173 5190 [
5174 5191 (b'p', b'public', False, _(b'set changeset phase to public')),
5175 5192 (b'd', b'draft', False, _(b'set changeset phase to draft')),
5176 5193 (b's', b'secret', False, _(b'set changeset phase to secret')),
5177 5194 (b'f', b'force', False, _(b'allow to move boundary backward')),
5178 5195 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5179 5196 ],
5180 5197 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5181 5198 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5182 5199 )
5183 5200 def phase(ui, repo, *revs, **opts):
5184 5201 """set or show the current phase name
5185 5202
5186 5203 With no argument, show the phase name of the current revision(s).
5187 5204
5188 5205 With one of -p/--public, -d/--draft or -s/--secret, change the
5189 5206 phase value of the specified revisions.
5190 5207
5191 5208 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5192 5209 lower phase to a higher phase. Phases are ordered as follows::
5193 5210
5194 5211 public < draft < secret
5195 5212
5196 5213 Returns 0 on success, 1 if some phases could not be changed.
5197 5214
5198 5215 (For more information about the phases concept, see :hg:`help phases`.)
5199 5216 """
5200 5217 opts = pycompat.byteskwargs(opts)
5201 5218 # search for a unique phase argument
5202 5219 targetphase = None
5203 5220 for idx, name in enumerate(phases.cmdphasenames):
5204 5221 if opts[name]:
5205 5222 if targetphase is not None:
5206 5223 raise error.InputError(_(b'only one phase can be specified'))
5207 5224 targetphase = idx
5208 5225
5209 5226 # look for specified revision
5210 5227 revs = list(revs)
5211 5228 revs.extend(opts[b'rev'])
5212 5229 if not revs:
5213 5230 # display both parents as the second parent phase can influence
5214 5231 # the phase of a merge commit
5215 5232 revs = [c.rev() for c in repo[None].parents()]
5216 5233
5217 5234 revs = scmutil.revrange(repo, revs)
5218 5235
5219 5236 ret = 0
5220 5237 if targetphase is None:
5221 5238 # display
5222 5239 for r in revs:
5223 5240 ctx = repo[r]
5224 5241 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5225 5242 else:
5226 5243 with repo.lock(), repo.transaction(b"phase") as tr:
5227 5244 # set phase
5228 5245 if not revs:
5229 5246 raise error.InputError(_(b'empty revision set'))
5230 5247 nodes = [repo[r].node() for r in revs]
5231 5248 # moving revision from public to draft may hide them
5232 5249 # We have to check result on an unfiltered repository
5233 5250 unfi = repo.unfiltered()
5234 5251 getphase = unfi._phasecache.phase
5235 5252 olddata = [getphase(unfi, r) for r in unfi]
5236 5253 phases.advanceboundary(repo, tr, targetphase, nodes)
5237 5254 if opts[b'force']:
5238 5255 phases.retractboundary(repo, tr, targetphase, nodes)
5239 5256 getphase = unfi._phasecache.phase
5240 5257 newdata = [getphase(unfi, r) for r in unfi]
5241 5258 changes = sum(newdata[r] != olddata[r] for r in unfi)
5242 5259 cl = unfi.changelog
5243 5260 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5244 5261 if rejected:
5245 5262 ui.warn(
5246 5263 _(
5247 5264 b'cannot move %i changesets to a higher '
5248 5265 b'phase, use --force\n'
5249 5266 )
5250 5267 % len(rejected)
5251 5268 )
5252 5269 ret = 1
5253 5270 if changes:
5254 5271 msg = _(b'phase changed for %i changesets\n') % changes
5255 5272 if ret:
5256 5273 ui.status(msg)
5257 5274 else:
5258 5275 ui.note(msg)
5259 5276 else:
5260 5277 ui.warn(_(b'no phases changed\n'))
5261 5278 return ret
5262 5279
5263 5280
5264 5281 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5265 5282 """Run after a changegroup has been added via pull/unbundle
5266 5283
5267 5284 This takes arguments below:
5268 5285
5269 5286 :modheads: change of heads by pull/unbundle
5270 5287 :optupdate: updating working directory is needed or not
5271 5288 :checkout: update destination revision (or None to default destination)
5272 5289 :brev: a name, which might be a bookmark to be activated after updating
5273 5290
5274 5291 return True if update raise any conflict, False otherwise.
5275 5292 """
5276 5293 if modheads == 0:
5277 5294 return False
5278 5295 if optupdate:
5279 5296 try:
5280 5297 return hg.updatetotally(ui, repo, checkout, brev)
5281 5298 except error.UpdateAbort as inst:
5282 5299 msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5283 5300 hint = inst.hint
5284 5301 raise error.UpdateAbort(msg, hint=hint)
5285 5302 if modheads is not None and modheads > 1:
5286 5303 currentbranchheads = len(repo.branchheads())
5287 5304 if currentbranchheads == modheads:
5288 5305 ui.status(
5289 5306 _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5290 5307 )
5291 5308 elif currentbranchheads > 1:
5292 5309 ui.status(
5293 5310 _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5294 5311 )
5295 5312 else:
5296 5313 ui.status(_(b"(run 'hg heads' to see heads)\n"))
5297 5314 elif not ui.configbool(b'commands', b'update.requiredest'):
5298 5315 ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5299 5316 return False
5300 5317
5301 5318
5302 5319 @command(
5303 5320 b'pull',
5304 5321 [
5305 5322 (
5306 5323 b'u',
5307 5324 b'update',
5308 5325 None,
5309 5326 _(b'update to new branch head if new descendants were pulled'),
5310 5327 ),
5311 5328 (
5312 5329 b'f',
5313 5330 b'force',
5314 5331 None,
5315 5332 _(b'run even when remote repository is unrelated'),
5316 5333 ),
5317 5334 (
5318 5335 b'',
5319 5336 b'confirm',
5320 5337 None,
5321 5338 _(b'confirm pull before applying changes'),
5322 5339 ),
5323 5340 (
5324 5341 b'r',
5325 5342 b'rev',
5326 5343 [],
5327 5344 _(b'a remote changeset intended to be added'),
5328 5345 _(b'REV'),
5329 5346 ),
5330 5347 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5331 5348 (
5332 5349 b'b',
5333 5350 b'branch',
5334 5351 [],
5335 5352 _(b'a specific branch you would like to pull'),
5336 5353 _(b'BRANCH'),
5337 5354 ),
5338 5355 ]
5339 5356 + remoteopts,
5340 5357 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]...'),
5341 5358 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5342 5359 helpbasic=True,
5343 5360 )
5344 5361 def pull(ui, repo, *sources, **opts):
5345 5362 """pull changes from the specified source
5346 5363
5347 5364 Pull changes from a remote repository to a local one.
5348 5365
5349 5366 This finds all changes from the repository at the specified path
5350 5367 or URL and adds them to a local repository (the current one unless
5351 5368 -R is specified). By default, this does not update the copy of the
5352 5369 project in the working directory.
5353 5370
5354 5371 When cloning from servers that support it, Mercurial may fetch
5355 5372 pre-generated data. When this is done, hooks operating on incoming
5356 5373 changesets and changegroups may fire more than once, once for each
5357 5374 pre-generated bundle and as well as for any additional remaining
5358 5375 data. See :hg:`help -e clonebundles` for more.
5359 5376
5360 5377 Use :hg:`incoming` if you want to see what would have been added
5361 5378 by a pull at the time you issued this command. If you then decide
5362 5379 to add those changes to the repository, you should use :hg:`pull
5363 5380 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5364 5381
5365 5382 If SOURCE is omitted, the 'default' path will be used.
5366 5383 See :hg:`help urls` for more information.
5367 5384
5368 5385 If multiple sources are specified, they will be pulled sequentially as if
5369 5386 the command was run multiple time. If --update is specify and the command
5370 5387 will stop at the first failed --update.
5371 5388
5372 5389 Specifying bookmark as ``.`` is equivalent to specifying the active
5373 5390 bookmark's name.
5374 5391
5375 5392 Returns 0 on success, 1 if an update had unresolved files.
5376 5393 """
5377 5394
5378 5395 opts = pycompat.byteskwargs(opts)
5379 5396 if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5380 5397 b'update'
5381 5398 ):
5382 5399 msg = _(b'update destination required by configuration')
5383 5400 hint = _(b'use hg pull followed by hg update DEST')
5384 5401 raise error.InputError(msg, hint=hint)
5385 5402
5386 5403 sources = urlutil.get_pull_paths(repo, ui, sources, opts.get(b'branch'))
5387 5404 for source, branches in sources:
5388 5405 ui.status(_(b'pulling from %s\n') % urlutil.hidepassword(source))
5389 5406 ui.flush()
5390 5407 other = hg.peer(repo, opts, source)
5391 5408 update_conflict = None
5392 5409 try:
5393 5410 revs, checkout = hg.addbranchrevs(
5394 5411 repo, other, branches, opts.get(b'rev')
5395 5412 )
5396 5413
5397 5414 pullopargs = {}
5398 5415
5399 5416 nodes = None
5400 5417 if opts.get(b'bookmark') or revs:
5401 5418 # The list of bookmark used here is the same used to actually update
5402 5419 # the bookmark names, to avoid the race from issue 4689 and we do
5403 5420 # all lookup and bookmark queries in one go so they see the same
5404 5421 # version of the server state (issue 4700).
5405 5422 nodes = []
5406 5423 fnodes = []
5407 5424 revs = revs or []
5408 5425 if revs and not other.capable(b'lookup'):
5409 5426 err = _(
5410 5427 b"other repository doesn't support revision lookup, "
5411 5428 b"so a rev cannot be specified."
5412 5429 )
5413 5430 raise error.Abort(err)
5414 5431 with other.commandexecutor() as e:
5415 5432 fremotebookmarks = e.callcommand(
5416 5433 b'listkeys', {b'namespace': b'bookmarks'}
5417 5434 )
5418 5435 for r in revs:
5419 5436 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5420 5437 remotebookmarks = fremotebookmarks.result()
5421 5438 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5422 5439 pullopargs[b'remotebookmarks'] = remotebookmarks
5423 5440 for b in opts.get(b'bookmark', []):
5424 5441 b = repo._bookmarks.expandname(b)
5425 5442 if b not in remotebookmarks:
5426 5443 raise error.InputError(
5427 5444 _(b'remote bookmark %s not found!') % b
5428 5445 )
5429 5446 nodes.append(remotebookmarks[b])
5430 5447 for i, rev in enumerate(revs):
5431 5448 node = fnodes[i].result()
5432 5449 nodes.append(node)
5433 5450 if rev == checkout:
5434 5451 checkout = node
5435 5452
5436 5453 wlock = util.nullcontextmanager()
5437 5454 if opts.get(b'update'):
5438 5455 wlock = repo.wlock()
5439 5456 with wlock:
5440 5457 pullopargs.update(opts.get(b'opargs', {}))
5441 5458 modheads = exchange.pull(
5442 5459 repo,
5443 5460 other,
5444 5461 heads=nodes,
5445 5462 force=opts.get(b'force'),
5446 5463 bookmarks=opts.get(b'bookmark', ()),
5447 5464 opargs=pullopargs,
5448 5465 confirm=opts.get(b'confirm'),
5449 5466 ).cgresult
5450 5467
5451 5468 # brev is a name, which might be a bookmark to be activated at
5452 5469 # the end of the update. In other words, it is an explicit
5453 5470 # destination of the update
5454 5471 brev = None
5455 5472
5456 5473 if checkout:
5457 5474 checkout = repo.unfiltered().changelog.rev(checkout)
5458 5475
5459 5476 # order below depends on implementation of
5460 5477 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5461 5478 # because 'checkout' is determined without it.
5462 5479 if opts.get(b'rev'):
5463 5480 brev = opts[b'rev'][0]
5464 5481 elif opts.get(b'branch'):
5465 5482 brev = opts[b'branch'][0]
5466 5483 else:
5467 5484 brev = branches[0]
5468 5485 repo._subtoppath = source
5469 5486 try:
5470 5487 update_conflict = postincoming(
5471 5488 ui, repo, modheads, opts.get(b'update'), checkout, brev
5472 5489 )
5473 5490 except error.FilteredRepoLookupError as exc:
5474 5491 msg = _(b'cannot update to target: %s') % exc.args[0]
5475 5492 exc.args = (msg,) + exc.args[1:]
5476 5493 raise
5477 5494 finally:
5478 5495 del repo._subtoppath
5479 5496
5480 5497 finally:
5481 5498 other.close()
5482 5499 # skip the remaining pull source if they are some conflict.
5483 5500 if update_conflict:
5484 5501 break
5485 5502 if update_conflict:
5486 5503 return 1
5487 5504 else:
5488 5505 return 0
5489 5506
5490 5507
5491 5508 @command(
5492 5509 b'purge|clean',
5493 5510 [
5494 5511 (b'a', b'abort-on-err', None, _(b'abort if an error occurs')),
5495 5512 (b'', b'all', None, _(b'purge ignored files too')),
5496 5513 (b'i', b'ignored', None, _(b'purge only ignored files')),
5497 5514 (b'', b'dirs', None, _(b'purge empty directories')),
5498 5515 (b'', b'files', None, _(b'purge files')),
5499 5516 (b'p', b'print', None, _(b'print filenames instead of deleting them')),
5500 5517 (
5501 5518 b'0',
5502 5519 b'print0',
5503 5520 None,
5504 5521 _(
5505 5522 b'end filenames with NUL, for use with xargs'
5506 5523 b' (implies -p/--print)'
5507 5524 ),
5508 5525 ),
5509 5526 (b'', b'confirm', None, _(b'ask before permanently deleting files')),
5510 5527 ]
5511 5528 + cmdutil.walkopts,
5512 5529 _(b'hg purge [OPTION]... [DIR]...'),
5513 5530 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5514 5531 )
5515 5532 def purge(ui, repo, *dirs, **opts):
5516 5533 """removes files not tracked by Mercurial
5517 5534
5518 5535 Delete files not known to Mercurial. This is useful to test local
5519 5536 and uncommitted changes in an otherwise-clean source tree.
5520 5537
5521 5538 This means that purge will delete the following by default:
5522 5539
5523 5540 - Unknown files: files marked with "?" by :hg:`status`
5524 5541 - Empty directories: in fact Mercurial ignores directories unless
5525 5542 they contain files under source control management
5526 5543
5527 5544 But it will leave untouched:
5528 5545
5529 5546 - Modified and unmodified tracked files
5530 5547 - Ignored files (unless -i or --all is specified)
5531 5548 - New files added to the repository (with :hg:`add`)
5532 5549
5533 5550 The --files and --dirs options can be used to direct purge to delete
5534 5551 only files, only directories, or both. If neither option is given,
5535 5552 both will be deleted.
5536 5553
5537 5554 If directories are given on the command line, only files in these
5538 5555 directories are considered.
5539 5556
5540 5557 Be careful with purge, as you could irreversibly delete some files
5541 5558 you forgot to add to the repository. If you only want to print the
5542 5559 list of files that this program would delete, use the --print
5543 5560 option.
5544 5561 """
5545 5562 opts = pycompat.byteskwargs(opts)
5546 5563 cmdutil.check_at_most_one_arg(opts, b'all', b'ignored')
5547 5564
5548 5565 act = not opts.get(b'print')
5549 5566 eol = b'\n'
5550 5567 if opts.get(b'print0'):
5551 5568 eol = b'\0'
5552 5569 act = False # --print0 implies --print
5553 5570 if opts.get(b'all', False):
5554 5571 ignored = True
5555 5572 unknown = True
5556 5573 else:
5557 5574 ignored = opts.get(b'ignored', False)
5558 5575 unknown = not ignored
5559 5576
5560 5577 removefiles = opts.get(b'files')
5561 5578 removedirs = opts.get(b'dirs')
5562 5579 confirm = opts.get(b'confirm')
5563 5580 if confirm is None:
5564 5581 try:
5565 5582 extensions.find(b'purge')
5566 5583 confirm = False
5567 5584 except KeyError:
5568 5585 confirm = True
5569 5586
5570 5587 if not removefiles and not removedirs:
5571 5588 removefiles = True
5572 5589 removedirs = True
5573 5590
5574 5591 match = scmutil.match(repo[None], dirs, opts)
5575 5592
5576 5593 paths = mergemod.purge(
5577 5594 repo,
5578 5595 match,
5579 5596 unknown=unknown,
5580 5597 ignored=ignored,
5581 5598 removeemptydirs=removedirs,
5582 5599 removefiles=removefiles,
5583 5600 abortonerror=opts.get(b'abort_on_err'),
5584 5601 noop=not act,
5585 5602 confirm=confirm,
5586 5603 )
5587 5604
5588 5605 for path in paths:
5589 5606 if not act:
5590 5607 ui.write(b'%s%s' % (path, eol))
5591 5608
5592 5609
5593 5610 @command(
5594 5611 b'push',
5595 5612 [
5596 5613 (b'f', b'force', None, _(b'force push')),
5597 5614 (
5598 5615 b'r',
5599 5616 b'rev',
5600 5617 [],
5601 5618 _(b'a changeset intended to be included in the destination'),
5602 5619 _(b'REV'),
5603 5620 ),
5604 5621 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5605 5622 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5606 5623 (
5607 5624 b'b',
5608 5625 b'branch',
5609 5626 [],
5610 5627 _(b'a specific branch you would like to push'),
5611 5628 _(b'BRANCH'),
5612 5629 ),
5613 5630 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5614 5631 (
5615 5632 b'',
5616 5633 b'pushvars',
5617 5634 [],
5618 5635 _(b'variables that can be sent to server (ADVANCED)'),
5619 5636 ),
5620 5637 (
5621 5638 b'',
5622 5639 b'publish',
5623 5640 False,
5624 5641 _(b'push the changeset as public (EXPERIMENTAL)'),
5625 5642 ),
5626 5643 ]
5627 5644 + remoteopts,
5628 5645 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]...'),
5629 5646 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5630 5647 helpbasic=True,
5631 5648 )
5632 5649 def push(ui, repo, *dests, **opts):
5633 5650 """push changes to the specified destination
5634 5651
5635 5652 Push changesets from the local repository to the specified
5636 5653 destination.
5637 5654
5638 5655 This operation is symmetrical to pull: it is identical to a pull
5639 5656 in the destination repository from the current one.
5640 5657
5641 5658 By default, push will not allow creation of new heads at the
5642 5659 destination, since multiple heads would make it unclear which head
5643 5660 to use. In this situation, it is recommended to pull and merge
5644 5661 before pushing.
5645 5662
5646 5663 Use --new-branch if you want to allow push to create a new named
5647 5664 branch that is not present at the destination. This allows you to
5648 5665 only create a new branch without forcing other changes.
5649 5666
5650 5667 .. note::
5651 5668
5652 5669 Extra care should be taken with the -f/--force option,
5653 5670 which will push all new heads on all branches, an action which will
5654 5671 almost always cause confusion for collaborators.
5655 5672
5656 5673 If -r/--rev is used, the specified revision and all its ancestors
5657 5674 will be pushed to the remote repository.
5658 5675
5659 5676 If -B/--bookmark is used, the specified bookmarked revision, its
5660 5677 ancestors, and the bookmark will be pushed to the remote
5661 5678 repository. Specifying ``.`` is equivalent to specifying the active
5662 5679 bookmark's name. Use the --all-bookmarks option for pushing all
5663 5680 current bookmarks.
5664 5681
5665 5682 Please see :hg:`help urls` for important details about ``ssh://``
5666 5683 URLs. If DESTINATION is omitted, a default path will be used.
5667 5684
5668 5685 When passed multiple destinations, push will process them one after the
5669 5686 other, but stop should an error occur.
5670 5687
5671 5688 .. container:: verbose
5672 5689
5673 5690 The --pushvars option sends strings to the server that become
5674 5691 environment variables prepended with ``HG_USERVAR_``. For example,
5675 5692 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5676 5693 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5677 5694
5678 5695 pushvars can provide for user-overridable hooks as well as set debug
5679 5696 levels. One example is having a hook that blocks commits containing
5680 5697 conflict markers, but enables the user to override the hook if the file
5681 5698 is using conflict markers for testing purposes or the file format has
5682 5699 strings that look like conflict markers.
5683 5700
5684 5701 By default, servers will ignore `--pushvars`. To enable it add the
5685 5702 following to your configuration file::
5686 5703
5687 5704 [push]
5688 5705 pushvars.server = true
5689 5706
5690 5707 Returns 0 if push was successful, 1 if nothing to push.
5691 5708 """
5692 5709
5693 5710 opts = pycompat.byteskwargs(opts)
5694 5711
5695 5712 if opts.get(b'all_bookmarks'):
5696 5713 cmdutil.check_incompatible_arguments(
5697 5714 opts,
5698 5715 b'all_bookmarks',
5699 5716 [b'bookmark', b'rev'],
5700 5717 )
5701 5718 opts[b'bookmark'] = list(repo._bookmarks)
5702 5719
5703 5720 if opts.get(b'bookmark'):
5704 5721 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5705 5722 for b in opts[b'bookmark']:
5706 5723 # translate -B options to -r so changesets get pushed
5707 5724 b = repo._bookmarks.expandname(b)
5708 5725 if b in repo._bookmarks:
5709 5726 opts.setdefault(b'rev', []).append(b)
5710 5727 else:
5711 5728 # if we try to push a deleted bookmark, translate it to null
5712 5729 # this lets simultaneous -r, -b options continue working
5713 5730 opts.setdefault(b'rev', []).append(b"null")
5714 5731
5715 5732 some_pushed = False
5716 5733 result = 0
5717 5734 for path in urlutil.get_push_paths(repo, ui, dests):
5718 5735 dest = path.pushloc or path.loc
5719 5736 branches = (path.branch, opts.get(b'branch') or [])
5720 5737 ui.status(_(b'pushing to %s\n') % urlutil.hidepassword(dest))
5721 5738 revs, checkout = hg.addbranchrevs(
5722 5739 repo, repo, branches, opts.get(b'rev')
5723 5740 )
5724 5741 other = hg.peer(repo, opts, dest)
5725 5742
5726 5743 try:
5727 5744 if revs:
5728 5745 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
5729 5746 if not revs:
5730 5747 raise error.InputError(
5731 5748 _(b"specified revisions evaluate to an empty set"),
5732 5749 hint=_(b"use different revision arguments"),
5733 5750 )
5734 5751 elif path.pushrev:
5735 5752 # It doesn't make any sense to specify ancestor revisions. So limit
5736 5753 # to DAG heads to make discovery simpler.
5737 5754 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5738 5755 revs = scmutil.revrange(repo, [expr])
5739 5756 revs = [repo[rev].node() for rev in revs]
5740 5757 if not revs:
5741 5758 raise error.InputError(
5742 5759 _(
5743 5760 b'default push revset for path evaluates to an empty set'
5744 5761 )
5745 5762 )
5746 5763 elif ui.configbool(b'commands', b'push.require-revs'):
5747 5764 raise error.InputError(
5748 5765 _(b'no revisions specified to push'),
5749 5766 hint=_(b'did you mean "hg push -r ."?'),
5750 5767 )
5751 5768
5752 5769 repo._subtoppath = dest
5753 5770 try:
5754 5771 # push subrepos depth-first for coherent ordering
5755 5772 c = repo[b'.']
5756 5773 subs = c.substate # only repos that are committed
5757 5774 for s in sorted(subs):
5758 5775 sub_result = c.sub(s).push(opts)
5759 5776 if sub_result == 0:
5760 5777 return 1
5761 5778 finally:
5762 5779 del repo._subtoppath
5763 5780
5764 5781 opargs = dict(
5765 5782 opts.get(b'opargs', {})
5766 5783 ) # copy opargs since we may mutate it
5767 5784 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5768 5785
5769 5786 pushop = exchange.push(
5770 5787 repo,
5771 5788 other,
5772 5789 opts.get(b'force'),
5773 5790 revs=revs,
5774 5791 newbranch=opts.get(b'new_branch'),
5775 5792 bookmarks=opts.get(b'bookmark', ()),
5776 5793 publish=opts.get(b'publish'),
5777 5794 opargs=opargs,
5778 5795 )
5779 5796
5780 5797 if pushop.cgresult == 0:
5781 5798 result = 1
5782 5799 elif pushop.cgresult is not None:
5783 5800 some_pushed = True
5784 5801
5785 5802 if pushop.bkresult is not None:
5786 5803 if pushop.bkresult == 2:
5787 5804 result = 2
5788 5805 elif not result and pushop.bkresult:
5789 5806 result = 2
5790 5807
5791 5808 if result:
5792 5809 break
5793 5810
5794 5811 finally:
5795 5812 other.close()
5796 5813 if result == 0 and not some_pushed:
5797 5814 result = 1
5798 5815 return result
5799 5816
5800 5817
5801 5818 @command(
5802 5819 b'recover',
5803 5820 [
5804 5821 (b'', b'verify', False, b"run `hg verify` after successful recover"),
5805 5822 ],
5806 5823 helpcategory=command.CATEGORY_MAINTENANCE,
5807 5824 )
5808 5825 def recover(ui, repo, **opts):
5809 5826 """roll back an interrupted transaction
5810 5827
5811 5828 Recover from an interrupted commit or pull.
5812 5829
5813 5830 This command tries to fix the repository status after an
5814 5831 interrupted operation. It should only be necessary when Mercurial
5815 5832 suggests it.
5816 5833
5817 5834 Returns 0 if successful, 1 if nothing to recover or verify fails.
5818 5835 """
5819 5836 ret = repo.recover()
5820 5837 if ret:
5821 5838 if opts['verify']:
5822 5839 return hg.verify(repo)
5823 5840 else:
5824 5841 msg = _(
5825 5842 b"(verify step skipped, run `hg verify` to check your "
5826 5843 b"repository content)\n"
5827 5844 )
5828 5845 ui.warn(msg)
5829 5846 return 0
5830 5847 return 1
5831 5848
5832 5849
5833 5850 @command(
5834 5851 b'remove|rm',
5835 5852 [
5836 5853 (b'A', b'after', None, _(b'record delete for missing files')),
5837 5854 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5838 5855 ]
5839 5856 + subrepoopts
5840 5857 + walkopts
5841 5858 + dryrunopts,
5842 5859 _(b'[OPTION]... FILE...'),
5843 5860 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5844 5861 helpbasic=True,
5845 5862 inferrepo=True,
5846 5863 )
5847 5864 def remove(ui, repo, *pats, **opts):
5848 5865 """remove the specified files on the next commit
5849 5866
5850 5867 Schedule the indicated files for removal from the current branch.
5851 5868
5852 5869 This command schedules the files to be removed at the next commit.
5853 5870 To undo a remove before that, see :hg:`revert`. To undo added
5854 5871 files, see :hg:`forget`.
5855 5872
5856 5873 .. container:: verbose
5857 5874
5858 5875 -A/--after can be used to remove only files that have already
5859 5876 been deleted, -f/--force can be used to force deletion, and -Af
5860 5877 can be used to remove files from the next revision without
5861 5878 deleting them from the working directory.
5862 5879
5863 5880 The following table details the behavior of remove for different
5864 5881 file states (columns) and option combinations (rows). The file
5865 5882 states are Added [A], Clean [C], Modified [M] and Missing [!]
5866 5883 (as reported by :hg:`status`). The actions are Warn, Remove
5867 5884 (from branch) and Delete (from disk):
5868 5885
5869 5886 ========= == == == ==
5870 5887 opt/state A C M !
5871 5888 ========= == == == ==
5872 5889 none W RD W R
5873 5890 -f R RD RD R
5874 5891 -A W W W R
5875 5892 -Af R R R R
5876 5893 ========= == == == ==
5877 5894
5878 5895 .. note::
5879 5896
5880 5897 :hg:`remove` never deletes files in Added [A] state from the
5881 5898 working directory, not even if ``--force`` is specified.
5882 5899
5883 5900 Returns 0 on success, 1 if any warnings encountered.
5884 5901 """
5885 5902
5886 5903 opts = pycompat.byteskwargs(opts)
5887 5904 after, force = opts.get(b'after'), opts.get(b'force')
5888 5905 dryrun = opts.get(b'dry_run')
5889 5906 if not pats and not after:
5890 5907 raise error.InputError(_(b'no files specified'))
5891 5908
5892 5909 m = scmutil.match(repo[None], pats, opts)
5893 5910 subrepos = opts.get(b'subrepos')
5894 5911 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5895 5912 return cmdutil.remove(
5896 5913 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5897 5914 )
5898 5915
5899 5916
5900 5917 @command(
5901 5918 b'rename|move|mv',
5902 5919 [
5903 5920 (b'', b'forget', None, _(b'unmark a destination file as renamed')),
5904 5921 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5905 5922 (
5906 5923 b'',
5907 5924 b'at-rev',
5908 5925 b'',
5909 5926 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5910 5927 _(b'REV'),
5911 5928 ),
5912 5929 (
5913 5930 b'f',
5914 5931 b'force',
5915 5932 None,
5916 5933 _(b'forcibly move over an existing managed file'),
5917 5934 ),
5918 5935 ]
5919 5936 + walkopts
5920 5937 + dryrunopts,
5921 5938 _(b'[OPTION]... SOURCE... DEST'),
5922 5939 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5923 5940 )
5924 5941 def rename(ui, repo, *pats, **opts):
5925 5942 """rename files; equivalent of copy + remove
5926 5943
5927 5944 Mark dest as copies of sources; mark sources for deletion. If dest
5928 5945 is a directory, copies are put in that directory. If dest is a
5929 5946 file, there can only be one source.
5930 5947
5931 5948 By default, this command copies the contents of files as they
5932 5949 exist in the working directory. If invoked with -A/--after, the
5933 5950 operation is recorded, but no copying is performed.
5934 5951
5935 5952 To undo marking a destination file as renamed, use --forget. With that
5936 5953 option, all given (positional) arguments are unmarked as renames. The
5937 5954 destination file(s) will be left in place (still tracked). The source
5938 5955 file(s) will not be restored. Note that :hg:`rename --forget` behaves
5939 5956 the same way as :hg:`copy --forget`.
5940 5957
5941 5958 This command takes effect with the next commit by default.
5942 5959
5943 5960 Returns 0 on success, 1 if errors are encountered.
5944 5961 """
5945 5962 opts = pycompat.byteskwargs(opts)
5946 5963 with repo.wlock():
5947 5964 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5948 5965
5949 5966
5950 5967 @command(
5951 5968 b'resolve',
5952 5969 [
5953 5970 (b'a', b'all', None, _(b'select all unresolved files')),
5954 5971 (b'l', b'list', None, _(b'list state of files needing merge')),
5955 5972 (b'm', b'mark', None, _(b'mark files as resolved')),
5956 5973 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5957 5974 (b'n', b'no-status', None, _(b'hide status prefix')),
5958 5975 (b'', b're-merge', None, _(b're-merge files')),
5959 5976 ]
5960 5977 + mergetoolopts
5961 5978 + walkopts
5962 5979 + formatteropts,
5963 5980 _(b'[OPTION]... [FILE]...'),
5964 5981 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5965 5982 inferrepo=True,
5966 5983 )
5967 5984 def resolve(ui, repo, *pats, **opts):
5968 5985 """redo merges or set/view the merge status of files
5969 5986
5970 5987 Merges with unresolved conflicts are often the result of
5971 5988 non-interactive merging using the ``internal:merge`` configuration
5972 5989 setting, or a command-line merge tool like ``diff3``. The resolve
5973 5990 command is used to manage the files involved in a merge, after
5974 5991 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5975 5992 working directory must have two parents). See :hg:`help
5976 5993 merge-tools` for information on configuring merge tools.
5977 5994
5978 5995 The resolve command can be used in the following ways:
5979 5996
5980 5997 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5981 5998 the specified files, discarding any previous merge attempts. Re-merging
5982 5999 is not performed for files already marked as resolved. Use ``--all/-a``
5983 6000 to select all unresolved files. ``--tool`` can be used to specify
5984 6001 the merge tool used for the given files. It overrides the HGMERGE
5985 6002 environment variable and your configuration files. Previous file
5986 6003 contents are saved with a ``.orig`` suffix.
5987 6004
5988 6005 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5989 6006 (e.g. after having manually fixed-up the files). The default is
5990 6007 to mark all unresolved files.
5991 6008
5992 6009 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5993 6010 default is to mark all resolved files.
5994 6011
5995 6012 - :hg:`resolve -l`: list files which had or still have conflicts.
5996 6013 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5997 6014 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5998 6015 the list. See :hg:`help filesets` for details.
5999 6016
6000 6017 .. note::
6001 6018
6002 6019 Mercurial will not let you commit files with unresolved merge
6003 6020 conflicts. You must use :hg:`resolve -m ...` before you can
6004 6021 commit after a conflicting merge.
6005 6022
6006 6023 .. container:: verbose
6007 6024
6008 6025 Template:
6009 6026
6010 6027 The following keywords are supported in addition to the common template
6011 6028 keywords and functions. See also :hg:`help templates`.
6012 6029
6013 6030 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
6014 6031 :path: String. Repository-absolute path of the file.
6015 6032
6016 6033 Returns 0 on success, 1 if any files fail a resolve attempt.
6017 6034 """
6018 6035
6019 6036 opts = pycompat.byteskwargs(opts)
6020 6037 confirm = ui.configbool(b'commands', b'resolve.confirm')
6021 6038 flaglist = b'all mark unmark list no_status re_merge'.split()
6022 6039 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
6023 6040
6024 6041 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
6025 6042 if actioncount > 1:
6026 6043 raise error.InputError(_(b"too many actions specified"))
6027 6044 elif actioncount == 0 and ui.configbool(
6028 6045 b'commands', b'resolve.explicit-re-merge'
6029 6046 ):
6030 6047 hint = _(b'use --mark, --unmark, --list or --re-merge')
6031 6048 raise error.InputError(_(b'no action specified'), hint=hint)
6032 6049 if pats and all:
6033 6050 raise error.InputError(_(b"can't specify --all and patterns"))
6034 6051 if not (all or pats or show or mark or unmark):
6035 6052 raise error.InputError(
6036 6053 _(b'no files or directories specified'),
6037 6054 hint=b'use --all to re-merge all unresolved files',
6038 6055 )
6039 6056
6040 6057 if confirm:
6041 6058 if all:
6042 6059 if ui.promptchoice(
6043 6060 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
6044 6061 ):
6045 6062 raise error.CanceledError(_(b'user quit'))
6046 6063 if mark and not pats:
6047 6064 if ui.promptchoice(
6048 6065 _(
6049 6066 b'mark all unresolved files as resolved (yn)?'
6050 6067 b'$$ &Yes $$ &No'
6051 6068 )
6052 6069 ):
6053 6070 raise error.CanceledError(_(b'user quit'))
6054 6071 if unmark and not pats:
6055 6072 if ui.promptchoice(
6056 6073 _(
6057 6074 b'mark all resolved files as unresolved (yn)?'
6058 6075 b'$$ &Yes $$ &No'
6059 6076 )
6060 6077 ):
6061 6078 raise error.CanceledError(_(b'user quit'))
6062 6079
6063 6080 uipathfn = scmutil.getuipathfn(repo)
6064 6081
6065 6082 if show:
6066 6083 ui.pager(b'resolve')
6067 6084 fm = ui.formatter(b'resolve', opts)
6068 6085 ms = mergestatemod.mergestate.read(repo)
6069 6086 wctx = repo[None]
6070 6087 m = scmutil.match(wctx, pats, opts)
6071 6088
6072 6089 # Labels and keys based on merge state. Unresolved path conflicts show
6073 6090 # as 'P'. Resolved path conflicts show as 'R', the same as normal
6074 6091 # resolved conflicts.
6075 6092 mergestateinfo = {
6076 6093 mergestatemod.MERGE_RECORD_UNRESOLVED: (
6077 6094 b'resolve.unresolved',
6078 6095 b'U',
6079 6096 ),
6080 6097 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
6081 6098 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
6082 6099 b'resolve.unresolved',
6083 6100 b'P',
6084 6101 ),
6085 6102 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
6086 6103 b'resolve.resolved',
6087 6104 b'R',
6088 6105 ),
6089 6106 }
6090 6107
6091 6108 for f in ms:
6092 6109 if not m(f):
6093 6110 continue
6094 6111
6095 6112 label, key = mergestateinfo[ms[f]]
6096 6113 fm.startitem()
6097 6114 fm.context(ctx=wctx)
6098 6115 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
6099 6116 fm.data(path=f)
6100 6117 fm.plain(b'%s\n' % uipathfn(f), label=label)
6101 6118 fm.end()
6102 6119 return 0
6103 6120
6104 6121 with repo.wlock():
6105 6122 ms = mergestatemod.mergestate.read(repo)
6106 6123
6107 6124 if not (ms.active() or repo.dirstate.p2() != repo.nullid):
6108 6125 raise error.StateError(
6109 6126 _(b'resolve command not applicable when not merging')
6110 6127 )
6111 6128
6112 6129 wctx = repo[None]
6113 6130 m = scmutil.match(wctx, pats, opts)
6114 6131 ret = 0
6115 6132 didwork = False
6116 6133
6117 6134 tocomplete = []
6118 6135 hasconflictmarkers = []
6119 6136 if mark:
6120 6137 markcheck = ui.config(b'commands', b'resolve.mark-check')
6121 6138 if markcheck not in [b'warn', b'abort']:
6122 6139 # Treat all invalid / unrecognized values as 'none'.
6123 6140 markcheck = False
6124 6141 for f in ms:
6125 6142 if not m(f):
6126 6143 continue
6127 6144
6128 6145 didwork = True
6129 6146
6130 6147 # path conflicts must be resolved manually
6131 6148 if ms[f] in (
6132 6149 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
6133 6150 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
6134 6151 ):
6135 6152 if mark:
6136 6153 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
6137 6154 elif unmark:
6138 6155 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
6139 6156 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
6140 6157 ui.warn(
6141 6158 _(b'%s: path conflict must be resolved manually\n')
6142 6159 % uipathfn(f)
6143 6160 )
6144 6161 continue
6145 6162
6146 6163 if mark:
6147 6164 if markcheck:
6148 6165 fdata = repo.wvfs.tryread(f)
6149 6166 if (
6150 6167 filemerge.hasconflictmarkers(fdata)
6151 6168 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
6152 6169 ):
6153 6170 hasconflictmarkers.append(f)
6154 6171 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
6155 6172 elif unmark:
6156 6173 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
6157 6174 else:
6158 6175 # backup pre-resolve (merge uses .orig for its own purposes)
6159 6176 a = repo.wjoin(f)
6160 6177 try:
6161 6178 util.copyfile(a, a + b".resolve")
6162 6179 except (IOError, OSError) as inst:
6163 6180 if inst.errno != errno.ENOENT:
6164 6181 raise
6165 6182
6166 6183 try:
6167 6184 # preresolve file
6168 6185 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6169 6186 with ui.configoverride(overrides, b'resolve'):
6170 6187 complete, r = ms.preresolve(f, wctx)
6171 6188 if not complete:
6172 6189 tocomplete.append(f)
6173 6190 elif r:
6174 6191 ret = 1
6175 6192 finally:
6176 6193 ms.commit()
6177 6194
6178 6195 # replace filemerge's .orig file with our resolve file, but only
6179 6196 # for merges that are complete
6180 6197 if complete:
6181 6198 try:
6182 6199 util.rename(
6183 6200 a + b".resolve", scmutil.backuppath(ui, repo, f)
6184 6201 )
6185 6202 except OSError as inst:
6186 6203 if inst.errno != errno.ENOENT:
6187 6204 raise
6188 6205
6189 6206 if hasconflictmarkers:
6190 6207 ui.warn(
6191 6208 _(
6192 6209 b'warning: the following files still have conflict '
6193 6210 b'markers:\n'
6194 6211 )
6195 6212 + b''.join(
6196 6213 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6197 6214 )
6198 6215 )
6199 6216 if markcheck == b'abort' and not all and not pats:
6200 6217 raise error.StateError(
6201 6218 _(b'conflict markers detected'),
6202 6219 hint=_(b'use --all to mark anyway'),
6203 6220 )
6204 6221
6205 6222 for f in tocomplete:
6206 6223 try:
6207 6224 # resolve file
6208 6225 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6209 6226 with ui.configoverride(overrides, b'resolve'):
6210 6227 r = ms.resolve(f, wctx)
6211 6228 if r:
6212 6229 ret = 1
6213 6230 finally:
6214 6231 ms.commit()
6215 6232
6216 6233 # replace filemerge's .orig file with our resolve file
6217 6234 a = repo.wjoin(f)
6218 6235 try:
6219 6236 util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6220 6237 except OSError as inst:
6221 6238 if inst.errno != errno.ENOENT:
6222 6239 raise
6223 6240
6224 6241 ms.commit()
6225 6242 branchmerge = repo.dirstate.p2() != repo.nullid
6226 6243 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6227 6244
6228 6245 if not didwork and pats:
6229 6246 hint = None
6230 6247 if not any([p for p in pats if p.find(b':') >= 0]):
6231 6248 pats = [b'path:%s' % p for p in pats]
6232 6249 m = scmutil.match(wctx, pats, opts)
6233 6250 for f in ms:
6234 6251 if not m(f):
6235 6252 continue
6236 6253
6237 6254 def flag(o):
6238 6255 if o == b're_merge':
6239 6256 return b'--re-merge '
6240 6257 return b'-%s ' % o[0:1]
6241 6258
6242 6259 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6243 6260 hint = _(b"(try: hg resolve %s%s)\n") % (
6244 6261 flags,
6245 6262 b' '.join(pats),
6246 6263 )
6247 6264 break
6248 6265 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6249 6266 if hint:
6250 6267 ui.warn(hint)
6251 6268
6252 6269 unresolvedf = ms.unresolvedcount()
6253 6270 if not unresolvedf:
6254 6271 ui.status(_(b'(no more unresolved files)\n'))
6255 6272 cmdutil.checkafterresolved(repo)
6256 6273
6257 6274 return ret
6258 6275
6259 6276
6260 6277 @command(
6261 6278 b'revert',
6262 6279 [
6263 6280 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6264 6281 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6265 6282 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6266 6283 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6267 6284 (b'i', b'interactive', None, _(b'interactively select the changes')),
6268 6285 ]
6269 6286 + walkopts
6270 6287 + dryrunopts,
6271 6288 _(b'[OPTION]... [-r REV] [NAME]...'),
6272 6289 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6273 6290 )
6274 6291 def revert(ui, repo, *pats, **opts):
6275 6292 """restore files to their checkout state
6276 6293
6277 6294 .. note::
6278 6295
6279 6296 To check out earlier revisions, you should use :hg:`update REV`.
6280 6297 To cancel an uncommitted merge (and lose your changes),
6281 6298 use :hg:`merge --abort`.
6282 6299
6283 6300 With no revision specified, revert the specified files or directories
6284 6301 to the contents they had in the parent of the working directory.
6285 6302 This restores the contents of files to an unmodified
6286 6303 state and unschedules adds, removes, copies, and renames. If the
6287 6304 working directory has two parents, you must explicitly specify a
6288 6305 revision.
6289 6306
6290 6307 Using the -r/--rev or -d/--date options, revert the given files or
6291 6308 directories to their states as of a specific revision. Because
6292 6309 revert does not change the working directory parents, this will
6293 6310 cause these files to appear modified. This can be helpful to "back
6294 6311 out" some or all of an earlier change. See :hg:`backout` for a
6295 6312 related method.
6296 6313
6297 6314 Modified files are saved with a .orig suffix before reverting.
6298 6315 To disable these backups, use --no-backup. It is possible to store
6299 6316 the backup files in a custom directory relative to the root of the
6300 6317 repository by setting the ``ui.origbackuppath`` configuration
6301 6318 option.
6302 6319
6303 6320 See :hg:`help dates` for a list of formats valid for -d/--date.
6304 6321
6305 6322 See :hg:`help backout` for a way to reverse the effect of an
6306 6323 earlier changeset.
6307 6324
6308 6325 Returns 0 on success.
6309 6326 """
6310 6327
6311 6328 opts = pycompat.byteskwargs(opts)
6312 6329 if opts.get(b"date"):
6313 6330 cmdutil.check_incompatible_arguments(opts, b'date', [b'rev'])
6314 6331 opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6315 6332
6316 6333 parent, p2 = repo.dirstate.parents()
6317 6334 if not opts.get(b'rev') and p2 != repo.nullid:
6318 6335 # revert after merge is a trap for new users (issue2915)
6319 6336 raise error.InputError(
6320 6337 _(b'uncommitted merge with no revision specified'),
6321 6338 hint=_(b"use 'hg update' or see 'hg help revert'"),
6322 6339 )
6323 6340
6324 6341 rev = opts.get(b'rev')
6325 6342 if rev:
6326 6343 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6327 6344 ctx = scmutil.revsingle(repo, rev)
6328 6345
6329 6346 if not (
6330 6347 pats
6331 6348 or opts.get(b'include')
6332 6349 or opts.get(b'exclude')
6333 6350 or opts.get(b'all')
6334 6351 or opts.get(b'interactive')
6335 6352 ):
6336 6353 msg = _(b"no files or directories specified")
6337 6354 if p2 != repo.nullid:
6338 6355 hint = _(
6339 6356 b"uncommitted merge, use --all to discard all changes,"
6340 6357 b" or 'hg update -C .' to abort the merge"
6341 6358 )
6342 6359 raise error.InputError(msg, hint=hint)
6343 6360 dirty = any(repo.status())
6344 6361 node = ctx.node()
6345 6362 if node != parent:
6346 6363 if dirty:
6347 6364 hint = (
6348 6365 _(
6349 6366 b"uncommitted changes, use --all to discard all"
6350 6367 b" changes, or 'hg update %d' to update"
6351 6368 )
6352 6369 % ctx.rev()
6353 6370 )
6354 6371 else:
6355 6372 hint = (
6356 6373 _(
6357 6374 b"use --all to revert all files,"
6358 6375 b" or 'hg update %d' to update"
6359 6376 )
6360 6377 % ctx.rev()
6361 6378 )
6362 6379 elif dirty:
6363 6380 hint = _(b"uncommitted changes, use --all to discard all changes")
6364 6381 else:
6365 6382 hint = _(b"use --all to revert all files")
6366 6383 raise error.InputError(msg, hint=hint)
6367 6384
6368 6385 return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6369 6386
6370 6387
6371 6388 @command(
6372 6389 b'rollback',
6373 6390 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6374 6391 helpcategory=command.CATEGORY_MAINTENANCE,
6375 6392 )
6376 6393 def rollback(ui, repo, **opts):
6377 6394 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6378 6395
6379 6396 Please use :hg:`commit --amend` instead of rollback to correct
6380 6397 mistakes in the last commit.
6381 6398
6382 6399 This command should be used with care. There is only one level of
6383 6400 rollback, and there is no way to undo a rollback. It will also
6384 6401 restore the dirstate at the time of the last transaction, losing
6385 6402 any dirstate changes since that time. This command does not alter
6386 6403 the working directory.
6387 6404
6388 6405 Transactions are used to encapsulate the effects of all commands
6389 6406 that create new changesets or propagate existing changesets into a
6390 6407 repository.
6391 6408
6392 6409 .. container:: verbose
6393 6410
6394 6411 For example, the following commands are transactional, and their
6395 6412 effects can be rolled back:
6396 6413
6397 6414 - commit
6398 6415 - import
6399 6416 - pull
6400 6417 - push (with this repository as the destination)
6401 6418 - unbundle
6402 6419
6403 6420 To avoid permanent data loss, rollback will refuse to rollback a
6404 6421 commit transaction if it isn't checked out. Use --force to
6405 6422 override this protection.
6406 6423
6407 6424 The rollback command can be entirely disabled by setting the
6408 6425 ``ui.rollback`` configuration setting to false. If you're here
6409 6426 because you want to use rollback and it's disabled, you can
6410 6427 re-enable the command by setting ``ui.rollback`` to true.
6411 6428
6412 6429 This command is not intended for use on public repositories. Once
6413 6430 changes are visible for pull by other users, rolling a transaction
6414 6431 back locally is ineffective (someone else may already have pulled
6415 6432 the changes). Furthermore, a race is possible with readers of the
6416 6433 repository; for example an in-progress pull from the repository
6417 6434 may fail if a rollback is performed.
6418 6435
6419 6436 Returns 0 on success, 1 if no rollback data is available.
6420 6437 """
6421 6438 if not ui.configbool(b'ui', b'rollback'):
6422 6439 raise error.Abort(
6423 6440 _(b'rollback is disabled because it is unsafe'),
6424 6441 hint=b'see `hg help -v rollback` for information',
6425 6442 )
6426 6443 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6427 6444
6428 6445
6429 6446 @command(
6430 6447 b'root',
6431 6448 [] + formatteropts,
6432 6449 intents={INTENT_READONLY},
6433 6450 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6434 6451 )
6435 6452 def root(ui, repo, **opts):
6436 6453 """print the root (top) of the current working directory
6437 6454
6438 6455 Print the root directory of the current repository.
6439 6456
6440 6457 .. container:: verbose
6441 6458
6442 6459 Template:
6443 6460
6444 6461 The following keywords are supported in addition to the common template
6445 6462 keywords and functions. See also :hg:`help templates`.
6446 6463
6447 6464 :hgpath: String. Path to the .hg directory.
6448 6465 :storepath: String. Path to the directory holding versioned data.
6449 6466
6450 6467 Returns 0 on success.
6451 6468 """
6452 6469 opts = pycompat.byteskwargs(opts)
6453 6470 with ui.formatter(b'root', opts) as fm:
6454 6471 fm.startitem()
6455 6472 fm.write(b'reporoot', b'%s\n', repo.root)
6456 6473 fm.data(hgpath=repo.path, storepath=repo.spath)
6457 6474
6458 6475
6459 6476 @command(
6460 6477 b'serve',
6461 6478 [
6462 6479 (
6463 6480 b'A',
6464 6481 b'accesslog',
6465 6482 b'',
6466 6483 _(b'name of access log file to write to'),
6467 6484 _(b'FILE'),
6468 6485 ),
6469 6486 (b'd', b'daemon', None, _(b'run server in background')),
6470 6487 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6471 6488 (
6472 6489 b'E',
6473 6490 b'errorlog',
6474 6491 b'',
6475 6492 _(b'name of error log file to write to'),
6476 6493 _(b'FILE'),
6477 6494 ),
6478 6495 # use string type, then we can check if something was passed
6479 6496 (
6480 6497 b'p',
6481 6498 b'port',
6482 6499 b'',
6483 6500 _(b'port to listen on (default: 8000)'),
6484 6501 _(b'PORT'),
6485 6502 ),
6486 6503 (
6487 6504 b'a',
6488 6505 b'address',
6489 6506 b'',
6490 6507 _(b'address to listen on (default: all interfaces)'),
6491 6508 _(b'ADDR'),
6492 6509 ),
6493 6510 (
6494 6511 b'',
6495 6512 b'prefix',
6496 6513 b'',
6497 6514 _(b'prefix path to serve from (default: server root)'),
6498 6515 _(b'PREFIX'),
6499 6516 ),
6500 6517 (
6501 6518 b'n',
6502 6519 b'name',
6503 6520 b'',
6504 6521 _(b'name to show in web pages (default: working directory)'),
6505 6522 _(b'NAME'),
6506 6523 ),
6507 6524 (
6508 6525 b'',
6509 6526 b'web-conf',
6510 6527 b'',
6511 6528 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6512 6529 _(b'FILE'),
6513 6530 ),
6514 6531 (
6515 6532 b'',
6516 6533 b'webdir-conf',
6517 6534 b'',
6518 6535 _(b'name of the hgweb config file (DEPRECATED)'),
6519 6536 _(b'FILE'),
6520 6537 ),
6521 6538 (
6522 6539 b'',
6523 6540 b'pid-file',
6524 6541 b'',
6525 6542 _(b'name of file to write process ID to'),
6526 6543 _(b'FILE'),
6527 6544 ),
6528 6545 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6529 6546 (
6530 6547 b'',
6531 6548 b'cmdserver',
6532 6549 b'',
6533 6550 _(b'for remote clients (ADVANCED)'),
6534 6551 _(b'MODE'),
6535 6552 ),
6536 6553 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6537 6554 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6538 6555 (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6539 6556 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6540 6557 (b'', b'print-url', None, _(b'start and print only the URL')),
6541 6558 ]
6542 6559 + subrepoopts,
6543 6560 _(b'[OPTION]...'),
6544 6561 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6545 6562 helpbasic=True,
6546 6563 optionalrepo=True,
6547 6564 )
6548 6565 def serve(ui, repo, **opts):
6549 6566 """start stand-alone webserver
6550 6567
6551 6568 Start a local HTTP repository browser and pull server. You can use
6552 6569 this for ad-hoc sharing and browsing of repositories. It is
6553 6570 recommended to use a real web server to serve a repository for
6554 6571 longer periods of time.
6555 6572
6556 6573 Please note that the server does not implement access control.
6557 6574 This means that, by default, anybody can read from the server and
6558 6575 nobody can write to it by default. Set the ``web.allow-push``
6559 6576 option to ``*`` to allow everybody to push to the server. You
6560 6577 should use a real web server if you need to authenticate users.
6561 6578
6562 6579 By default, the server logs accesses to stdout and errors to
6563 6580 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6564 6581 files.
6565 6582
6566 6583 To have the server choose a free port number to listen on, specify
6567 6584 a port number of 0; in this case, the server will print the port
6568 6585 number it uses.
6569 6586
6570 6587 Returns 0 on success.
6571 6588 """
6572 6589
6573 6590 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6574 6591 opts = pycompat.byteskwargs(opts)
6575 6592 if opts[b"print_url"] and ui.verbose:
6576 6593 raise error.InputError(_(b"cannot use --print-url with --verbose"))
6577 6594
6578 6595 if opts[b"stdio"]:
6579 6596 if repo is None:
6580 6597 raise error.RepoError(
6581 6598 _(b"there is no Mercurial repository here (.hg not found)")
6582 6599 )
6583 6600 s = wireprotoserver.sshserver(ui, repo)
6584 6601 s.serve_forever()
6585 6602 return
6586 6603
6587 6604 service = server.createservice(ui, repo, opts)
6588 6605 return server.runservice(opts, initfn=service.init, runfn=service.run)
6589 6606
6590 6607
6591 6608 @command(
6592 6609 b'shelve',
6593 6610 [
6594 6611 (
6595 6612 b'A',
6596 6613 b'addremove',
6597 6614 None,
6598 6615 _(b'mark new/missing files as added/removed before shelving'),
6599 6616 ),
6600 6617 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6601 6618 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6602 6619 (
6603 6620 b'',
6604 6621 b'date',
6605 6622 b'',
6606 6623 _(b'shelve with the specified commit date'),
6607 6624 _(b'DATE'),
6608 6625 ),
6609 6626 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6610 6627 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6611 6628 (
6612 6629 b'k',
6613 6630 b'keep',
6614 6631 False,
6615 6632 _(b'shelve, but keep changes in the working directory'),
6616 6633 ),
6617 6634 (b'l', b'list', None, _(b'list current shelves')),
6618 6635 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6619 6636 (
6620 6637 b'n',
6621 6638 b'name',
6622 6639 b'',
6623 6640 _(b'use the given name for the shelved commit'),
6624 6641 _(b'NAME'),
6625 6642 ),
6626 6643 (
6627 6644 b'p',
6628 6645 b'patch',
6629 6646 None,
6630 6647 _(
6631 6648 b'output patches for changes (provide the names of the shelved '
6632 6649 b'changes as positional arguments)'
6633 6650 ),
6634 6651 ),
6635 6652 (b'i', b'interactive', None, _(b'interactive mode')),
6636 6653 (
6637 6654 b'',
6638 6655 b'stat',
6639 6656 None,
6640 6657 _(
6641 6658 b'output diffstat-style summary of changes (provide the names of '
6642 6659 b'the shelved changes as positional arguments)'
6643 6660 ),
6644 6661 ),
6645 6662 ]
6646 6663 + cmdutil.walkopts,
6647 6664 _(b'hg shelve [OPTION]... [FILE]...'),
6648 6665 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6649 6666 )
6650 6667 def shelve(ui, repo, *pats, **opts):
6651 6668 """save and set aside changes from the working directory
6652 6669
6653 6670 Shelving takes files that "hg status" reports as not clean, saves
6654 6671 the modifications to a bundle (a shelved change), and reverts the
6655 6672 files so that their state in the working directory becomes clean.
6656 6673
6657 6674 To restore these changes to the working directory, using "hg
6658 6675 unshelve"; this will work even if you switch to a different
6659 6676 commit.
6660 6677
6661 6678 When no files are specified, "hg shelve" saves all not-clean
6662 6679 files. If specific files or directories are named, only changes to
6663 6680 those files are shelved.
6664 6681
6665 6682 In bare shelve (when no files are specified, without interactive,
6666 6683 include and exclude option), shelving remembers information if the
6667 6684 working directory was on newly created branch, in other words working
6668 6685 directory was on different branch than its first parent. In this
6669 6686 situation unshelving restores branch information to the working directory.
6670 6687
6671 6688 Each shelved change has a name that makes it easier to find later.
6672 6689 The name of a shelved change defaults to being based on the active
6673 6690 bookmark, or if there is no active bookmark, the current named
6674 6691 branch. To specify a different name, use ``--name``.
6675 6692
6676 6693 To see a list of existing shelved changes, use the ``--list``
6677 6694 option. For each shelved change, this will print its name, age,
6678 6695 and description; use ``--patch`` or ``--stat`` for more details.
6679 6696
6680 6697 To delete specific shelved changes, use ``--delete``. To delete
6681 6698 all shelved changes, use ``--cleanup``.
6682 6699 """
6683 6700 opts = pycompat.byteskwargs(opts)
6684 6701 allowables = [
6685 6702 (b'addremove', {b'create'}), # 'create' is pseudo action
6686 6703 (b'unknown', {b'create'}),
6687 6704 (b'cleanup', {b'cleanup'}),
6688 6705 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6689 6706 (b'delete', {b'delete'}),
6690 6707 (b'edit', {b'create'}),
6691 6708 (b'keep', {b'create'}),
6692 6709 (b'list', {b'list'}),
6693 6710 (b'message', {b'create'}),
6694 6711 (b'name', {b'create'}),
6695 6712 (b'patch', {b'patch', b'list'}),
6696 6713 (b'stat', {b'stat', b'list'}),
6697 6714 ]
6698 6715
6699 6716 def checkopt(opt):
6700 6717 if opts.get(opt):
6701 6718 for i, allowable in allowables:
6702 6719 if opts[i] and opt not in allowable:
6703 6720 raise error.InputError(
6704 6721 _(
6705 6722 b"options '--%s' and '--%s' may not be "
6706 6723 b"used together"
6707 6724 )
6708 6725 % (opt, i)
6709 6726 )
6710 6727 return True
6711 6728
6712 6729 if checkopt(b'cleanup'):
6713 6730 if pats:
6714 6731 raise error.InputError(
6715 6732 _(b"cannot specify names when using '--cleanup'")
6716 6733 )
6717 6734 return shelvemod.cleanupcmd(ui, repo)
6718 6735 elif checkopt(b'delete'):
6719 6736 return shelvemod.deletecmd(ui, repo, pats)
6720 6737 elif checkopt(b'list'):
6721 6738 return shelvemod.listcmd(ui, repo, pats, opts)
6722 6739 elif checkopt(b'patch') or checkopt(b'stat'):
6723 6740 return shelvemod.patchcmds(ui, repo, pats, opts)
6724 6741 else:
6725 6742 return shelvemod.createcmd(ui, repo, pats, opts)
6726 6743
6727 6744
6728 6745 _NOTTERSE = b'nothing'
6729 6746
6730 6747
6731 6748 @command(
6732 6749 b'status|st',
6733 6750 [
6734 6751 (b'A', b'all', None, _(b'show status of all files')),
6735 6752 (b'm', b'modified', None, _(b'show only modified files')),
6736 6753 (b'a', b'added', None, _(b'show only added files')),
6737 6754 (b'r', b'removed', None, _(b'show only removed files')),
6738 6755 (b'd', b'deleted', None, _(b'show only missing files')),
6739 6756 (b'c', b'clean', None, _(b'show only files without changes')),
6740 6757 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6741 6758 (b'i', b'ignored', None, _(b'show only ignored files')),
6742 6759 (b'n', b'no-status', None, _(b'hide status prefix')),
6743 6760 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6744 6761 (
6745 6762 b'C',
6746 6763 b'copies',
6747 6764 None,
6748 6765 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6749 6766 ),
6750 6767 (
6751 6768 b'0',
6752 6769 b'print0',
6753 6770 None,
6754 6771 _(b'end filenames with NUL, for use with xargs'),
6755 6772 ),
6756 6773 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6757 6774 (
6758 6775 b'',
6759 6776 b'change',
6760 6777 b'',
6761 6778 _(b'list the changed files of a revision'),
6762 6779 _(b'REV'),
6763 6780 ),
6764 6781 ]
6765 6782 + walkopts
6766 6783 + subrepoopts
6767 6784 + formatteropts,
6768 6785 _(b'[OPTION]... [FILE]...'),
6769 6786 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6770 6787 helpbasic=True,
6771 6788 inferrepo=True,
6772 6789 intents={INTENT_READONLY},
6773 6790 )
6774 6791 def status(ui, repo, *pats, **opts):
6775 6792 """show changed files in the working directory
6776 6793
6777 6794 Show status of files in the repository. If names are given, only
6778 6795 files that match are shown. Files that are clean or ignored or
6779 6796 the source of a copy/move operation, are not listed unless
6780 6797 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6781 6798 Unless options described with "show only ..." are given, the
6782 6799 options -mardu are used.
6783 6800
6784 6801 Option -q/--quiet hides untracked (unknown and ignored) files
6785 6802 unless explicitly requested with -u/--unknown or -i/--ignored.
6786 6803
6787 6804 .. note::
6788 6805
6789 6806 :hg:`status` may appear to disagree with diff if permissions have
6790 6807 changed or a merge has occurred. The standard diff format does
6791 6808 not report permission changes and diff only reports changes
6792 6809 relative to one merge parent.
6793 6810
6794 6811 If one revision is given, it is used as the base revision.
6795 6812 If two revisions are given, the differences between them are
6796 6813 shown. The --change option can also be used as a shortcut to list
6797 6814 the changed files of a revision from its first parent.
6798 6815
6799 6816 The codes used to show the status of files are::
6800 6817
6801 6818 M = modified
6802 6819 A = added
6803 6820 R = removed
6804 6821 C = clean
6805 6822 ! = missing (deleted by non-hg command, but still tracked)
6806 6823 ? = not tracked
6807 6824 I = ignored
6808 6825 = origin of the previous file (with --copies)
6809 6826
6810 6827 .. container:: verbose
6811 6828
6812 6829 The -t/--terse option abbreviates the output by showing only the directory
6813 6830 name if all the files in it share the same status. The option takes an
6814 6831 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6815 6832 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6816 6833 for 'ignored' and 'c' for clean.
6817 6834
6818 6835 It abbreviates only those statuses which are passed. Note that clean and
6819 6836 ignored files are not displayed with '--terse ic' unless the -c/--clean
6820 6837 and -i/--ignored options are also used.
6821 6838
6822 6839 The -v/--verbose option shows information when the repository is in an
6823 6840 unfinished merge, shelve, rebase state etc. You can have this behavior
6824 6841 turned on by default by enabling the ``commands.status.verbose`` option.
6825 6842
6826 6843 You can skip displaying some of these states by setting
6827 6844 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6828 6845 'histedit', 'merge', 'rebase', or 'unshelve'.
6829 6846
6830 6847 Template:
6831 6848
6832 6849 The following keywords are supported in addition to the common template
6833 6850 keywords and functions. See also :hg:`help templates`.
6834 6851
6835 6852 :path: String. Repository-absolute path of the file.
6836 6853 :source: String. Repository-absolute path of the file originated from.
6837 6854 Available if ``--copies`` is specified.
6838 6855 :status: String. Character denoting file's status.
6839 6856
6840 6857 Examples:
6841 6858
6842 6859 - show changes in the working directory relative to a
6843 6860 changeset::
6844 6861
6845 6862 hg status --rev 9353
6846 6863
6847 6864 - show changes in the working directory relative to the
6848 6865 current directory (see :hg:`help patterns` for more information)::
6849 6866
6850 6867 hg status re:
6851 6868
6852 6869 - show all changes including copies in an existing changeset::
6853 6870
6854 6871 hg status --copies --change 9353
6855 6872
6856 6873 - get a NUL separated list of added files, suitable for xargs::
6857 6874
6858 6875 hg status -an0
6859 6876
6860 6877 - show more information about the repository status, abbreviating
6861 6878 added, removed, modified, deleted, and untracked paths::
6862 6879
6863 6880 hg status -v -t mardu
6864 6881
6865 6882 Returns 0 on success.
6866 6883
6867 6884 """
6868 6885
6869 6886 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6870 6887 opts = pycompat.byteskwargs(opts)
6871 6888 revs = opts.get(b'rev')
6872 6889 change = opts.get(b'change')
6873 6890 terse = opts.get(b'terse')
6874 6891 if terse is _NOTTERSE:
6875 6892 if revs:
6876 6893 terse = b''
6877 6894 else:
6878 6895 terse = ui.config(b'commands', b'status.terse')
6879 6896
6880 6897 if revs and terse:
6881 6898 msg = _(b'cannot use --terse with --rev')
6882 6899 raise error.InputError(msg)
6883 6900 elif change:
6884 6901 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6885 6902 ctx2 = scmutil.revsingle(repo, change, None)
6886 6903 ctx1 = ctx2.p1()
6887 6904 else:
6888 6905 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6889 6906 ctx1, ctx2 = scmutil.revpair(repo, revs)
6890 6907
6891 6908 forcerelativevalue = None
6892 6909 if ui.hasconfig(b'commands', b'status.relative'):
6893 6910 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6894 6911 uipathfn = scmutil.getuipathfn(
6895 6912 repo,
6896 6913 legacyrelativevalue=bool(pats),
6897 6914 forcerelativevalue=forcerelativevalue,
6898 6915 )
6899 6916
6900 6917 if opts.get(b'print0'):
6901 6918 end = b'\0'
6902 6919 else:
6903 6920 end = b'\n'
6904 6921 states = b'modified added removed deleted unknown ignored clean'.split()
6905 6922 show = [k for k in states if opts.get(k)]
6906 6923 if opts.get(b'all'):
6907 6924 show += ui.quiet and (states[:4] + [b'clean']) or states
6908 6925
6909 6926 if not show:
6910 6927 if ui.quiet:
6911 6928 show = states[:4]
6912 6929 else:
6913 6930 show = states[:5]
6914 6931
6915 6932 m = scmutil.match(ctx2, pats, opts)
6916 6933 if terse:
6917 6934 # we need to compute clean and unknown to terse
6918 6935 stat = repo.status(
6919 6936 ctx1.node(),
6920 6937 ctx2.node(),
6921 6938 m,
6922 6939 b'ignored' in show or b'i' in terse,
6923 6940 clean=True,
6924 6941 unknown=True,
6925 6942 listsubrepos=opts.get(b'subrepos'),
6926 6943 )
6927 6944
6928 6945 stat = cmdutil.tersedir(stat, terse)
6929 6946 else:
6930 6947 stat = repo.status(
6931 6948 ctx1.node(),
6932 6949 ctx2.node(),
6933 6950 m,
6934 6951 b'ignored' in show,
6935 6952 b'clean' in show,
6936 6953 b'unknown' in show,
6937 6954 opts.get(b'subrepos'),
6938 6955 )
6939 6956
6940 6957 changestates = zip(
6941 6958 states,
6942 6959 pycompat.iterbytestr(b'MAR!?IC'),
6943 6960 [getattr(stat, s.decode('utf8')) for s in states],
6944 6961 )
6945 6962
6946 6963 copy = {}
6947 6964 if (
6948 6965 opts.get(b'all')
6949 6966 or opts.get(b'copies')
6950 6967 or ui.configbool(b'ui', b'statuscopies')
6951 6968 ) and not opts.get(b'no_status'):
6952 6969 copy = copies.pathcopies(ctx1, ctx2, m)
6953 6970
6954 6971 morestatus = None
6955 6972 if (
6956 6973 (ui.verbose or ui.configbool(b'commands', b'status.verbose'))
6957 6974 and not ui.plain()
6958 6975 and not opts.get(b'print0')
6959 6976 ):
6960 6977 morestatus = cmdutil.readmorestatus(repo)
6961 6978
6962 6979 ui.pager(b'status')
6963 6980 fm = ui.formatter(b'status', opts)
6964 6981 fmt = b'%s' + end
6965 6982 showchar = not opts.get(b'no_status')
6966 6983
6967 6984 for state, char, files in changestates:
6968 6985 if state in show:
6969 6986 label = b'status.' + state
6970 6987 for f in files:
6971 6988 fm.startitem()
6972 6989 fm.context(ctx=ctx2)
6973 6990 fm.data(itemtype=b'file', path=f)
6974 6991 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6975 6992 fm.plain(fmt % uipathfn(f), label=label)
6976 6993 if f in copy:
6977 6994 fm.data(source=copy[f])
6978 6995 fm.plain(
6979 6996 (b' %s' + end) % uipathfn(copy[f]),
6980 6997 label=b'status.copied',
6981 6998 )
6982 6999 if morestatus:
6983 7000 morestatus.formatfile(f, fm)
6984 7001
6985 7002 if morestatus:
6986 7003 morestatus.formatfooter(fm)
6987 7004 fm.end()
6988 7005
6989 7006
6990 7007 @command(
6991 7008 b'summary|sum',
6992 7009 [(b'', b'remote', None, _(b'check for push and pull'))],
6993 7010 b'[--remote]',
6994 7011 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6995 7012 helpbasic=True,
6996 7013 intents={INTENT_READONLY},
6997 7014 )
6998 7015 def summary(ui, repo, **opts):
6999 7016 """summarize working directory state
7000 7017
7001 7018 This generates a brief summary of the working directory state,
7002 7019 including parents, branch, commit status, phase and available updates.
7003 7020
7004 7021 With the --remote option, this will check the default paths for
7005 7022 incoming and outgoing changes. This can be time-consuming.
7006 7023
7007 7024 Returns 0 on success.
7008 7025 """
7009 7026
7010 7027 opts = pycompat.byteskwargs(opts)
7011 7028 ui.pager(b'summary')
7012 7029 ctx = repo[None]
7013 7030 parents = ctx.parents()
7014 7031 pnode = parents[0].node()
7015 7032 marks = []
7016 7033
7017 7034 try:
7018 7035 ms = mergestatemod.mergestate.read(repo)
7019 7036 except error.UnsupportedMergeRecords as e:
7020 7037 s = b' '.join(e.recordtypes)
7021 7038 ui.warn(
7022 7039 _(b'warning: merge state has unsupported record types: %s\n') % s
7023 7040 )
7024 7041 unresolved = []
7025 7042 else:
7026 7043 unresolved = list(ms.unresolved())
7027 7044
7028 7045 for p in parents:
7029 7046 # label with log.changeset (instead of log.parent) since this
7030 7047 # shows a working directory parent *changeset*:
7031 7048 # i18n: column positioning for "hg summary"
7032 7049 ui.write(
7033 7050 _(b'parent: %d:%s ') % (p.rev(), p),
7034 7051 label=logcmdutil.changesetlabels(p),
7035 7052 )
7036 7053 ui.write(b' '.join(p.tags()), label=b'log.tag')
7037 7054 if p.bookmarks():
7038 7055 marks.extend(p.bookmarks())
7039 7056 if p.rev() == -1:
7040 7057 if not len(repo):
7041 7058 ui.write(_(b' (empty repository)'))
7042 7059 else:
7043 7060 ui.write(_(b' (no revision checked out)'))
7044 7061 if p.obsolete():
7045 7062 ui.write(_(b' (obsolete)'))
7046 7063 if p.isunstable():
7047 7064 instabilities = (
7048 7065 ui.label(instability, b'trouble.%s' % instability)
7049 7066 for instability in p.instabilities()
7050 7067 )
7051 7068 ui.write(b' (' + b', '.join(instabilities) + b')')
7052 7069 ui.write(b'\n')
7053 7070 if p.description():
7054 7071 ui.status(
7055 7072 b' ' + p.description().splitlines()[0].strip() + b'\n',
7056 7073 label=b'log.summary',
7057 7074 )
7058 7075
7059 7076 branch = ctx.branch()
7060 7077 bheads = repo.branchheads(branch)
7061 7078 # i18n: column positioning for "hg summary"
7062 7079 m = _(b'branch: %s\n') % branch
7063 7080 if branch != b'default':
7064 7081 ui.write(m, label=b'log.branch')
7065 7082 else:
7066 7083 ui.status(m, label=b'log.branch')
7067 7084
7068 7085 if marks:
7069 7086 active = repo._activebookmark
7070 7087 # i18n: column positioning for "hg summary"
7071 7088 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
7072 7089 if active is not None:
7073 7090 if active in marks:
7074 7091 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
7075 7092 marks.remove(active)
7076 7093 else:
7077 7094 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
7078 7095 for m in marks:
7079 7096 ui.write(b' ' + m, label=b'log.bookmark')
7080 7097 ui.write(b'\n', label=b'log.bookmark')
7081 7098
7082 7099 status = repo.status(unknown=True)
7083 7100
7084 7101 c = repo.dirstate.copies()
7085 7102 copied, renamed = [], []
7086 7103 for d, s in pycompat.iteritems(c):
7087 7104 if s in status.removed:
7088 7105 status.removed.remove(s)
7089 7106 renamed.append(d)
7090 7107 else:
7091 7108 copied.append(d)
7092 7109 if d in status.added:
7093 7110 status.added.remove(d)
7094 7111
7095 7112 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7096 7113
7097 7114 labels = [
7098 7115 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7099 7116 (ui.label(_(b'%d added'), b'status.added'), status.added),
7100 7117 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7101 7118 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7102 7119 (ui.label(_(b'%d copied'), b'status.copied'), copied),
7103 7120 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7104 7121 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7105 7122 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7106 7123 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7107 7124 ]
7108 7125 t = []
7109 7126 for l, s in labels:
7110 7127 if s:
7111 7128 t.append(l % len(s))
7112 7129
7113 7130 t = b', '.join(t)
7114 7131 cleanworkdir = False
7115 7132
7116 7133 if repo.vfs.exists(b'graftstate'):
7117 7134 t += _(b' (graft in progress)')
7118 7135 if repo.vfs.exists(b'updatestate'):
7119 7136 t += _(b' (interrupted update)')
7120 7137 elif len(parents) > 1:
7121 7138 t += _(b' (merge)')
7122 7139 elif branch != parents[0].branch():
7123 7140 t += _(b' (new branch)')
7124 7141 elif parents[0].closesbranch() and pnode in repo.branchheads(
7125 7142 branch, closed=True
7126 7143 ):
7127 7144 t += _(b' (head closed)')
7128 7145 elif not (
7129 7146 status.modified
7130 7147 or status.added
7131 7148 or status.removed
7132 7149 or renamed
7133 7150 or copied
7134 7151 or subs
7135 7152 ):
7136 7153 t += _(b' (clean)')
7137 7154 cleanworkdir = True
7138 7155 elif pnode not in bheads:
7139 7156 t += _(b' (new branch head)')
7140 7157
7141 7158 if parents:
7142 7159 pendingphase = max(p.phase() for p in parents)
7143 7160 else:
7144 7161 pendingphase = phases.public
7145 7162
7146 7163 if pendingphase > phases.newcommitphase(ui):
7147 7164 t += b' (%s)' % phases.phasenames[pendingphase]
7148 7165
7149 7166 if cleanworkdir:
7150 7167 # i18n: column positioning for "hg summary"
7151 7168 ui.status(_(b'commit: %s\n') % t.strip())
7152 7169 else:
7153 7170 # i18n: column positioning for "hg summary"
7154 7171 ui.write(_(b'commit: %s\n') % t.strip())
7155 7172
7156 7173 # all ancestors of branch heads - all ancestors of parent = new csets
7157 7174 new = len(
7158 7175 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7159 7176 )
7160 7177
7161 7178 if new == 0:
7162 7179 # i18n: column positioning for "hg summary"
7163 7180 ui.status(_(b'update: (current)\n'))
7164 7181 elif pnode not in bheads:
7165 7182 # i18n: column positioning for "hg summary"
7166 7183 ui.write(_(b'update: %d new changesets (update)\n') % new)
7167 7184 else:
7168 7185 # i18n: column positioning for "hg summary"
7169 7186 ui.write(
7170 7187 _(b'update: %d new changesets, %d branch heads (merge)\n')
7171 7188 % (new, len(bheads))
7172 7189 )
7173 7190
7174 7191 t = []
7175 7192 draft = len(repo.revs(b'draft()'))
7176 7193 if draft:
7177 7194 t.append(_(b'%d draft') % draft)
7178 7195 secret = len(repo.revs(b'secret()'))
7179 7196 if secret:
7180 7197 t.append(_(b'%d secret') % secret)
7181 7198
7182 7199 if draft or secret:
7183 7200 ui.status(_(b'phases: %s\n') % b', '.join(t))
7184 7201
7185 7202 if obsolete.isenabled(repo, obsolete.createmarkersopt):
7186 7203 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7187 7204 numtrouble = len(repo.revs(trouble + b"()"))
7188 7205 # We write all the possibilities to ease translation
7189 7206 troublemsg = {
7190 7207 b"orphan": _(b"orphan: %d changesets"),
7191 7208 b"contentdivergent": _(b"content-divergent: %d changesets"),
7192 7209 b"phasedivergent": _(b"phase-divergent: %d changesets"),
7193 7210 }
7194 7211 if numtrouble > 0:
7195 7212 ui.status(troublemsg[trouble] % numtrouble + b"\n")
7196 7213
7197 7214 cmdutil.summaryhooks(ui, repo)
7198 7215
7199 7216 if opts.get(b'remote'):
7200 7217 needsincoming, needsoutgoing = True, True
7201 7218 else:
7202 7219 needsincoming, needsoutgoing = False, False
7203 7220 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7204 7221 if i:
7205 7222 needsincoming = True
7206 7223 if o:
7207 7224 needsoutgoing = True
7208 7225 if not needsincoming and not needsoutgoing:
7209 7226 return
7210 7227
7211 7228 def getincoming():
7212 7229 # XXX We should actually skip this if no default is specified, instead
7213 7230 # of passing "default" which will resolve as "./default/" if no default
7214 7231 # path is defined.
7215 7232 source, branches = urlutil.get_unique_pull_path(
7216 7233 b'summary', repo, ui, b'default'
7217 7234 )
7218 7235 sbranch = branches[0]
7219 7236 try:
7220 7237 other = hg.peer(repo, {}, source)
7221 7238 except error.RepoError:
7222 7239 if opts.get(b'remote'):
7223 7240 raise
7224 7241 return source, sbranch, None, None, None
7225 7242 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7226 7243 if revs:
7227 7244 revs = [other.lookup(rev) for rev in revs]
7228 7245 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(source))
7229 7246 repo.ui.pushbuffer()
7230 7247 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7231 7248 repo.ui.popbuffer()
7232 7249 return source, sbranch, other, commoninc, commoninc[1]
7233 7250
7234 7251 if needsincoming:
7235 7252 source, sbranch, sother, commoninc, incoming = getincoming()
7236 7253 else:
7237 7254 source = sbranch = sother = commoninc = incoming = None
7238 7255
7239 7256 def getoutgoing():
7240 7257 # XXX We should actually skip this if no default is specified, instead
7241 7258 # of passing "default" which will resolve as "./default/" if no default
7242 7259 # path is defined.
7243 7260 d = None
7244 7261 if b'default-push' in ui.paths:
7245 7262 d = b'default-push'
7246 7263 elif b'default' in ui.paths:
7247 7264 d = b'default'
7248 7265 if d is not None:
7249 7266 path = urlutil.get_unique_push_path(b'summary', repo, ui, d)
7250 7267 dest = path.pushloc or path.loc
7251 7268 dbranch = path.branch
7252 7269 else:
7253 7270 dest = b'default'
7254 7271 dbranch = None
7255 7272 revs, checkout = hg.addbranchrevs(repo, repo, (dbranch, []), None)
7256 7273 if source != dest:
7257 7274 try:
7258 7275 dother = hg.peer(repo, {}, dest)
7259 7276 except error.RepoError:
7260 7277 if opts.get(b'remote'):
7261 7278 raise
7262 7279 return dest, dbranch, None, None
7263 7280 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(dest))
7264 7281 elif sother is None:
7265 7282 # there is no explicit destination peer, but source one is invalid
7266 7283 return dest, dbranch, None, None
7267 7284 else:
7268 7285 dother = sother
7269 7286 if source != dest or (sbranch is not None and sbranch != dbranch):
7270 7287 common = None
7271 7288 else:
7272 7289 common = commoninc
7273 7290 if revs:
7274 7291 revs = [repo.lookup(rev) for rev in revs]
7275 7292 repo.ui.pushbuffer()
7276 7293 outgoing = discovery.findcommonoutgoing(
7277 7294 repo, dother, onlyheads=revs, commoninc=common
7278 7295 )
7279 7296 repo.ui.popbuffer()
7280 7297 return dest, dbranch, dother, outgoing
7281 7298
7282 7299 if needsoutgoing:
7283 7300 dest, dbranch, dother, outgoing = getoutgoing()
7284 7301 else:
7285 7302 dest = dbranch = dother = outgoing = None
7286 7303
7287 7304 if opts.get(b'remote'):
7288 7305 # Help pytype. --remote sets both `needsincoming` and `needsoutgoing`.
7289 7306 # The former always sets `sother` (or raises an exception if it can't);
7290 7307 # the latter always sets `outgoing`.
7291 7308 assert sother is not None
7292 7309 assert outgoing is not None
7293 7310
7294 7311 t = []
7295 7312 if incoming:
7296 7313 t.append(_(b'1 or more incoming'))
7297 7314 o = outgoing.missing
7298 7315 if o:
7299 7316 t.append(_(b'%d outgoing') % len(o))
7300 7317 other = dother or sother
7301 7318 if b'bookmarks' in other.listkeys(b'namespaces'):
7302 7319 counts = bookmarks.summary(repo, other)
7303 7320 if counts[0] > 0:
7304 7321 t.append(_(b'%d incoming bookmarks') % counts[0])
7305 7322 if counts[1] > 0:
7306 7323 t.append(_(b'%d outgoing bookmarks') % counts[1])
7307 7324
7308 7325 if t:
7309 7326 # i18n: column positioning for "hg summary"
7310 7327 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7311 7328 else:
7312 7329 # i18n: column positioning for "hg summary"
7313 7330 ui.status(_(b'remote: (synced)\n'))
7314 7331
7315 7332 cmdutil.summaryremotehooks(
7316 7333 ui,
7317 7334 repo,
7318 7335 opts,
7319 7336 (
7320 7337 (source, sbranch, sother, commoninc),
7321 7338 (dest, dbranch, dother, outgoing),
7322 7339 ),
7323 7340 )
7324 7341
7325 7342
7326 7343 @command(
7327 7344 b'tag',
7328 7345 [
7329 7346 (b'f', b'force', None, _(b'force tag')),
7330 7347 (b'l', b'local', None, _(b'make the tag local')),
7331 7348 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7332 7349 (b'', b'remove', None, _(b'remove a tag')),
7333 7350 # -l/--local is already there, commitopts cannot be used
7334 7351 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7335 7352 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7336 7353 ]
7337 7354 + commitopts2,
7338 7355 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7339 7356 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7340 7357 )
7341 7358 def tag(ui, repo, name1, *names, **opts):
7342 7359 """add one or more tags for the current or given revision
7343 7360
7344 7361 Name a particular revision using <name>.
7345 7362
7346 7363 Tags are used to name particular revisions of the repository and are
7347 7364 very useful to compare different revisions, to go back to significant
7348 7365 earlier versions or to mark branch points as releases, etc. Changing
7349 7366 an existing tag is normally disallowed; use -f/--force to override.
7350 7367
7351 7368 If no revision is given, the parent of the working directory is
7352 7369 used.
7353 7370
7354 7371 To facilitate version control, distribution, and merging of tags,
7355 7372 they are stored as a file named ".hgtags" which is managed similarly
7356 7373 to other project files and can be hand-edited if necessary. This
7357 7374 also means that tagging creates a new commit. The file
7358 7375 ".hg/localtags" is used for local tags (not shared among
7359 7376 repositories).
7360 7377
7361 7378 Tag commits are usually made at the head of a branch. If the parent
7362 7379 of the working directory is not a branch head, :hg:`tag` aborts; use
7363 7380 -f/--force to force the tag commit to be based on a non-head
7364 7381 changeset.
7365 7382
7366 7383 See :hg:`help dates` for a list of formats valid for -d/--date.
7367 7384
7368 7385 Since tag names have priority over branch names during revision
7369 7386 lookup, using an existing branch name as a tag name is discouraged.
7370 7387
7371 7388 Returns 0 on success.
7372 7389 """
7373 7390 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7374 7391 opts = pycompat.byteskwargs(opts)
7375 7392 with repo.wlock(), repo.lock():
7376 7393 rev_ = b"."
7377 7394 names = [t.strip() for t in (name1,) + names]
7378 7395 if len(names) != len(set(names)):
7379 7396 raise error.InputError(_(b'tag names must be unique'))
7380 7397 for n in names:
7381 7398 scmutil.checknewlabel(repo, n, b'tag')
7382 7399 if not n:
7383 7400 raise error.InputError(
7384 7401 _(b'tag names cannot consist entirely of whitespace')
7385 7402 )
7386 7403 if opts.get(b'rev'):
7387 7404 rev_ = opts[b'rev']
7388 7405 message = opts.get(b'message')
7389 7406 if opts.get(b'remove'):
7390 7407 if opts.get(b'local'):
7391 7408 expectedtype = b'local'
7392 7409 else:
7393 7410 expectedtype = b'global'
7394 7411
7395 7412 for n in names:
7396 7413 if repo.tagtype(n) == b'global':
7397 7414 alltags = tagsmod.findglobaltags(ui, repo)
7398 7415 if alltags[n][0] == repo.nullid:
7399 7416 raise error.InputError(
7400 7417 _(b"tag '%s' is already removed") % n
7401 7418 )
7402 7419 if not repo.tagtype(n):
7403 7420 raise error.InputError(_(b"tag '%s' does not exist") % n)
7404 7421 if repo.tagtype(n) != expectedtype:
7405 7422 if expectedtype == b'global':
7406 7423 raise error.InputError(
7407 7424 _(b"tag '%s' is not a global tag") % n
7408 7425 )
7409 7426 else:
7410 7427 raise error.InputError(
7411 7428 _(b"tag '%s' is not a local tag") % n
7412 7429 )
7413 7430 rev_ = b'null'
7414 7431 if not message:
7415 7432 # we don't translate commit messages
7416 7433 message = b'Removed tag %s' % b', '.join(names)
7417 7434 elif not opts.get(b'force'):
7418 7435 for n in names:
7419 7436 if n in repo.tags():
7420 7437 raise error.InputError(
7421 7438 _(b"tag '%s' already exists (use -f to force)") % n
7422 7439 )
7423 7440 if not opts.get(b'local'):
7424 7441 p1, p2 = repo.dirstate.parents()
7425 7442 if p2 != repo.nullid:
7426 7443 raise error.StateError(_(b'uncommitted merge'))
7427 7444 bheads = repo.branchheads()
7428 7445 if not opts.get(b'force') and bheads and p1 not in bheads:
7429 7446 raise error.InputError(
7430 7447 _(
7431 7448 b'working directory is not at a branch head '
7432 7449 b'(use -f to force)'
7433 7450 )
7434 7451 )
7435 7452 node = scmutil.revsingle(repo, rev_).node()
7436 7453
7437 7454 if not message:
7438 7455 # we don't translate commit messages
7439 7456 message = b'Added tag %s for changeset %s' % (
7440 7457 b', '.join(names),
7441 7458 short(node),
7442 7459 )
7443 7460
7444 7461 date = opts.get(b'date')
7445 7462 if date:
7446 7463 date = dateutil.parsedate(date)
7447 7464
7448 7465 if opts.get(b'remove'):
7449 7466 editform = b'tag.remove'
7450 7467 else:
7451 7468 editform = b'tag.add'
7452 7469 editor = cmdutil.getcommiteditor(
7453 7470 editform=editform, **pycompat.strkwargs(opts)
7454 7471 )
7455 7472
7456 7473 # don't allow tagging the null rev
7457 7474 if (
7458 7475 not opts.get(b'remove')
7459 7476 and scmutil.revsingle(repo, rev_).rev() == nullrev
7460 7477 ):
7461 7478 raise error.InputError(_(b"cannot tag null revision"))
7462 7479
7463 7480 tagsmod.tag(
7464 7481 repo,
7465 7482 names,
7466 7483 node,
7467 7484 message,
7468 7485 opts.get(b'local'),
7469 7486 opts.get(b'user'),
7470 7487 date,
7471 7488 editor=editor,
7472 7489 )
7473 7490
7474 7491
7475 7492 @command(
7476 7493 b'tags',
7477 7494 formatteropts,
7478 7495 b'',
7479 7496 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7480 7497 intents={INTENT_READONLY},
7481 7498 )
7482 7499 def tags(ui, repo, **opts):
7483 7500 """list repository tags
7484 7501
7485 7502 This lists both regular and local tags. When the -v/--verbose
7486 7503 switch is used, a third column "local" is printed for local tags.
7487 7504 When the -q/--quiet switch is used, only the tag name is printed.
7488 7505
7489 7506 .. container:: verbose
7490 7507
7491 7508 Template:
7492 7509
7493 7510 The following keywords are supported in addition to the common template
7494 7511 keywords and functions such as ``{tag}``. See also
7495 7512 :hg:`help templates`.
7496 7513
7497 7514 :type: String. ``local`` for local tags.
7498 7515
7499 7516 Returns 0 on success.
7500 7517 """
7501 7518
7502 7519 opts = pycompat.byteskwargs(opts)
7503 7520 ui.pager(b'tags')
7504 7521 fm = ui.formatter(b'tags', opts)
7505 7522 hexfunc = fm.hexfunc
7506 7523
7507 7524 for t, n in reversed(repo.tagslist()):
7508 7525 hn = hexfunc(n)
7509 7526 label = b'tags.normal'
7510 7527 tagtype = repo.tagtype(t)
7511 7528 if not tagtype or tagtype == b'global':
7512 7529 tagtype = b''
7513 7530 else:
7514 7531 label = b'tags.' + tagtype
7515 7532
7516 7533 fm.startitem()
7517 7534 fm.context(repo=repo)
7518 7535 fm.write(b'tag', b'%s', t, label=label)
7519 7536 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7520 7537 fm.condwrite(
7521 7538 not ui.quiet,
7522 7539 b'rev node',
7523 7540 fmt,
7524 7541 repo.changelog.rev(n),
7525 7542 hn,
7526 7543 label=label,
7527 7544 )
7528 7545 fm.condwrite(
7529 7546 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7530 7547 )
7531 7548 fm.plain(b'\n')
7532 7549 fm.end()
7533 7550
7534 7551
7535 7552 @command(
7536 7553 b'tip',
7537 7554 [
7538 7555 (b'p', b'patch', None, _(b'show patch')),
7539 7556 (b'g', b'git', None, _(b'use git extended diff format')),
7540 7557 ]
7541 7558 + templateopts,
7542 7559 _(b'[-p] [-g]'),
7543 7560 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7544 7561 )
7545 7562 def tip(ui, repo, **opts):
7546 7563 """show the tip revision (DEPRECATED)
7547 7564
7548 7565 The tip revision (usually just called the tip) is the changeset
7549 7566 most recently added to the repository (and therefore the most
7550 7567 recently changed head).
7551 7568
7552 7569 If you have just made a commit, that commit will be the tip. If
7553 7570 you have just pulled changes from another repository, the tip of
7554 7571 that repository becomes the current tip. The "tip" tag is special
7555 7572 and cannot be renamed or assigned to a different changeset.
7556 7573
7557 7574 This command is deprecated, please use :hg:`heads` instead.
7558 7575
7559 7576 Returns 0 on success.
7560 7577 """
7561 7578 opts = pycompat.byteskwargs(opts)
7562 7579 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7563 7580 displayer.show(repo[b'tip'])
7564 7581 displayer.close()
7565 7582
7566 7583
7567 7584 @command(
7568 7585 b'unbundle',
7569 7586 [
7570 7587 (
7571 7588 b'u',
7572 7589 b'update',
7573 7590 None,
7574 7591 _(b'update to new branch head if changesets were unbundled'),
7575 7592 )
7576 7593 ],
7577 7594 _(b'[-u] FILE...'),
7578 7595 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7579 7596 )
7580 7597 def unbundle(ui, repo, fname1, *fnames, **opts):
7581 7598 """apply one or more bundle files
7582 7599
7583 7600 Apply one or more bundle files generated by :hg:`bundle`.
7584 7601
7585 7602 Returns 0 on success, 1 if an update has unresolved files.
7586 7603 """
7587 7604 fnames = (fname1,) + fnames
7588 7605
7589 7606 with repo.lock():
7590 7607 for fname in fnames:
7591 7608 f = hg.openpath(ui, fname)
7592 7609 gen = exchange.readbundle(ui, f, fname)
7593 7610 if isinstance(gen, streamclone.streamcloneapplier):
7594 7611 raise error.InputError(
7595 7612 _(
7596 7613 b'packed bundles cannot be applied with '
7597 7614 b'"hg unbundle"'
7598 7615 ),
7599 7616 hint=_(b'use "hg debugapplystreamclonebundle"'),
7600 7617 )
7601 7618 url = b'bundle:' + fname
7602 7619 try:
7603 7620 txnname = b'unbundle'
7604 7621 if not isinstance(gen, bundle2.unbundle20):
7605 7622 txnname = b'unbundle\n%s' % urlutil.hidepassword(url)
7606 7623 with repo.transaction(txnname) as tr:
7607 7624 op = bundle2.applybundle(
7608 7625 repo, gen, tr, source=b'unbundle', url=url
7609 7626 )
7610 7627 except error.BundleUnknownFeatureError as exc:
7611 7628 raise error.Abort(
7612 7629 _(b'%s: unknown bundle feature, %s') % (fname, exc),
7613 7630 hint=_(
7614 7631 b"see https://mercurial-scm.org/"
7615 7632 b"wiki/BundleFeature for more "
7616 7633 b"information"
7617 7634 ),
7618 7635 )
7619 7636 modheads = bundle2.combinechangegroupresults(op)
7620 7637
7621 7638 if postincoming(ui, repo, modheads, opts.get('update'), None, None):
7622 7639 return 1
7623 7640 else:
7624 7641 return 0
7625 7642
7626 7643
7627 7644 @command(
7628 7645 b'unshelve',
7629 7646 [
7630 7647 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7631 7648 (
7632 7649 b'c',
7633 7650 b'continue',
7634 7651 None,
7635 7652 _(b'continue an incomplete unshelve operation'),
7636 7653 ),
7637 7654 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7638 7655 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7639 7656 (
7640 7657 b'n',
7641 7658 b'name',
7642 7659 b'',
7643 7660 _(b'restore shelved change with given name'),
7644 7661 _(b'NAME'),
7645 7662 ),
7646 7663 (b't', b'tool', b'', _(b'specify merge tool')),
7647 7664 (
7648 7665 b'',
7649 7666 b'date',
7650 7667 b'',
7651 7668 _(b'set date for temporary commits (DEPRECATED)'),
7652 7669 _(b'DATE'),
7653 7670 ),
7654 7671 ],
7655 7672 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7656 7673 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7657 7674 )
7658 7675 def unshelve(ui, repo, *shelved, **opts):
7659 7676 """restore a shelved change to the working directory
7660 7677
7661 7678 This command accepts an optional name of a shelved change to
7662 7679 restore. If none is given, the most recent shelved change is used.
7663 7680
7664 7681 If a shelved change is applied successfully, the bundle that
7665 7682 contains the shelved changes is moved to a backup location
7666 7683 (.hg/shelve-backup).
7667 7684
7668 7685 Since you can restore a shelved change on top of an arbitrary
7669 7686 commit, it is possible that unshelving will result in a conflict
7670 7687 between your changes and the commits you are unshelving onto. If
7671 7688 this occurs, you must resolve the conflict, then use
7672 7689 ``--continue`` to complete the unshelve operation. (The bundle
7673 7690 will not be moved until you successfully complete the unshelve.)
7674 7691
7675 7692 (Alternatively, you can use ``--abort`` to abandon an unshelve
7676 7693 that causes a conflict. This reverts the unshelved changes, and
7677 7694 leaves the bundle in place.)
7678 7695
7679 7696 If bare shelved change (without interactive, include and exclude
7680 7697 option) was done on newly created branch it would restore branch
7681 7698 information to the working directory.
7682 7699
7683 7700 After a successful unshelve, the shelved changes are stored in a
7684 7701 backup directory. Only the N most recent backups are kept. N
7685 7702 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7686 7703 configuration option.
7687 7704
7688 7705 .. container:: verbose
7689 7706
7690 7707 Timestamp in seconds is used to decide order of backups. More
7691 7708 than ``maxbackups`` backups are kept, if same timestamp
7692 7709 prevents from deciding exact order of them, for safety.
7693 7710
7694 7711 Selected changes can be unshelved with ``--interactive`` flag.
7695 7712 The working directory is updated with the selected changes, and
7696 7713 only the unselected changes remain shelved.
7697 7714 Note: The whole shelve is applied to working directory first before
7698 7715 running interactively. So, this will bring up all the conflicts between
7699 7716 working directory and the shelve, irrespective of which changes will be
7700 7717 unshelved.
7701 7718 """
7702 7719 with repo.wlock():
7703 7720 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7704 7721
7705 7722
7706 7723 statemod.addunfinished(
7707 7724 b'unshelve',
7708 7725 fname=b'shelvedstate',
7709 7726 continueflag=True,
7710 7727 abortfunc=shelvemod.hgabortunshelve,
7711 7728 continuefunc=shelvemod.hgcontinueunshelve,
7712 7729 cmdmsg=_(b'unshelve already in progress'),
7713 7730 )
7714 7731
7715 7732
7716 7733 @command(
7717 7734 b'update|up|checkout|co',
7718 7735 [
7719 7736 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7720 7737 (b'c', b'check', None, _(b'require clean working directory')),
7721 7738 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7722 7739 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7723 7740 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7724 7741 ]
7725 7742 + mergetoolopts,
7726 7743 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7727 7744 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7728 7745 helpbasic=True,
7729 7746 )
7730 7747 def update(ui, repo, node=None, **opts):
7731 7748 """update working directory (or switch revisions)
7732 7749
7733 7750 Update the repository's working directory to the specified
7734 7751 changeset. If no changeset is specified, update to the tip of the
7735 7752 current named branch and move the active bookmark (see :hg:`help
7736 7753 bookmarks`).
7737 7754
7738 7755 Update sets the working directory's parent revision to the specified
7739 7756 changeset (see :hg:`help parents`).
7740 7757
7741 7758 If the changeset is not a descendant or ancestor of the working
7742 7759 directory's parent and there are uncommitted changes, the update is
7743 7760 aborted. With the -c/--check option, the working directory is checked
7744 7761 for uncommitted changes; if none are found, the working directory is
7745 7762 updated to the specified changeset.
7746 7763
7747 7764 .. container:: verbose
7748 7765
7749 7766 The -C/--clean, -c/--check, and -m/--merge options control what
7750 7767 happens if the working directory contains uncommitted changes.
7751 7768 At most of one of them can be specified.
7752 7769
7753 7770 1. If no option is specified, and if
7754 7771 the requested changeset is an ancestor or descendant of
7755 7772 the working directory's parent, the uncommitted changes
7756 7773 are merged into the requested changeset and the merged
7757 7774 result is left uncommitted. If the requested changeset is
7758 7775 not an ancestor or descendant (that is, it is on another
7759 7776 branch), the update is aborted and the uncommitted changes
7760 7777 are preserved.
7761 7778
7762 7779 2. With the -m/--merge option, the update is allowed even if the
7763 7780 requested changeset is not an ancestor or descendant of
7764 7781 the working directory's parent.
7765 7782
7766 7783 3. With the -c/--check option, the update is aborted and the
7767 7784 uncommitted changes are preserved.
7768 7785
7769 7786 4. With the -C/--clean option, uncommitted changes are discarded and
7770 7787 the working directory is updated to the requested changeset.
7771 7788
7772 7789 To cancel an uncommitted merge (and lose your changes), use
7773 7790 :hg:`merge --abort`.
7774 7791
7775 7792 Use null as the changeset to remove the working directory (like
7776 7793 :hg:`clone -U`).
7777 7794
7778 7795 If you want to revert just one file to an older revision, use
7779 7796 :hg:`revert [-r REV] NAME`.
7780 7797
7781 7798 See :hg:`help dates` for a list of formats valid for -d/--date.
7782 7799
7783 7800 Returns 0 on success, 1 if there are unresolved files.
7784 7801 """
7785 7802 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7786 7803 rev = opts.get('rev')
7787 7804 date = opts.get('date')
7788 7805 clean = opts.get('clean')
7789 7806 check = opts.get('check')
7790 7807 merge = opts.get('merge')
7791 7808 if rev and node:
7792 7809 raise error.InputError(_(b"please specify just one revision"))
7793 7810
7794 7811 if ui.configbool(b'commands', b'update.requiredest'):
7795 7812 if not node and not rev and not date:
7796 7813 raise error.InputError(
7797 7814 _(b'you must specify a destination'),
7798 7815 hint=_(b'for example: hg update ".::"'),
7799 7816 )
7800 7817
7801 7818 if rev is None or rev == b'':
7802 7819 rev = node
7803 7820
7804 7821 if date and rev is not None:
7805 7822 raise error.InputError(_(b"you can't specify a revision and a date"))
7806 7823
7807 7824 updatecheck = None
7808 7825 if check:
7809 7826 updatecheck = b'abort'
7810 7827 elif merge:
7811 7828 updatecheck = b'none'
7812 7829
7813 7830 with repo.wlock():
7814 7831 cmdutil.clearunfinished(repo)
7815 7832 if date:
7816 7833 rev = cmdutil.finddate(ui, repo, date)
7817 7834
7818 7835 # if we defined a bookmark, we have to remember the original name
7819 7836 brev = rev
7820 7837 if rev:
7821 7838 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7822 7839 ctx = scmutil.revsingle(repo, rev, default=None)
7823 7840 rev = ctx.rev()
7824 7841 hidden = ctx.hidden()
7825 7842 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7826 7843 with ui.configoverride(overrides, b'update'):
7827 7844 ret = hg.updatetotally(
7828 7845 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7829 7846 )
7830 7847 if hidden:
7831 7848 ctxstr = ctx.hex()[:12]
7832 7849 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7833 7850
7834 7851 if ctx.obsolete():
7835 7852 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7836 7853 ui.warn(b"(%s)\n" % obsfatemsg)
7837 7854 return ret
7838 7855
7839 7856
7840 7857 @command(
7841 7858 b'verify',
7842 7859 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7843 7860 helpcategory=command.CATEGORY_MAINTENANCE,
7844 7861 )
7845 7862 def verify(ui, repo, **opts):
7846 7863 """verify the integrity of the repository
7847 7864
7848 7865 Verify the integrity of the current repository.
7849 7866
7850 7867 This will perform an extensive check of the repository's
7851 7868 integrity, validating the hashes and checksums of each entry in
7852 7869 the changelog, manifest, and tracked files, as well as the
7853 7870 integrity of their crosslinks and indices.
7854 7871
7855 7872 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7856 7873 for more information about recovery from corruption of the
7857 7874 repository.
7858 7875
7859 7876 Returns 0 on success, 1 if errors are encountered.
7860 7877 """
7861 7878 opts = pycompat.byteskwargs(opts)
7862 7879
7863 7880 level = None
7864 7881 if opts[b'full']:
7865 7882 level = verifymod.VERIFY_FULL
7866 7883 return hg.verify(repo, level)
7867 7884
7868 7885
7869 7886 @command(
7870 7887 b'version',
7871 7888 [] + formatteropts,
7872 7889 helpcategory=command.CATEGORY_HELP,
7873 7890 norepo=True,
7874 7891 intents={INTENT_READONLY},
7875 7892 )
7876 7893 def version_(ui, **opts):
7877 7894 """output version and copyright information
7878 7895
7879 7896 .. container:: verbose
7880 7897
7881 7898 Template:
7882 7899
7883 7900 The following keywords are supported. See also :hg:`help templates`.
7884 7901
7885 7902 :extensions: List of extensions.
7886 7903 :ver: String. Version number.
7887 7904
7888 7905 And each entry of ``{extensions}`` provides the following sub-keywords
7889 7906 in addition to ``{ver}``.
7890 7907
7891 7908 :bundled: Boolean. True if included in the release.
7892 7909 :name: String. Extension name.
7893 7910 """
7894 7911 opts = pycompat.byteskwargs(opts)
7895 7912 if ui.verbose:
7896 7913 ui.pager(b'version')
7897 7914 fm = ui.formatter(b"version", opts)
7898 7915 fm.startitem()
7899 7916 fm.write(
7900 7917 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7901 7918 )
7902 7919 license = _(
7903 7920 b"(see https://mercurial-scm.org for more information)\n"
7904 7921 b"\nCopyright (C) 2005-2021 Olivia Mackall and others\n"
7905 7922 b"This is free software; see the source for copying conditions. "
7906 7923 b"There is NO\nwarranty; "
7907 7924 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7908 7925 )
7909 7926 if not ui.quiet:
7910 7927 fm.plain(license)
7911 7928
7912 7929 if ui.verbose:
7913 7930 fm.plain(_(b"\nEnabled extensions:\n\n"))
7914 7931 # format names and versions into columns
7915 7932 names = []
7916 7933 vers = []
7917 7934 isinternals = []
7918 7935 for name, module in sorted(extensions.extensions()):
7919 7936 names.append(name)
7920 7937 vers.append(extensions.moduleversion(module) or None)
7921 7938 isinternals.append(extensions.ismoduleinternal(module))
7922 7939 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7923 7940 if names:
7924 7941 namefmt = b" %%-%ds " % max(len(n) for n in names)
7925 7942 places = [_(b"external"), _(b"internal")]
7926 7943 for n, v, p in zip(names, vers, isinternals):
7927 7944 fn.startitem()
7928 7945 fn.condwrite(ui.verbose, b"name", namefmt, n)
7929 7946 if ui.verbose:
7930 7947 fn.plain(b"%s " % places[p])
7931 7948 fn.data(bundled=p)
7932 7949 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7933 7950 if ui.verbose:
7934 7951 fn.plain(b"\n")
7935 7952 fn.end()
7936 7953 fm.end()
7937 7954
7938 7955
7939 7956 def loadcmdtable(ui, name, cmdtable):
7940 7957 """Load command functions from specified cmdtable"""
7941 7958 overrides = [cmd for cmd in cmdtable if cmd in table]
7942 7959 if overrides:
7943 7960 ui.warn(
7944 7961 _(b"extension '%s' overrides commands: %s\n")
7945 7962 % (name, b" ".join(overrides))
7946 7963 )
7947 7964 table.update(cmdtable)
@@ -1,2236 +1,2277
1 1 # ui.py - user interface bits for mercurial
2 2 #
3 3 # Copyright 2005-2007 Olivia Mackall <olivia@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 collections
11 11 import contextlib
12 12 import datetime
13 13 import errno
14 14 import inspect
15 15 import os
16 16 import re
17 17 import signal
18 18 import socket
19 19 import subprocess
20 20 import sys
21 21 import traceback
22 22
23 23 from .i18n import _
24 24 from .node import hex
25 25 from .pycompat import (
26 26 getattr,
27 27 open,
28 28 )
29 29
30 30 from . import (
31 31 color,
32 32 config,
33 33 configitems,
34 34 encoding,
35 35 error,
36 36 formatter,
37 37 loggingutil,
38 38 progress,
39 39 pycompat,
40 40 rcutil,
41 41 scmutil,
42 42 util,
43 43 )
44 44 from .utils import (
45 45 dateutil,
46 46 procutil,
47 47 resourceutil,
48 48 stringutil,
49 49 urlutil,
50 50 )
51 51
52 52 urlreq = util.urlreq
53 53
54 54 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
55 55 _keepalnum = b''.join(
56 56 c for c in map(pycompat.bytechr, range(256)) if not c.isalnum()
57 57 )
58 58
59 59 # The config knobs that will be altered (if unset) by ui.tweakdefaults.
60 60 tweakrc = b"""
61 61 [ui]
62 62 # The rollback command is dangerous. As a rule, don't use it.
63 63 rollback = False
64 64 # Make `hg status` report copy information
65 65 statuscopies = yes
66 66 # Prefer curses UIs when available. Revert to plain-text with `text`.
67 67 interface = curses
68 68 # Make compatible commands emit cwd-relative paths by default.
69 69 relative-paths = yes
70 70
71 71 [commands]
72 72 # Grep working directory by default.
73 73 grep.all-files = True
74 74 # Refuse to perform an `hg update` that would cause a file content merge
75 75 update.check = noconflict
76 76 # Show conflicts information in `hg status`
77 77 status.verbose = True
78 78 # Make `hg resolve` with no action (like `-m`) fail instead of re-merging.
79 79 resolve.explicit-re-merge = True
80 80
81 81 [diff]
82 82 git = 1
83 83 showfunc = 1
84 84 word-diff = 1
85 85 """
86 86
87 87 samplehgrcs = {
88 88 b'user': b"""# example user config (see 'hg help config' for more info)
89 89 [ui]
90 90 # name and email, e.g.
91 91 # username = Jane Doe <jdoe@example.com>
92 92 username =
93 93
94 94 # We recommend enabling tweakdefaults to get slight improvements to
95 95 # the UI over time. Make sure to set HGPLAIN in the environment when
96 96 # writing scripts!
97 97 # tweakdefaults = True
98 98
99 99 # uncomment to disable color in command output
100 100 # (see 'hg help color' for details)
101 101 # color = never
102 102
103 103 # uncomment to disable command output pagination
104 104 # (see 'hg help pager' for details)
105 105 # paginate = never
106 106
107 107 [extensions]
108 108 # uncomment the lines below to enable some popular extensions
109 109 # (see 'hg help extensions' for more info)
110 110 #
111 111 # histedit =
112 112 # rebase =
113 113 # uncommit =
114 114 """,
115 115 b'cloned': b"""# example repository config (see 'hg help config' for more info)
116 116 [paths]
117 117 default = %s
118 118
119 119 # path aliases to other clones of this repo in URLs or filesystem paths
120 120 # (see 'hg help config.paths' for more info)
121 121 #
122 122 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
123 123 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
124 124 # my-clone = /home/jdoe/jdoes-clone
125 125
126 126 [ui]
127 127 # name and email (local to this repository, optional), e.g.
128 128 # username = Jane Doe <jdoe@example.com>
129 129 """,
130 130 b'local': b"""# example repository config (see 'hg help config' for more info)
131 131 [paths]
132 132 # path aliases to other clones of this repo in URLs or filesystem paths
133 133 # (see 'hg help config.paths' for more info)
134 134 #
135 135 # default = http://example.com/hg/example-repo
136 136 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
137 137 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
138 138 # my-clone = /home/jdoe/jdoes-clone
139 139
140 140 [ui]
141 141 # name and email (local to this repository, optional), e.g.
142 142 # username = Jane Doe <jdoe@example.com>
143 143 """,
144 144 b'global': b"""# example system-wide hg config (see 'hg help config' for more info)
145 145
146 146 [ui]
147 147 # uncomment to disable color in command output
148 148 # (see 'hg help color' for details)
149 149 # color = never
150 150
151 151 # uncomment to disable command output pagination
152 152 # (see 'hg help pager' for details)
153 153 # paginate = never
154 154
155 155 [extensions]
156 156 # uncomment the lines below to enable some popular extensions
157 157 # (see 'hg help extensions' for more info)
158 158 #
159 159 # blackbox =
160 160 # churn =
161 161 """,
162 162 }
163 163
164 164
165 165 def _maybestrurl(maybebytes):
166 166 return pycompat.rapply(pycompat.strurl, maybebytes)
167 167
168 168
169 169 def _maybebytesurl(maybestr):
170 170 return pycompat.rapply(pycompat.bytesurl, maybestr)
171 171
172 172
173 173 class httppasswordmgrdbproxy(object):
174 174 """Delays loading urllib2 until it's needed."""
175 175
176 176 def __init__(self):
177 177 self._mgr = None
178 178
179 179 def _get_mgr(self):
180 180 if self._mgr is None:
181 181 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
182 182 return self._mgr
183 183
184 184 def add_password(self, realm, uris, user, passwd):
185 185 return self._get_mgr().add_password(
186 186 _maybestrurl(realm),
187 187 _maybestrurl(uris),
188 188 _maybestrurl(user),
189 189 _maybestrurl(passwd),
190 190 )
191 191
192 192 def find_user_password(self, realm, uri):
193 193 mgr = self._get_mgr()
194 194 return _maybebytesurl(
195 195 mgr.find_user_password(_maybestrurl(realm), _maybestrurl(uri))
196 196 )
197 197
198 198
199 199 def _catchterm(*args):
200 200 raise error.SignalInterrupt
201 201
202 202
203 203 # unique object used to detect no default value has been provided when
204 204 # retrieving configuration value.
205 205 _unset = object()
206 206
207 207 # _reqexithandlers: callbacks run at the end of a request
208 208 _reqexithandlers = []
209 209
210 210
211 211 class ui(object):
212 212 def __init__(self, src=None):
213 213 """Create a fresh new ui object if no src given
214 214
215 215 Use uimod.ui.load() to create a ui which knows global and user configs.
216 216 In most cases, you should use ui.copy() to create a copy of an existing
217 217 ui object.
218 218 """
219 219 # _buffers: used for temporary capture of output
220 220 self._buffers = []
221 221 # 3-tuple describing how each buffer in the stack behaves.
222 222 # Values are (capture stderr, capture subprocesses, apply labels).
223 223 self._bufferstates = []
224 224 # When a buffer is active, defines whether we are expanding labels.
225 225 # This exists to prevent an extra list lookup.
226 226 self._bufferapplylabels = None
227 227 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
228 228 self._reportuntrusted = True
229 229 self._knownconfig = configitems.coreitems
230 230 self._ocfg = config.config() # overlay
231 231 self._tcfg = config.config() # trusted
232 232 self._ucfg = config.config() # untrusted
233 233 self._trustusers = set()
234 234 self._trustgroups = set()
235 235 self.callhooks = True
236 236 # hold the root to use for each [paths] entry
237 237 self._path_to_root = {}
238 238 # Insecure server connections requested.
239 239 self.insecureconnections = False
240 240 # Blocked time
241 241 self.logblockedtimes = False
242 242 # color mode: see mercurial/color.py for possible value
243 243 self._colormode = None
244 244 self._terminfoparams = {}
245 245 self._styles = {}
246 246 self._uninterruptible = False
247 247 self.showtimestamp = False
248 248
249 249 if src:
250 250 self._fout = src._fout
251 251 self._ferr = src._ferr
252 252 self._fin = src._fin
253 253 self._fmsg = src._fmsg
254 254 self._fmsgout = src._fmsgout
255 255 self._fmsgerr = src._fmsgerr
256 256 self._finoutredirected = src._finoutredirected
257 257 self._loggers = src._loggers.copy()
258 258 self.pageractive = src.pageractive
259 259 self._disablepager = src._disablepager
260 260 self._tweaked = src._tweaked
261 261
262 262 self._tcfg = src._tcfg.copy()
263 263 self._ucfg = src._ucfg.copy()
264 264 self._ocfg = src._ocfg.copy()
265 265 self._trustusers = src._trustusers.copy()
266 266 self._trustgroups = src._trustgroups.copy()
267 267 self.environ = src.environ
268 268 self.callhooks = src.callhooks
269 269 self._path_to_root = src._path_to_root
270 270 self.insecureconnections = src.insecureconnections
271 271 self._colormode = src._colormode
272 272 self._terminfoparams = src._terminfoparams.copy()
273 273 self._styles = src._styles.copy()
274 274
275 275 self.fixconfig()
276 276
277 277 self.httppasswordmgrdb = src.httppasswordmgrdb
278 278 self._blockedtimes = src._blockedtimes
279 279 else:
280 280 self._fout = procutil.stdout
281 281 self._ferr = procutil.stderr
282 282 self._fin = procutil.stdin
283 283 self._fmsg = None
284 284 self._fmsgout = self.fout # configurable
285 285 self._fmsgerr = self.ferr # configurable
286 286 self._finoutredirected = False
287 287 self._loggers = {}
288 288 self.pageractive = False
289 289 self._disablepager = False
290 290 self._tweaked = False
291 291
292 292 # shared read-only environment
293 293 self.environ = encoding.environ
294 294
295 295 self.httppasswordmgrdb = httppasswordmgrdbproxy()
296 296 self._blockedtimes = collections.defaultdict(int)
297 297
298 298 allowed = self.configlist(b'experimental', b'exportableenviron')
299 299 if b'*' in allowed:
300 300 self._exportableenviron = self.environ
301 301 else:
302 302 self._exportableenviron = {}
303 303 for k in allowed:
304 304 if k in self.environ:
305 305 self._exportableenviron[k] = self.environ[k]
306 306
307 307 def _new_source(self):
308 308 self._ocfg.new_source()
309 309 self._tcfg.new_source()
310 310 self._ucfg.new_source()
311 311
312 312 @classmethod
313 313 def load(cls):
314 314 """Create a ui and load global and user configs"""
315 315 u = cls()
316 316 # we always trust global config files and environment variables
317 317 for t, f in rcutil.rccomponents():
318 318 if t == b'path':
319 319 u.readconfig(f, trust=True)
320 320 elif t == b'resource':
321 321 u.read_resource_config(f, trust=True)
322 322 elif t == b'items':
323 323 u._new_source()
324 324 sections = set()
325 325 for section, name, value, source in f:
326 326 # do not set u._ocfg
327 327 # XXX clean this up once immutable config object is a thing
328 328 u._tcfg.set(section, name, value, source)
329 329 u._ucfg.set(section, name, value, source)
330 330 sections.add(section)
331 331 for section in sections:
332 332 u.fixconfig(section=section)
333 333 else:
334 334 raise error.ProgrammingError(b'unknown rctype: %s' % t)
335 335 u._maybetweakdefaults()
336 336 u._new_source() # anything after that is a different level
337 337 return u
338 338
339 339 def _maybetweakdefaults(self):
340 340 if not self.configbool(b'ui', b'tweakdefaults'):
341 341 return
342 342 if self._tweaked or self.plain(b'tweakdefaults'):
343 343 return
344 344
345 345 # Note: it is SUPER IMPORTANT that you set self._tweaked to
346 346 # True *before* any calls to setconfig(), otherwise you'll get
347 347 # infinite recursion between setconfig and this method.
348 348 #
349 349 # TODO: We should extract an inner method in setconfig() to
350 350 # avoid this weirdness.
351 351 self._tweaked = True
352 352 tmpcfg = config.config()
353 353 tmpcfg.parse(b'<tweakdefaults>', tweakrc)
354 354 for section in tmpcfg:
355 355 for name, value in tmpcfg.items(section):
356 356 if not self.hasconfig(section, name):
357 357 self.setconfig(section, name, value, b"<tweakdefaults>")
358 358
359 359 def copy(self):
360 360 return self.__class__(self)
361 361
362 362 def resetstate(self):
363 363 """Clear internal state that shouldn't persist across commands"""
364 364 if self._progbar:
365 365 self._progbar.resetstate() # reset last-print time of progress bar
366 366 self.httppasswordmgrdb = httppasswordmgrdbproxy()
367 367
368 368 @contextlib.contextmanager
369 369 def timeblockedsection(self, key):
370 370 # this is open-coded below - search for timeblockedsection to find them
371 371 starttime = util.timer()
372 372 try:
373 373 yield
374 374 finally:
375 375 self._blockedtimes[key + b'_blocked'] += (
376 376 util.timer() - starttime
377 377 ) * 1000
378 378
379 379 @contextlib.contextmanager
380 380 def uninterruptible(self):
381 381 """Mark an operation as unsafe.
382 382
383 383 Most operations on a repository are safe to interrupt, but a
384 384 few are risky (for example repair.strip). This context manager
385 385 lets you advise Mercurial that something risky is happening so
386 386 that control-C etc can be blocked if desired.
387 387 """
388 388 enabled = self.configbool(b'experimental', b'nointerrupt')
389 389 if enabled and self.configbool(
390 390 b'experimental', b'nointerrupt-interactiveonly'
391 391 ):
392 392 enabled = self.interactive()
393 393 if self._uninterruptible or not enabled:
394 394 # if nointerrupt support is turned off, the process isn't
395 395 # interactive, or we're already in an uninterruptible
396 396 # block, do nothing.
397 397 yield
398 398 return
399 399
400 400 def warn():
401 401 self.warn(_(b"shutting down cleanly\n"))
402 402 self.warn(
403 403 _(b"press ^C again to terminate immediately (dangerous)\n")
404 404 )
405 405 return True
406 406
407 407 with procutil.uninterruptible(warn):
408 408 try:
409 409 self._uninterruptible = True
410 410 yield
411 411 finally:
412 412 self._uninterruptible = False
413 413
414 414 def formatter(self, topic, opts):
415 415 return formatter.formatter(self, self, topic, opts)
416 416
417 417 def _trusted(self, fp, f):
418 418 st = util.fstat(fp)
419 419 if util.isowner(st):
420 420 return True
421 421
422 422 tusers, tgroups = self._trustusers, self._trustgroups
423 423 if b'*' in tusers or b'*' in tgroups:
424 424 return True
425 425
426 426 user = util.username(st.st_uid)
427 427 group = util.groupname(st.st_gid)
428 428 if user in tusers or group in tgroups or user == util.username():
429 429 return True
430 430
431 431 if self._reportuntrusted:
432 432 self.warn(
433 433 _(
434 434 b'not trusting file %s from untrusted '
435 435 b'user %s, group %s\n'
436 436 )
437 437 % (f, user, group)
438 438 )
439 439 return False
440 440
441 441 def read_resource_config(
442 442 self, name, root=None, trust=False, sections=None, remap=None
443 443 ):
444 444 try:
445 445 fp = resourceutil.open_resource(name[0], name[1])
446 446 except IOError:
447 447 if not sections: # ignore unless we were looking for something
448 448 return
449 449 raise
450 450
451 451 self._readconfig(
452 452 b'resource:%s.%s' % name, fp, root, trust, sections, remap
453 453 )
454 454
455 455 def readconfig(
456 456 self, filename, root=None, trust=False, sections=None, remap=None
457 457 ):
458 458 try:
459 459 fp = open(filename, 'rb')
460 460 except IOError:
461 461 if not sections: # ignore unless we were looking for something
462 462 return
463 463 raise
464 464
465 465 self._readconfig(filename, fp, root, trust, sections, remap)
466 466
467 467 def _readconfig(
468 468 self, filename, fp, root=None, trust=False, sections=None, remap=None
469 469 ):
470 470 with fp:
471 471 cfg = config.config()
472 472 trusted = sections or trust or self._trusted(fp, filename)
473 473
474 474 try:
475 475 cfg.read(filename, fp, sections=sections, remap=remap)
476 476 except error.ConfigError as inst:
477 477 if trusted:
478 478 raise
479 479 self.warn(
480 480 _(b'ignored %s: %s\n') % (inst.location, inst.message)
481 481 )
482 482
483 483 self._applyconfig(cfg, trusted, root)
484 484
485 485 def applyconfig(self, configitems, source=b"", root=None):
486 486 """Add configitems from a non-file source. Unlike with ``setconfig()``,
487 487 they can be overridden by subsequent config file reads. The items are
488 488 in the same format as ``configoverride()``, namely a dict of the
489 489 following structures: {(section, name) : value}
490 490
491 491 Typically this is used by extensions that inject themselves into the
492 492 config file load procedure by monkeypatching ``localrepo.loadhgrc()``.
493 493 """
494 494 cfg = config.config()
495 495
496 496 for (section, name), value in configitems.items():
497 497 cfg.set(section, name, value, source)
498 498
499 499 self._applyconfig(cfg, True, root)
500 500
501 501 def _applyconfig(self, cfg, trusted, root):
502 502 if self.plain():
503 503 for k in (
504 504 b'debug',
505 505 b'fallbackencoding',
506 506 b'quiet',
507 507 b'slash',
508 508 b'logtemplate',
509 509 b'message-output',
510 510 b'statuscopies',
511 511 b'style',
512 512 b'traceback',
513 513 b'verbose',
514 514 ):
515 515 if k in cfg[b'ui']:
516 516 del cfg[b'ui'][k]
517 517 for k, v in cfg.items(b'defaults'):
518 518 del cfg[b'defaults'][k]
519 519 for k, v in cfg.items(b'commands'):
520 520 del cfg[b'commands'][k]
521 521 for k, v in cfg.items(b'command-templates'):
522 522 del cfg[b'command-templates'][k]
523 523 # Don't remove aliases from the configuration if in the exceptionlist
524 524 if self.plain(b'alias'):
525 525 for k, v in cfg.items(b'alias'):
526 526 del cfg[b'alias'][k]
527 527 if self.plain(b'revsetalias'):
528 528 for k, v in cfg.items(b'revsetalias'):
529 529 del cfg[b'revsetalias'][k]
530 530 if self.plain(b'templatealias'):
531 531 for k, v in cfg.items(b'templatealias'):
532 532 del cfg[b'templatealias'][k]
533 533
534 534 if trusted:
535 535 self._tcfg.update(cfg)
536 536 self._tcfg.update(self._ocfg)
537 537 self._ucfg.update(cfg)
538 538 self._ucfg.update(self._ocfg)
539 539
540 540 if root is None:
541 541 root = os.path.expanduser(b'~')
542 542 self.fixconfig(root=root)
543 543
544 544 def fixconfig(self, root=None, section=None):
545 545 if section in (None, b'paths'):
546 546 # expand vars and ~
547 547 # translate paths relative to root (or home) into absolute paths
548 548 root = root or encoding.getcwd()
549 549 for c in self._tcfg, self._ucfg, self._ocfg:
550 550 for n, p in c.items(b'paths'):
551 551 old_p = p
552 552 s = self.configsource(b'paths', n) or b'none'
553 553 root_key = (n, p, s)
554 554 if root_key not in self._path_to_root:
555 555 self._path_to_root[root_key] = root
556 556 # Ignore sub-options.
557 557 if b':' in n:
558 558 continue
559 559 if not p:
560 560 continue
561 561 if b'%%' in p:
562 562 if s is None:
563 563 s = 'none'
564 564 self.warn(
565 565 _(b"(deprecated '%%' in path %s=%s from %s)\n")
566 566 % (n, p, s)
567 567 )
568 568 p = p.replace(b'%%', b'%')
569 569 if p != old_p:
570 570 c.alter(b"paths", n, p)
571 571
572 572 if section in (None, b'ui'):
573 573 # update ui options
574 574 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
575 575 self.debugflag = self.configbool(b'ui', b'debug')
576 576 self.verbose = self.debugflag or self.configbool(b'ui', b'verbose')
577 577 self.quiet = not self.debugflag and self.configbool(b'ui', b'quiet')
578 578 if self.verbose and self.quiet:
579 579 self.quiet = self.verbose = False
580 580 self._reportuntrusted = self.debugflag or self.configbool(
581 581 b"ui", b"report_untrusted"
582 582 )
583 583 self.showtimestamp = self.configbool(b'ui', b'timestamp-output')
584 584 self.tracebackflag = self.configbool(b'ui', b'traceback')
585 585 self.logblockedtimes = self.configbool(b'ui', b'logblockedtimes')
586 586
587 587 if section in (None, b'trusted'):
588 588 # update trust information
589 589 self._trustusers.update(self.configlist(b'trusted', b'users'))
590 590 self._trustgroups.update(self.configlist(b'trusted', b'groups'))
591 591
592 592 if section in (None, b'devel', b'ui') and self.debugflag:
593 593 tracked = set()
594 594 if self.configbool(b'devel', b'debug.extensions'):
595 595 tracked.add(b'extension')
596 596 if tracked:
597 597 logger = loggingutil.fileobjectlogger(self._ferr, tracked)
598 598 self.setlogger(b'debug', logger)
599 599
600 600 def backupconfig(self, section, item):
601 601 return (
602 602 self._ocfg.backup(section, item),
603 603 self._tcfg.backup(section, item),
604 604 self._ucfg.backup(section, item),
605 605 )
606 606
607 607 def restoreconfig(self, data):
608 608 self._ocfg.restore(data[0])
609 609 self._tcfg.restore(data[1])
610 610 self._ucfg.restore(data[2])
611 611
612 612 def setconfig(self, section, name, value, source=b''):
613 613 for cfg in (self._ocfg, self._tcfg, self._ucfg):
614 614 cfg.set(section, name, value, source)
615 615 self.fixconfig(section=section)
616 616 self._maybetweakdefaults()
617 617
618 618 def _data(self, untrusted):
619 619 return untrusted and self._ucfg or self._tcfg
620 620
621 621 def configsource(self, section, name, untrusted=False):
622 622 return self._data(untrusted).source(section, name)
623 623
624 624 def config(self, section, name, default=_unset, untrusted=False):
625 625 """return the plain string version of a config"""
626 626 value = self._config(
627 627 section, name, default=default, untrusted=untrusted
628 628 )
629 629 if value is _unset:
630 630 return None
631 631 return value
632 632
633 633 def _config(self, section, name, default=_unset, untrusted=False):
634 634 value = itemdefault = default
635 635 item = self._knownconfig.get(section, {}).get(name)
636 636 alternates = [(section, name)]
637 637
638 638 if item is not None:
639 639 alternates.extend(item.alias)
640 640 if callable(item.default):
641 641 itemdefault = item.default()
642 642 else:
643 643 itemdefault = item.default
644 644 else:
645 645 msg = b"accessing unregistered config item: '%s.%s'"
646 646 msg %= (section, name)
647 647 self.develwarn(msg, 2, b'warn-config-unknown')
648 648
649 649 if default is _unset:
650 650 if item is None:
651 651 value = default
652 652 elif item.default is configitems.dynamicdefault:
653 653 value = None
654 654 msg = b"config item requires an explicit default value: '%s.%s'"
655 655 msg %= (section, name)
656 656 self.develwarn(msg, 2, b'warn-config-default')
657 657 else:
658 658 value = itemdefault
659 659 elif (
660 660 item is not None
661 661 and item.default is not configitems.dynamicdefault
662 662 and default != itemdefault
663 663 ):
664 664 msg = (
665 665 b"specifying a mismatched default value for a registered "
666 666 b"config item: '%s.%s' '%s'"
667 667 )
668 668 msg %= (section, name, pycompat.bytestr(default))
669 669 self.develwarn(msg, 2, b'warn-config-default')
670 670
671 671 candidates = []
672 672 config = self._data(untrusted)
673 673 for s, n in alternates:
674 674 candidate = config.get(s, n, None)
675 675 if candidate is not None:
676 676 candidates.append((s, n, candidate))
677 677 if candidates:
678 678
679 679 def level(x):
680 680 return config.level(x[0], x[1])
681 681
682 682 value = max(candidates, key=level)[2]
683 683
684 684 if self.debugflag and not untrusted and self._reportuntrusted:
685 685 for s, n in alternates:
686 686 uvalue = self._ucfg.get(s, n)
687 687 if uvalue is not None and uvalue != value:
688 688 self.debug(
689 689 b"ignoring untrusted configuration option "
690 690 b"%s.%s = %s\n" % (s, n, uvalue)
691 691 )
692 692 return value
693 693
694 694 def config_default(self, section, name):
695 695 """return the default value for a config option
696 696
697 697 The default is returned "raw", for example if it is a callable, the
698 698 callable was not called.
699 699 """
700 700 item = self._knownconfig.get(section, {}).get(name)
701 701
702 702 if item is None:
703 703 raise KeyError((section, name))
704 704 return item.default
705 705
706 706 def configsuboptions(self, section, name, default=_unset, untrusted=False):
707 707 """Get a config option and all sub-options.
708 708
709 709 Some config options have sub-options that are declared with the
710 710 format "key:opt = value". This method is used to return the main
711 711 option and all its declared sub-options.
712 712
713 713 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
714 714 is a dict of defined sub-options where keys and values are strings.
715 715 """
716 716 main = self.config(section, name, default, untrusted=untrusted)
717 717 data = self._data(untrusted)
718 718 sub = {}
719 719 prefix = b'%s:' % name
720 720 for k, v in data.items(section):
721 721 if k.startswith(prefix):
722 722 sub[k[len(prefix) :]] = v
723 723
724 724 if self.debugflag and not untrusted and self._reportuntrusted:
725 725 for k, v in sub.items():
726 726 uvalue = self._ucfg.get(section, b'%s:%s' % (name, k))
727 727 if uvalue is not None and uvalue != v:
728 728 self.debug(
729 729 b'ignoring untrusted configuration option '
730 730 b'%s:%s.%s = %s\n' % (section, name, k, uvalue)
731 731 )
732 732
733 733 return main, sub
734 734
735 735 def configpath(self, section, name, default=_unset, untrusted=False):
736 736 """get a path config item, expanded relative to repo root or config
737 737 file"""
738 738 v = self.config(section, name, default, untrusted)
739 739 if v is None:
740 740 return None
741 741 if not os.path.isabs(v) or b"://" not in v:
742 742 src = self.configsource(section, name, untrusted)
743 743 if b':' in src:
744 744 base = os.path.dirname(src.rsplit(b':')[0])
745 745 v = os.path.join(base, os.path.expanduser(v))
746 746 return v
747 747
748 748 def configbool(self, section, name, default=_unset, untrusted=False):
749 749 """parse a configuration element as a boolean
750 750
751 751 >>> u = ui(); s = b'foo'
752 752 >>> u.setconfig(s, b'true', b'yes')
753 753 >>> u.configbool(s, b'true')
754 754 True
755 755 >>> u.setconfig(s, b'false', b'no')
756 756 >>> u.configbool(s, b'false')
757 757 False
758 758 >>> u.configbool(s, b'unknown')
759 759 False
760 760 >>> u.configbool(s, b'unknown', True)
761 761 True
762 762 >>> u.setconfig(s, b'invalid', b'somevalue')
763 763 >>> u.configbool(s, b'invalid')
764 764 Traceback (most recent call last):
765 765 ...
766 766 ConfigError: foo.invalid is not a boolean ('somevalue')
767 767 """
768 768
769 769 v = self._config(section, name, default, untrusted=untrusted)
770 770 if v is None:
771 771 return v
772 772 if v is _unset:
773 773 if default is _unset:
774 774 return False
775 775 return default
776 776 if isinstance(v, bool):
777 777 return v
778 778 b = stringutil.parsebool(v)
779 779 if b is None:
780 780 raise error.ConfigError(
781 781 _(b"%s.%s is not a boolean ('%s')") % (section, name, v)
782 782 )
783 783 return b
784 784
785 785 def configwith(
786 786 self, convert, section, name, default=_unset, desc=None, untrusted=False
787 787 ):
788 788 """parse a configuration element with a conversion function
789 789
790 790 >>> u = ui(); s = b'foo'
791 791 >>> u.setconfig(s, b'float1', b'42')
792 792 >>> u.configwith(float, s, b'float1')
793 793 42.0
794 794 >>> u.setconfig(s, b'float2', b'-4.25')
795 795 >>> u.configwith(float, s, b'float2')
796 796 -4.25
797 797 >>> u.configwith(float, s, b'unknown', 7)
798 798 7.0
799 799 >>> u.setconfig(s, b'invalid', b'somevalue')
800 800 >>> u.configwith(float, s, b'invalid')
801 801 Traceback (most recent call last):
802 802 ...
803 803 ConfigError: foo.invalid is not a valid float ('somevalue')
804 804 >>> u.configwith(float, s, b'invalid', desc=b'womble')
805 805 Traceback (most recent call last):
806 806 ...
807 807 ConfigError: foo.invalid is not a valid womble ('somevalue')
808 808 """
809 809
810 810 v = self.config(section, name, default, untrusted)
811 811 if v is None:
812 812 return v # do not attempt to convert None
813 813 try:
814 814 return convert(v)
815 815 except (ValueError, error.ParseError):
816 816 if desc is None:
817 817 desc = pycompat.sysbytes(convert.__name__)
818 818 raise error.ConfigError(
819 819 _(b"%s.%s is not a valid %s ('%s')") % (section, name, desc, v)
820 820 )
821 821
822 822 def configint(self, section, name, default=_unset, untrusted=False):
823 823 """parse a configuration element as an integer
824 824
825 825 >>> u = ui(); s = b'foo'
826 826 >>> u.setconfig(s, b'int1', b'42')
827 827 >>> u.configint(s, b'int1')
828 828 42
829 829 >>> u.setconfig(s, b'int2', b'-42')
830 830 >>> u.configint(s, b'int2')
831 831 -42
832 832 >>> u.configint(s, b'unknown', 7)
833 833 7
834 834 >>> u.setconfig(s, b'invalid', b'somevalue')
835 835 >>> u.configint(s, b'invalid')
836 836 Traceback (most recent call last):
837 837 ...
838 838 ConfigError: foo.invalid is not a valid integer ('somevalue')
839 839 """
840 840
841 841 return self.configwith(
842 842 int, section, name, default, b'integer', untrusted
843 843 )
844 844
845 845 def configbytes(self, section, name, default=_unset, untrusted=False):
846 846 """parse a configuration element as a quantity in bytes
847 847
848 848 Units can be specified as b (bytes), k or kb (kilobytes), m or
849 849 mb (megabytes), g or gb (gigabytes).
850 850
851 851 >>> u = ui(); s = b'foo'
852 852 >>> u.setconfig(s, b'val1', b'42')
853 853 >>> u.configbytes(s, b'val1')
854 854 42
855 855 >>> u.setconfig(s, b'val2', b'42.5 kb')
856 856 >>> u.configbytes(s, b'val2')
857 857 43520
858 858 >>> u.configbytes(s, b'unknown', b'7 MB')
859 859 7340032
860 860 >>> u.setconfig(s, b'invalid', b'somevalue')
861 861 >>> u.configbytes(s, b'invalid')
862 862 Traceback (most recent call last):
863 863 ...
864 864 ConfigError: foo.invalid is not a byte quantity ('somevalue')
865 865 """
866 866
867 867 value = self._config(section, name, default, untrusted)
868 868 if value is _unset:
869 869 if default is _unset:
870 870 default = 0
871 871 value = default
872 872 if not isinstance(value, bytes):
873 873 return value
874 874 try:
875 875 return util.sizetoint(value)
876 876 except error.ParseError:
877 877 raise error.ConfigError(
878 878 _(b"%s.%s is not a byte quantity ('%s')")
879 879 % (section, name, value)
880 880 )
881 881
882 882 def configlist(self, section, name, default=_unset, untrusted=False):
883 883 """parse a configuration element as a list of comma/space separated
884 884 strings
885 885
886 886 >>> u = ui(); s = b'foo'
887 887 >>> u.setconfig(s, b'list1', b'this,is "a small" ,test')
888 888 >>> u.configlist(s, b'list1')
889 889 ['this', 'is', 'a small', 'test']
890 890 >>> u.setconfig(s, b'list2', b'this, is "a small" , test ')
891 891 >>> u.configlist(s, b'list2')
892 892 ['this', 'is', 'a small', 'test']
893 893 """
894 894 # default is not always a list
895 895 v = self.configwith(
896 896 stringutil.parselist, section, name, default, b'list', untrusted
897 897 )
898 898 if isinstance(v, bytes):
899 899 return stringutil.parselist(v)
900 900 elif v is None:
901 901 return []
902 902 return v
903 903
904 904 def configdate(self, section, name, default=_unset, untrusted=False):
905 905 """parse a configuration element as a tuple of ints
906 906
907 907 >>> u = ui(); s = b'foo'
908 908 >>> u.setconfig(s, b'date', b'0 0')
909 909 >>> u.configdate(s, b'date')
910 910 (0, 0)
911 911 """
912 912 if self.config(section, name, default, untrusted):
913 913 return self.configwith(
914 914 dateutil.parsedate, section, name, default, b'date', untrusted
915 915 )
916 916 if default is _unset:
917 917 return None
918 918 return default
919 919
920 920 def configdefault(self, section, name):
921 921 """returns the default value of the config item"""
922 922 item = self._knownconfig.get(section, {}).get(name)
923 923 itemdefault = None
924 924 if item is not None:
925 925 if callable(item.default):
926 926 itemdefault = item.default()
927 927 else:
928 928 itemdefault = item.default
929 929 return itemdefault
930 930
931 931 def hasconfig(self, section, name, untrusted=False):
932 932 return self._data(untrusted).hasitem(section, name)
933 933
934 934 def has_section(self, section, untrusted=False):
935 935 '''tell whether section exists in config.'''
936 936 return section in self._data(untrusted)
937 937
938 938 def configitems(self, section, untrusted=False, ignoresub=False):
939 939 items = self._data(untrusted).items(section)
940 940 if ignoresub:
941 941 items = [i for i in items if b':' not in i[0]]
942 942 if self.debugflag and not untrusted and self._reportuntrusted:
943 943 for k, v in self._ucfg.items(section):
944 944 if self._tcfg.get(section, k) != v:
945 945 self.debug(
946 946 b"ignoring untrusted configuration option "
947 947 b"%s.%s = %s\n" % (section, k, v)
948 948 )
949 949 return items
950 950
951 def walkconfig(self, untrusted=False):
951 def walkconfig(self, untrusted=False, all_known=False):
952 defined = self._walk_config(untrusted)
953 if not all_known:
954 for d in defined:
955 yield d
956 return
957 known = self._walk_known()
958 current_defined = next(defined, None)
959 current_known = next(known, None)
960 while current_defined is not None or current_known is not None:
961 if current_defined is None:
962 yield current_known
963 current_known = next(known, None)
964 elif current_known is None:
965 yield current_defined
966 current_defined = next(defined, None)
967 elif current_known[0:2] == current_defined[0:2]:
968 yield current_defined
969 current_defined = next(defined, None)
970 current_known = next(known, None)
971 elif current_known[0:2] < current_defined[0:2]:
972 yield current_known
973 current_known = next(known, None)
974 else:
975 yield current_defined
976 current_defined = next(defined, None)
977
978 def _walk_known(self):
979 for section, items in sorted(self._knownconfig.items()):
980 for k, i in sorted(items.items()):
981 # We don't have a way to display generic well, so skip them
982 if i.generic:
983 continue
984 if callable(i.default):
985 default = i.default()
986 elif i.default is configitems.dynamicdefault:
987 default = b'<DYNAMIC>'
988 else:
989 default = i.default
990 yield section, i.name, default
991
992 def _walk_config(self, untrusted):
952 993 cfg = self._data(untrusted)
953 994 for section in cfg.sections():
954 995 for name, value in self.configitems(section, untrusted):
955 996 yield section, name, value
956 997
957 998 def plain(self, feature=None):
958 999 """is plain mode active?
959 1000
960 1001 Plain mode means that all configuration variables which affect
961 1002 the behavior and output of Mercurial should be
962 1003 ignored. Additionally, the output should be stable,
963 1004 reproducible and suitable for use in scripts or applications.
964 1005
965 1006 The only way to trigger plain mode is by setting either the
966 1007 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
967 1008
968 1009 The return value can either be
969 1010 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
970 1011 - False if feature is disabled by default and not included in HGPLAIN
971 1012 - True otherwise
972 1013 """
973 1014 if (
974 1015 b'HGPLAIN' not in encoding.environ
975 1016 and b'HGPLAINEXCEPT' not in encoding.environ
976 1017 ):
977 1018 return False
978 1019 exceptions = (
979 1020 encoding.environ.get(b'HGPLAINEXCEPT', b'').strip().split(b',')
980 1021 )
981 1022 # TODO: add support for HGPLAIN=+feature,-feature syntax
982 1023 if b'+strictflags' not in encoding.environ.get(b'HGPLAIN', b'').split(
983 1024 b','
984 1025 ):
985 1026 exceptions.append(b'strictflags')
986 1027 if feature and exceptions:
987 1028 return feature not in exceptions
988 1029 return True
989 1030
990 1031 def username(self, acceptempty=False):
991 1032 """Return default username to be used in commits.
992 1033
993 1034 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
994 1035 and stop searching if one of these is set.
995 1036 If not found and acceptempty is True, returns None.
996 1037 If not found and ui.askusername is True, ask the user, else use
997 1038 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
998 1039 If no username could be found, raise an Abort error.
999 1040 """
1000 1041 user = encoding.environ.get(b"HGUSER")
1001 1042 if user is None:
1002 1043 user = self.config(b"ui", b"username")
1003 1044 if user is not None:
1004 1045 user = os.path.expandvars(user)
1005 1046 if user is None:
1006 1047 user = encoding.environ.get(b"EMAIL")
1007 1048 if user is None and acceptempty:
1008 1049 return user
1009 1050 if user is None and self.configbool(b"ui", b"askusername"):
1010 1051 user = self.prompt(_(b"enter a commit username:"), default=None)
1011 1052 if user is None and not self.interactive():
1012 1053 try:
1013 1054 user = b'%s@%s' % (
1014 1055 procutil.getuser(),
1015 1056 encoding.strtolocal(socket.getfqdn()),
1016 1057 )
1017 1058 self.warn(_(b"no username found, using '%s' instead\n") % user)
1018 1059 except KeyError:
1019 1060 pass
1020 1061 if not user:
1021 1062 raise error.Abort(
1022 1063 _(b'no username supplied'),
1023 1064 hint=_(b"use 'hg config --edit' " b'to set your username'),
1024 1065 )
1025 1066 if b"\n" in user:
1026 1067 raise error.Abort(
1027 1068 _(b"username %r contains a newline\n") % pycompat.bytestr(user)
1028 1069 )
1029 1070 return user
1030 1071
1031 1072 def shortuser(self, user):
1032 1073 """Return a short representation of a user name or email address."""
1033 1074 if not self.verbose:
1034 1075 user = stringutil.shortuser(user)
1035 1076 return user
1036 1077
1037 1078 def expandpath(self, loc, default=None):
1038 1079 """Return repository location relative to cwd or from [paths]"""
1039 1080 msg = b'ui.expandpath is deprecated, use `get_*` functions from urlutil'
1040 1081 self.deprecwarn(msg, b'6.0')
1041 1082 try:
1042 1083 p = self.getpath(loc)
1043 1084 if p:
1044 1085 return p.rawloc
1045 1086 except error.RepoError:
1046 1087 pass
1047 1088
1048 1089 if default:
1049 1090 try:
1050 1091 p = self.getpath(default)
1051 1092 if p:
1052 1093 return p.rawloc
1053 1094 except error.RepoError:
1054 1095 pass
1055 1096
1056 1097 return loc
1057 1098
1058 1099 @util.propertycache
1059 1100 def paths(self):
1060 1101 return urlutil.paths(self)
1061 1102
1062 1103 def getpath(self, *args, **kwargs):
1063 1104 """see paths.getpath for details
1064 1105
1065 1106 This method exist as `getpath` need a ui for potential warning message.
1066 1107 """
1067 1108 msg = b'ui.getpath is deprecated, use `get_*` functions from urlutil'
1068 1109 self.deprecwarn(msg, b'6.0')
1069 1110 return self.paths.getpath(self, *args, **kwargs)
1070 1111
1071 1112 @property
1072 1113 def fout(self):
1073 1114 return self._fout
1074 1115
1075 1116 @fout.setter
1076 1117 def fout(self, f):
1077 1118 self._fout = f
1078 1119 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
1079 1120
1080 1121 @property
1081 1122 def ferr(self):
1082 1123 return self._ferr
1083 1124
1084 1125 @ferr.setter
1085 1126 def ferr(self, f):
1086 1127 self._ferr = f
1087 1128 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
1088 1129
1089 1130 @property
1090 1131 def fin(self):
1091 1132 return self._fin
1092 1133
1093 1134 @fin.setter
1094 1135 def fin(self, f):
1095 1136 self._fin = f
1096 1137
1097 1138 @property
1098 1139 def fmsg(self):
1099 1140 """Stream dedicated for status/error messages; may be None if
1100 1141 fout/ferr are used"""
1101 1142 return self._fmsg
1102 1143
1103 1144 @fmsg.setter
1104 1145 def fmsg(self, f):
1105 1146 self._fmsg = f
1106 1147 self._fmsgout, self._fmsgerr = _selectmsgdests(self)
1107 1148
1108 1149 def pushbuffer(self, error=False, subproc=False, labeled=False):
1109 1150 """install a buffer to capture standard output of the ui object
1110 1151
1111 1152 If error is True, the error output will be captured too.
1112 1153
1113 1154 If subproc is True, output from subprocesses (typically hooks) will be
1114 1155 captured too.
1115 1156
1116 1157 If labeled is True, any labels associated with buffered
1117 1158 output will be handled. By default, this has no effect
1118 1159 on the output returned, but extensions and GUI tools may
1119 1160 handle this argument and returned styled output. If output
1120 1161 is being buffered so it can be captured and parsed or
1121 1162 processed, labeled should not be set to True.
1122 1163 """
1123 1164 self._buffers.append([])
1124 1165 self._bufferstates.append((error, subproc, labeled))
1125 1166 self._bufferapplylabels = labeled
1126 1167
1127 1168 def popbuffer(self):
1128 1169 '''pop the last buffer and return the buffered output'''
1129 1170 self._bufferstates.pop()
1130 1171 if self._bufferstates:
1131 1172 self._bufferapplylabels = self._bufferstates[-1][2]
1132 1173 else:
1133 1174 self._bufferapplylabels = None
1134 1175
1135 1176 return b"".join(self._buffers.pop())
1136 1177
1137 1178 def _isbuffered(self, dest):
1138 1179 if dest is self._fout:
1139 1180 return bool(self._buffers)
1140 1181 if dest is self._ferr:
1141 1182 return bool(self._bufferstates and self._bufferstates[-1][0])
1142 1183 return False
1143 1184
1144 1185 def canwritewithoutlabels(self):
1145 1186 '''check if write skips the label'''
1146 1187 if self._buffers and not self._bufferapplylabels:
1147 1188 return True
1148 1189 return self._colormode is None
1149 1190
1150 1191 def canbatchlabeledwrites(self):
1151 1192 '''check if write calls with labels are batchable'''
1152 1193 # Windows color printing is special, see ``write``.
1153 1194 return self._colormode != b'win32'
1154 1195
1155 1196 def write(self, *args, **opts):
1156 1197 """write args to output
1157 1198
1158 1199 By default, this method simply writes to the buffer or stdout.
1159 1200 Color mode can be set on the UI class to have the output decorated
1160 1201 with color modifier before being written to stdout.
1161 1202
1162 1203 The color used is controlled by an optional keyword argument, "label".
1163 1204 This should be a string containing label names separated by space.
1164 1205 Label names take the form of "topic.type". For example, ui.debug()
1165 1206 issues a label of "ui.debug".
1166 1207
1167 1208 Progress reports via stderr are normally cleared before writing as
1168 1209 stdout and stderr go to the same terminal. This can be skipped with
1169 1210 the optional keyword argument "keepprogressbar". The progress bar
1170 1211 will continue to occupy a partial line on stderr in that case.
1171 1212 This functionality is intended when Mercurial acts as data source
1172 1213 in a pipe.
1173 1214
1174 1215 When labeling output for a specific command, a label of
1175 1216 "cmdname.type" is recommended. For example, status issues
1176 1217 a label of "status.modified" for modified files.
1177 1218 """
1178 1219 dest = self._fout
1179 1220
1180 1221 # inlined _write() for speed
1181 1222 if self._buffers:
1182 1223 label = opts.get('label', b'')
1183 1224 if label and self._bufferapplylabels:
1184 1225 self._buffers[-1].extend(self.label(a, label) for a in args)
1185 1226 else:
1186 1227 self._buffers[-1].extend(args)
1187 1228 return
1188 1229
1189 1230 # inlined _writenobuf() for speed
1190 1231 if not opts.get('keepprogressbar', False):
1191 1232 self._progclear()
1192 1233 msg = b''.join(args)
1193 1234
1194 1235 # opencode timeblockedsection because this is a critical path
1195 1236 starttime = util.timer()
1196 1237 try:
1197 1238 if self._colormode == b'win32':
1198 1239 # windows color printing is its own can of crab, defer to
1199 1240 # the color module and that is it.
1200 1241 color.win32print(self, dest.write, msg, **opts)
1201 1242 else:
1202 1243 if self._colormode is not None:
1203 1244 label = opts.get('label', b'')
1204 1245 msg = self.label(msg, label)
1205 1246 dest.write(msg)
1206 1247 except IOError as err:
1207 1248 raise error.StdioError(err)
1208 1249 finally:
1209 1250 self._blockedtimes[b'stdio_blocked'] += (
1210 1251 util.timer() - starttime
1211 1252 ) * 1000
1212 1253
1213 1254 def write_err(self, *args, **opts):
1214 1255 self._write(self._ferr, *args, **opts)
1215 1256
1216 1257 def _write(self, dest, *args, **opts):
1217 1258 # update write() as well if you touch this code
1218 1259 if self._isbuffered(dest):
1219 1260 label = opts.get('label', b'')
1220 1261 if label and self._bufferapplylabels:
1221 1262 self._buffers[-1].extend(self.label(a, label) for a in args)
1222 1263 else:
1223 1264 self._buffers[-1].extend(args)
1224 1265 else:
1225 1266 self._writenobuf(dest, *args, **opts)
1226 1267
1227 1268 def _writenobuf(self, dest, *args, **opts):
1228 1269 # update write() as well if you touch this code
1229 1270 if not opts.get('keepprogressbar', False):
1230 1271 self._progclear()
1231 1272 msg = b''.join(args)
1232 1273
1233 1274 # opencode timeblockedsection because this is a critical path
1234 1275 starttime = util.timer()
1235 1276 try:
1236 1277 if dest is self._ferr and not getattr(self._fout, 'closed', False):
1237 1278 self._fout.flush()
1238 1279 if getattr(dest, 'structured', False):
1239 1280 # channel for machine-readable output with metadata, where
1240 1281 # no extra colorization is necessary.
1241 1282 dest.write(msg, **opts)
1242 1283 elif self._colormode == b'win32':
1243 1284 # windows color printing is its own can of crab, defer to
1244 1285 # the color module and that is it.
1245 1286 color.win32print(self, dest.write, msg, **opts)
1246 1287 else:
1247 1288 if self._colormode is not None:
1248 1289 label = opts.get('label', b'')
1249 1290 msg = self.label(msg, label)
1250 1291 dest.write(msg)
1251 1292 # stderr may be buffered under win32 when redirected to files,
1252 1293 # including stdout.
1253 1294 if dest is self._ferr and not getattr(dest, 'closed', False):
1254 1295 dest.flush()
1255 1296 except IOError as err:
1256 1297 if dest is self._ferr and err.errno in (
1257 1298 errno.EPIPE,
1258 1299 errno.EIO,
1259 1300 errno.EBADF,
1260 1301 ):
1261 1302 # no way to report the error, so ignore it
1262 1303 return
1263 1304 raise error.StdioError(err)
1264 1305 finally:
1265 1306 self._blockedtimes[b'stdio_blocked'] += (
1266 1307 util.timer() - starttime
1267 1308 ) * 1000
1268 1309
1269 1310 def _writemsg(self, dest, *args, **opts):
1270 1311 timestamp = self.showtimestamp and opts.get('type') in {
1271 1312 b'debug',
1272 1313 b'error',
1273 1314 b'note',
1274 1315 b'status',
1275 1316 b'warning',
1276 1317 }
1277 1318 if timestamp:
1278 1319 args = (
1279 1320 b'[%s] '
1280 1321 % pycompat.bytestr(datetime.datetime.now().isoformat()),
1281 1322 ) + args
1282 1323 _writemsgwith(self._write, dest, *args, **opts)
1283 1324 if timestamp:
1284 1325 dest.flush()
1285 1326
1286 1327 def _writemsgnobuf(self, dest, *args, **opts):
1287 1328 _writemsgwith(self._writenobuf, dest, *args, **opts)
1288 1329
1289 1330 def flush(self):
1290 1331 # opencode timeblockedsection because this is a critical path
1291 1332 starttime = util.timer()
1292 1333 try:
1293 1334 try:
1294 1335 self._fout.flush()
1295 1336 except IOError as err:
1296 1337 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1297 1338 raise error.StdioError(err)
1298 1339 finally:
1299 1340 try:
1300 1341 self._ferr.flush()
1301 1342 except IOError as err:
1302 1343 if err.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
1303 1344 raise error.StdioError(err)
1304 1345 finally:
1305 1346 self._blockedtimes[b'stdio_blocked'] += (
1306 1347 util.timer() - starttime
1307 1348 ) * 1000
1308 1349
1309 1350 def _isatty(self, fh):
1310 1351 if self.configbool(b'ui', b'nontty'):
1311 1352 return False
1312 1353 return procutil.isatty(fh)
1313 1354
1314 1355 def protectfinout(self):
1315 1356 """Duplicate ui streams and redirect original if they are stdio
1316 1357
1317 1358 Returns (fin, fout) which point to the original ui fds, but may be
1318 1359 copy of them. The returned streams can be considered "owned" in that
1319 1360 print(), exec(), etc. never reach to them.
1320 1361 """
1321 1362 if self._finoutredirected:
1322 1363 # if already redirected, protectstdio() would just create another
1323 1364 # nullfd pair, which is equivalent to returning self._fin/_fout.
1324 1365 return self._fin, self._fout
1325 1366 fin, fout = procutil.protectstdio(self._fin, self._fout)
1326 1367 self._finoutredirected = (fin, fout) != (self._fin, self._fout)
1327 1368 return fin, fout
1328 1369
1329 1370 def restorefinout(self, fin, fout):
1330 1371 """Restore ui streams from possibly duplicated (fin, fout)"""
1331 1372 if (fin, fout) == (self._fin, self._fout):
1332 1373 return
1333 1374 procutil.restorestdio(self._fin, self._fout, fin, fout)
1334 1375 # protectfinout() won't create more than one duplicated streams,
1335 1376 # so we can just turn the redirection flag off.
1336 1377 self._finoutredirected = False
1337 1378
1338 1379 @contextlib.contextmanager
1339 1380 def protectedfinout(self):
1340 1381 """Run code block with protected standard streams"""
1341 1382 fin, fout = self.protectfinout()
1342 1383 try:
1343 1384 yield fin, fout
1344 1385 finally:
1345 1386 self.restorefinout(fin, fout)
1346 1387
1347 1388 def disablepager(self):
1348 1389 self._disablepager = True
1349 1390
1350 1391 def pager(self, command):
1351 1392 """Start a pager for subsequent command output.
1352 1393
1353 1394 Commands which produce a long stream of output should call
1354 1395 this function to activate the user's preferred pagination
1355 1396 mechanism (which may be no pager). Calling this function
1356 1397 precludes any future use of interactive functionality, such as
1357 1398 prompting the user or activating curses.
1358 1399
1359 1400 Args:
1360 1401 command: The full, non-aliased name of the command. That is, "log"
1361 1402 not "history, "summary" not "summ", etc.
1362 1403 """
1363 1404 if self._disablepager or self.pageractive:
1364 1405 # how pager should do is already determined
1365 1406 return
1366 1407
1367 1408 if not command.startswith(b'internal-always-') and (
1368 1409 # explicit --pager=on (= 'internal-always-' prefix) should
1369 1410 # take precedence over disabling factors below
1370 1411 command in self.configlist(b'pager', b'ignore')
1371 1412 or not self.configbool(b'ui', b'paginate')
1372 1413 or not self.configbool(b'pager', b'attend-' + command, True)
1373 1414 or encoding.environ.get(b'TERM') == b'dumb'
1374 1415 # TODO: if we want to allow HGPLAINEXCEPT=pager,
1375 1416 # formatted() will need some adjustment.
1376 1417 or not self.formatted()
1377 1418 or self.plain()
1378 1419 or self._buffers
1379 1420 # TODO: expose debugger-enabled on the UI object
1380 1421 or b'--debugger' in pycompat.sysargv
1381 1422 ):
1382 1423 # We only want to paginate if the ui appears to be
1383 1424 # interactive, the user didn't say HGPLAIN or
1384 1425 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
1385 1426 return
1386 1427
1387 1428 pagercmd = self.config(b'pager', b'pager', rcutil.fallbackpager)
1388 1429 if not pagercmd:
1389 1430 return
1390 1431
1391 1432 pagerenv = {}
1392 1433 for name, value in rcutil.defaultpagerenv().items():
1393 1434 if name not in encoding.environ:
1394 1435 pagerenv[name] = value
1395 1436
1396 1437 self.debug(
1397 1438 b'starting pager for command %s\n' % stringutil.pprint(command)
1398 1439 )
1399 1440 self.flush()
1400 1441
1401 1442 wasformatted = self.formatted()
1402 1443 if util.safehasattr(signal, b"SIGPIPE"):
1403 1444 signal.signal(signal.SIGPIPE, _catchterm)
1404 1445 if self._runpager(pagercmd, pagerenv):
1405 1446 self.pageractive = True
1406 1447 # Preserve the formatted-ness of the UI. This is important
1407 1448 # because we mess with stdout, which might confuse
1408 1449 # auto-detection of things being formatted.
1409 1450 self.setconfig(b'ui', b'formatted', wasformatted, b'pager')
1410 1451 self.setconfig(b'ui', b'interactive', False, b'pager')
1411 1452
1412 1453 # If pagermode differs from color.mode, reconfigure color now that
1413 1454 # pageractive is set.
1414 1455 cm = self._colormode
1415 1456 if cm != self.config(b'color', b'pagermode', cm):
1416 1457 color.setup(self)
1417 1458 else:
1418 1459 # If the pager can't be spawned in dispatch when --pager=on is
1419 1460 # given, don't try again when the command runs, to avoid a duplicate
1420 1461 # warning about a missing pager command.
1421 1462 self.disablepager()
1422 1463
1423 1464 def _runpager(self, command, env=None):
1424 1465 """Actually start the pager and set up file descriptors.
1425 1466
1426 1467 This is separate in part so that extensions (like chg) can
1427 1468 override how a pager is invoked.
1428 1469 """
1429 1470 if command == b'cat':
1430 1471 # Save ourselves some work.
1431 1472 return False
1432 1473 # If the command doesn't contain any of these characters, we
1433 1474 # assume it's a binary and exec it directly. This means for
1434 1475 # simple pager command configurations, we can degrade
1435 1476 # gracefully and tell the user about their broken pager.
1436 1477 shell = any(c in command for c in b"|&;<>()$`\\\"' \t\n*?[#~=%")
1437 1478
1438 1479 if pycompat.iswindows and not shell:
1439 1480 # Window's built-in `more` cannot be invoked with shell=False, but
1440 1481 # its `more.com` can. Hide this implementation detail from the
1441 1482 # user so we can also get sane bad PAGER behavior. MSYS has
1442 1483 # `more.exe`, so do a cmd.exe style resolution of the executable to
1443 1484 # determine which one to use.
1444 1485 fullcmd = procutil.findexe(command)
1445 1486 if not fullcmd:
1446 1487 self.warn(
1447 1488 _(b"missing pager command '%s', skipping pager\n") % command
1448 1489 )
1449 1490 return False
1450 1491
1451 1492 command = fullcmd
1452 1493
1453 1494 try:
1454 1495 pager = subprocess.Popen(
1455 1496 procutil.tonativestr(command),
1456 1497 shell=shell,
1457 1498 bufsize=-1,
1458 1499 close_fds=procutil.closefds,
1459 1500 stdin=subprocess.PIPE,
1460 1501 stdout=procutil.stdout,
1461 1502 stderr=procutil.stderr,
1462 1503 env=procutil.tonativeenv(procutil.shellenviron(env)),
1463 1504 )
1464 1505 except OSError as e:
1465 1506 if e.errno == errno.ENOENT and not shell:
1466 1507 self.warn(
1467 1508 _(b"missing pager command '%s', skipping pager\n") % command
1468 1509 )
1469 1510 return False
1470 1511 raise
1471 1512
1472 1513 # back up original file descriptors
1473 1514 stdoutfd = os.dup(procutil.stdout.fileno())
1474 1515 stderrfd = os.dup(procutil.stderr.fileno())
1475 1516
1476 1517 os.dup2(pager.stdin.fileno(), procutil.stdout.fileno())
1477 1518 if self._isatty(procutil.stderr):
1478 1519 os.dup2(pager.stdin.fileno(), procutil.stderr.fileno())
1479 1520
1480 1521 @self.atexit
1481 1522 def killpager():
1482 1523 if util.safehasattr(signal, b"SIGINT"):
1483 1524 signal.signal(signal.SIGINT, signal.SIG_IGN)
1484 1525 # restore original fds, closing pager.stdin copies in the process
1485 1526 os.dup2(stdoutfd, procutil.stdout.fileno())
1486 1527 os.dup2(stderrfd, procutil.stderr.fileno())
1487 1528 pager.stdin.close()
1488 1529 pager.wait()
1489 1530
1490 1531 return True
1491 1532
1492 1533 @property
1493 1534 def _exithandlers(self):
1494 1535 return _reqexithandlers
1495 1536
1496 1537 def atexit(self, func, *args, **kwargs):
1497 1538 """register a function to run after dispatching a request
1498 1539
1499 1540 Handlers do not stay registered across request boundaries."""
1500 1541 self._exithandlers.append((func, args, kwargs))
1501 1542 return func
1502 1543
1503 1544 def interface(self, feature):
1504 1545 """what interface to use for interactive console features?
1505 1546
1506 1547 The interface is controlled by the value of `ui.interface` but also by
1507 1548 the value of feature-specific configuration. For example:
1508 1549
1509 1550 ui.interface.histedit = text
1510 1551 ui.interface.chunkselector = curses
1511 1552
1512 1553 Here the features are "histedit" and "chunkselector".
1513 1554
1514 1555 The configuration above means that the default interfaces for commands
1515 1556 is curses, the interface for histedit is text and the interface for
1516 1557 selecting chunk is crecord (the best curses interface available).
1517 1558
1518 1559 Consider the following example:
1519 1560 ui.interface = curses
1520 1561 ui.interface.histedit = text
1521 1562
1522 1563 Then histedit will use the text interface and chunkselector will use
1523 1564 the default curses interface (crecord at the moment).
1524 1565 """
1525 1566 alldefaults = frozenset([b"text", b"curses"])
1526 1567
1527 1568 featureinterfaces = {
1528 1569 b"chunkselector": [
1529 1570 b"text",
1530 1571 b"curses",
1531 1572 ],
1532 1573 b"histedit": [
1533 1574 b"text",
1534 1575 b"curses",
1535 1576 ],
1536 1577 }
1537 1578
1538 1579 # Feature-specific interface
1539 1580 if feature not in featureinterfaces.keys():
1540 1581 # Programming error, not user error
1541 1582 raise ValueError(b"Unknown feature requested %s" % feature)
1542 1583
1543 1584 availableinterfaces = frozenset(featureinterfaces[feature])
1544 1585 if alldefaults > availableinterfaces:
1545 1586 # Programming error, not user error. We need a use case to
1546 1587 # define the right thing to do here.
1547 1588 raise ValueError(
1548 1589 b"Feature %s does not handle all default interfaces" % feature
1549 1590 )
1550 1591
1551 1592 if self.plain() or encoding.environ.get(b'TERM') == b'dumb':
1552 1593 return b"text"
1553 1594
1554 1595 # Default interface for all the features
1555 1596 defaultinterface = b"text"
1556 1597 i = self.config(b"ui", b"interface")
1557 1598 if i in alldefaults:
1558 1599 defaultinterface = i
1559 1600
1560 1601 choseninterface = defaultinterface
1561 1602 f = self.config(b"ui", b"interface.%s" % feature)
1562 1603 if f in availableinterfaces:
1563 1604 choseninterface = f
1564 1605
1565 1606 if i is not None and defaultinterface != i:
1566 1607 if f is not None:
1567 1608 self.warn(_(b"invalid value for ui.interface: %s\n") % (i,))
1568 1609 else:
1569 1610 self.warn(
1570 1611 _(b"invalid value for ui.interface: %s (using %s)\n")
1571 1612 % (i, choseninterface)
1572 1613 )
1573 1614 if f is not None and choseninterface != f:
1574 1615 self.warn(
1575 1616 _(b"invalid value for ui.interface.%s: %s (using %s)\n")
1576 1617 % (feature, f, choseninterface)
1577 1618 )
1578 1619
1579 1620 return choseninterface
1580 1621
1581 1622 def interactive(self):
1582 1623 """is interactive input allowed?
1583 1624
1584 1625 An interactive session is a session where input can be reasonably read
1585 1626 from `sys.stdin'. If this function returns false, any attempt to read
1586 1627 from stdin should fail with an error, unless a sensible default has been
1587 1628 specified.
1588 1629
1589 1630 Interactiveness is triggered by the value of the `ui.interactive'
1590 1631 configuration variable or - if it is unset - when `sys.stdin' points
1591 1632 to a terminal device.
1592 1633
1593 1634 This function refers to input only; for output, see `ui.formatted()'.
1594 1635 """
1595 1636 i = self.configbool(b"ui", b"interactive")
1596 1637 if i is None:
1597 1638 # some environments replace stdin without implementing isatty
1598 1639 # usually those are non-interactive
1599 1640 return self._isatty(self._fin)
1600 1641
1601 1642 return i
1602 1643
1603 1644 def termwidth(self):
1604 1645 """how wide is the terminal in columns?"""
1605 1646 if b'COLUMNS' in encoding.environ:
1606 1647 try:
1607 1648 return int(encoding.environ[b'COLUMNS'])
1608 1649 except ValueError:
1609 1650 pass
1610 1651 return scmutil.termsize(self)[0]
1611 1652
1612 1653 def formatted(self):
1613 1654 """should formatted output be used?
1614 1655
1615 1656 It is often desirable to format the output to suite the output medium.
1616 1657 Examples of this are truncating long lines or colorizing messages.
1617 1658 However, this is not often not desirable when piping output into other
1618 1659 utilities, e.g. `grep'.
1619 1660
1620 1661 Formatted output is triggered by the value of the `ui.formatted'
1621 1662 configuration variable or - if it is unset - when `sys.stdout' points
1622 1663 to a terminal device. Please note that `ui.formatted' should be
1623 1664 considered an implementation detail; it is not intended for use outside
1624 1665 Mercurial or its extensions.
1625 1666
1626 1667 This function refers to output only; for input, see `ui.interactive()'.
1627 1668 This function always returns false when in plain mode, see `ui.plain()'.
1628 1669 """
1629 1670 if self.plain():
1630 1671 return False
1631 1672
1632 1673 i = self.configbool(b"ui", b"formatted")
1633 1674 if i is None:
1634 1675 # some environments replace stdout without implementing isatty
1635 1676 # usually those are non-interactive
1636 1677 return self._isatty(self._fout)
1637 1678
1638 1679 return i
1639 1680
1640 1681 def _readline(self, prompt=b' ', promptopts=None):
1641 1682 # Replacing stdin/stdout temporarily is a hard problem on Python 3
1642 1683 # because they have to be text streams with *no buffering*. Instead,
1643 1684 # we use rawinput() only if call_readline() will be invoked by
1644 1685 # PyOS_Readline(), so no I/O will be made at Python layer.
1645 1686 usereadline = (
1646 1687 self._isatty(self._fin)
1647 1688 and self._isatty(self._fout)
1648 1689 and procutil.isstdin(self._fin)
1649 1690 and procutil.isstdout(self._fout)
1650 1691 )
1651 1692 if usereadline:
1652 1693 try:
1653 1694 # magically add command line editing support, where
1654 1695 # available
1655 1696 import readline
1656 1697
1657 1698 # force demandimport to really load the module
1658 1699 readline.read_history_file
1659 1700 # windows sometimes raises something other than ImportError
1660 1701 except Exception:
1661 1702 usereadline = False
1662 1703
1663 1704 if self._colormode == b'win32' or not usereadline:
1664 1705 if not promptopts:
1665 1706 promptopts = {}
1666 1707 self._writemsgnobuf(
1667 1708 self._fmsgout, prompt, type=b'prompt', **promptopts
1668 1709 )
1669 1710 self.flush()
1670 1711 prompt = b' '
1671 1712 else:
1672 1713 prompt = self.label(prompt, b'ui.prompt') + b' '
1673 1714
1674 1715 # prompt ' ' must exist; otherwise readline may delete entire line
1675 1716 # - http://bugs.python.org/issue12833
1676 1717 with self.timeblockedsection(b'stdio'):
1677 1718 if usereadline:
1678 1719 self.flush()
1679 1720 prompt = encoding.strfromlocal(prompt)
1680 1721 line = encoding.strtolocal(pycompat.rawinput(prompt))
1681 1722 # When stdin is in binary mode on Windows, it can cause
1682 1723 # raw_input() to emit an extra trailing carriage return
1683 1724 if pycompat.oslinesep == b'\r\n' and line.endswith(b'\r'):
1684 1725 line = line[:-1]
1685 1726 else:
1686 1727 self._fout.write(pycompat.bytestr(prompt))
1687 1728 self._fout.flush()
1688 1729 line = self._fin.readline()
1689 1730 if not line:
1690 1731 raise EOFError
1691 1732 line = line.rstrip(pycompat.oslinesep)
1692 1733
1693 1734 return line
1694 1735
1695 1736 def prompt(self, msg, default=b"y"):
1696 1737 """Prompt user with msg, read response.
1697 1738 If ui is not interactive, the default is returned.
1698 1739 """
1699 1740 return self._prompt(msg, default=default)
1700 1741
1701 1742 def _prompt(self, msg, **opts):
1702 1743 default = opts['default']
1703 1744 if not self.interactive():
1704 1745 self._writemsg(self._fmsgout, msg, b' ', type=b'prompt', **opts)
1705 1746 self._writemsg(
1706 1747 self._fmsgout, default or b'', b"\n", type=b'promptecho'
1707 1748 )
1708 1749 return default
1709 1750 try:
1710 1751 r = self._readline(prompt=msg, promptopts=opts)
1711 1752 if not r:
1712 1753 r = default
1713 1754 if self.configbool(b'ui', b'promptecho'):
1714 1755 self._writemsg(
1715 1756 self._fmsgout, r or b'', b"\n", type=b'promptecho'
1716 1757 )
1717 1758 return r
1718 1759 except EOFError:
1719 1760 raise error.ResponseExpected()
1720 1761
1721 1762 @staticmethod
1722 1763 def extractchoices(prompt):
1723 1764 """Extract prompt message and list of choices from specified prompt.
1724 1765
1725 1766 This returns tuple "(message, choices)", and "choices" is the
1726 1767 list of tuple "(response character, text without &)".
1727 1768
1728 1769 >>> ui.extractchoices(b"awake? $$ &Yes $$ &No")
1729 1770 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1730 1771 >>> ui.extractchoices(b"line\\nbreak? $$ &Yes $$ &No")
1731 1772 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1732 1773 >>> ui.extractchoices(b"want lots of $$money$$?$$Ye&s$$N&o")
1733 1774 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1734 1775 """
1735 1776
1736 1777 # Sadly, the prompt string may have been built with a filename
1737 1778 # containing "$$" so let's try to find the first valid-looking
1738 1779 # prompt to start parsing. Sadly, we also can't rely on
1739 1780 # choices containing spaces, ASCII, or basically anything
1740 1781 # except an ampersand followed by a character.
1741 1782 m = re.match(br'(?s)(.+?)\$\$([^$]*&[^ $].*)', prompt)
1742 1783 msg = m.group(1)
1743 1784 choices = [p.strip(b' ') for p in m.group(2).split(b'$$')]
1744 1785
1745 1786 def choicetuple(s):
1746 1787 ampidx = s.index(b'&')
1747 1788 return s[ampidx + 1 : ampidx + 2].lower(), s.replace(b'&', b'', 1)
1748 1789
1749 1790 return (msg, [choicetuple(s) for s in choices])
1750 1791
1751 1792 def promptchoice(self, prompt, default=0):
1752 1793 """Prompt user with a message, read response, and ensure it matches
1753 1794 one of the provided choices. The prompt is formatted as follows:
1754 1795
1755 1796 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1756 1797
1757 1798 The index of the choice is returned. Responses are case
1758 1799 insensitive. If ui is not interactive, the default is
1759 1800 returned.
1760 1801 """
1761 1802
1762 1803 msg, choices = self.extractchoices(prompt)
1763 1804 resps = [r for r, t in choices]
1764 1805 while True:
1765 1806 r = self._prompt(msg, default=resps[default], choices=choices)
1766 1807 if r.lower() in resps:
1767 1808 return resps.index(r.lower())
1768 1809 # TODO: shouldn't it be a warning?
1769 1810 self._writemsg(self._fmsgout, _(b"unrecognized response\n"))
1770 1811
1771 1812 def getpass(self, prompt=None, default=None):
1772 1813 if not self.interactive():
1773 1814 return default
1774 1815 try:
1775 1816 self._writemsg(
1776 1817 self._fmsgerr,
1777 1818 prompt or _(b'password: '),
1778 1819 type=b'prompt',
1779 1820 password=True,
1780 1821 )
1781 1822 # disable getpass() only if explicitly specified. it's still valid
1782 1823 # to interact with tty even if fin is not a tty.
1783 1824 with self.timeblockedsection(b'stdio'):
1784 1825 if self.configbool(b'ui', b'nontty'):
1785 1826 l = self._fin.readline()
1786 1827 if not l:
1787 1828 raise EOFError
1788 1829 return l.rstrip(b'\n')
1789 1830 else:
1790 1831 return util.get_password()
1791 1832 except EOFError:
1792 1833 raise error.ResponseExpected()
1793 1834
1794 1835 def status(self, *msg, **opts):
1795 1836 """write status message to output (if ui.quiet is False)
1796 1837
1797 1838 This adds an output label of "ui.status".
1798 1839 """
1799 1840 if not self.quiet:
1800 1841 self._writemsg(self._fmsgout, type=b'status', *msg, **opts)
1801 1842
1802 1843 def warn(self, *msg, **opts):
1803 1844 """write warning message to output (stderr)
1804 1845
1805 1846 This adds an output label of "ui.warning".
1806 1847 """
1807 1848 self._writemsg(self._fmsgerr, type=b'warning', *msg, **opts)
1808 1849
1809 1850 def error(self, *msg, **opts):
1810 1851 """write error message to output (stderr)
1811 1852
1812 1853 This adds an output label of "ui.error".
1813 1854 """
1814 1855 self._writemsg(self._fmsgerr, type=b'error', *msg, **opts)
1815 1856
1816 1857 def note(self, *msg, **opts):
1817 1858 """write note to output (if ui.verbose is True)
1818 1859
1819 1860 This adds an output label of "ui.note".
1820 1861 """
1821 1862 if self.verbose:
1822 1863 self._writemsg(self._fmsgout, type=b'note', *msg, **opts)
1823 1864
1824 1865 def debug(self, *msg, **opts):
1825 1866 """write debug message to output (if ui.debugflag is True)
1826 1867
1827 1868 This adds an output label of "ui.debug".
1828 1869 """
1829 1870 if self.debugflag:
1830 1871 self._writemsg(self._fmsgout, type=b'debug', *msg, **opts)
1831 1872 self.log(b'debug', b'%s', b''.join(msg))
1832 1873
1833 1874 # Aliases to defeat check-code.
1834 1875 statusnoi18n = status
1835 1876 notenoi18n = note
1836 1877 warnnoi18n = warn
1837 1878 writenoi18n = write
1838 1879
1839 1880 def edit(
1840 1881 self,
1841 1882 text,
1842 1883 user,
1843 1884 extra=None,
1844 1885 editform=None,
1845 1886 pending=None,
1846 1887 repopath=None,
1847 1888 action=None,
1848 1889 ):
1849 1890 if action is None:
1850 1891 self.develwarn(
1851 1892 b'action is None but will soon be a required '
1852 1893 b'parameter to ui.edit()'
1853 1894 )
1854 1895 extra_defaults = {
1855 1896 b'prefix': b'editor',
1856 1897 b'suffix': b'.txt',
1857 1898 }
1858 1899 if extra is not None:
1859 1900 if extra.get(b'suffix') is not None:
1860 1901 self.develwarn(
1861 1902 b'extra.suffix is not None but will soon be '
1862 1903 b'ignored by ui.edit()'
1863 1904 )
1864 1905 extra_defaults.update(extra)
1865 1906 extra = extra_defaults
1866 1907
1867 1908 if action == b'diff':
1868 1909 suffix = b'.diff'
1869 1910 elif action:
1870 1911 suffix = b'.%s.hg.txt' % action
1871 1912 else:
1872 1913 suffix = extra[b'suffix']
1873 1914
1874 1915 rdir = None
1875 1916 if self.configbool(b'experimental', b'editortmpinhg'):
1876 1917 rdir = repopath
1877 1918 (fd, name) = pycompat.mkstemp(
1878 1919 prefix=b'hg-' + extra[b'prefix'] + b'-', suffix=suffix, dir=rdir
1879 1920 )
1880 1921 try:
1881 1922 with os.fdopen(fd, 'wb') as f:
1882 1923 f.write(util.tonativeeol(text))
1883 1924
1884 1925 environ = {b'HGUSER': user}
1885 1926 if b'transplant_source' in extra:
1886 1927 environ.update(
1887 1928 {b'HGREVISION': hex(extra[b'transplant_source'])}
1888 1929 )
1889 1930 for label in (b'intermediate-source', b'source', b'rebase_source'):
1890 1931 if label in extra:
1891 1932 environ.update({b'HGREVISION': extra[label]})
1892 1933 break
1893 1934 if editform:
1894 1935 environ.update({b'HGEDITFORM': editform})
1895 1936 if pending:
1896 1937 environ.update({b'HG_PENDING': pending})
1897 1938
1898 1939 editor = self.geteditor()
1899 1940
1900 1941 self.system(
1901 1942 b"%s \"%s\"" % (editor, name),
1902 1943 environ=environ,
1903 1944 onerr=error.CanceledError,
1904 1945 errprefix=_(b"edit failed"),
1905 1946 blockedtag=b'editor',
1906 1947 )
1907 1948
1908 1949 with open(name, 'rb') as f:
1909 1950 t = util.fromnativeeol(f.read())
1910 1951 finally:
1911 1952 os.unlink(name)
1912 1953
1913 1954 return t
1914 1955
1915 1956 def system(
1916 1957 self,
1917 1958 cmd,
1918 1959 environ=None,
1919 1960 cwd=None,
1920 1961 onerr=None,
1921 1962 errprefix=None,
1922 1963 blockedtag=None,
1923 1964 ):
1924 1965 """execute shell command with appropriate output stream. command
1925 1966 output will be redirected if fout is not stdout.
1926 1967
1927 1968 if command fails and onerr is None, return status, else raise onerr
1928 1969 object as exception.
1929 1970 """
1930 1971 if blockedtag is None:
1931 1972 # Long cmds tend to be because of an absolute path on cmd. Keep
1932 1973 # the tail end instead
1933 1974 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1934 1975 blockedtag = b'unknown_system_' + cmdsuffix
1935 1976 out = self._fout
1936 1977 if any(s[1] for s in self._bufferstates):
1937 1978 out = self
1938 1979 with self.timeblockedsection(blockedtag):
1939 1980 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1940 1981 if rc and onerr:
1941 1982 errmsg = b'%s %s' % (
1942 1983 procutil.shellsplit(cmd)[0],
1943 1984 procutil.explainexit(rc),
1944 1985 )
1945 1986 if errprefix:
1946 1987 errmsg = b'%s: %s' % (errprefix, errmsg)
1947 1988 raise onerr(errmsg)
1948 1989 return rc
1949 1990
1950 1991 def _runsystem(self, cmd, environ, cwd, out):
1951 1992 """actually execute the given shell command (can be overridden by
1952 1993 extensions like chg)"""
1953 1994 return procutil.system(cmd, environ=environ, cwd=cwd, out=out)
1954 1995
1955 1996 def traceback(self, exc=None, force=False):
1956 1997 """print exception traceback if traceback printing enabled or forced.
1957 1998 only to call in exception handler. returns true if traceback
1958 1999 printed."""
1959 2000 if self.tracebackflag or force:
1960 2001 if exc is None:
1961 2002 exc = sys.exc_info()
1962 2003 cause = getattr(exc[1], 'cause', None)
1963 2004
1964 2005 if cause is not None:
1965 2006 causetb = traceback.format_tb(cause[2])
1966 2007 exctb = traceback.format_tb(exc[2])
1967 2008 exconly = traceback.format_exception_only(cause[0], cause[1])
1968 2009
1969 2010 # exclude frame where 'exc' was chained and rethrown from exctb
1970 2011 self.write_err(
1971 2012 b'Traceback (most recent call last):\n',
1972 2013 encoding.strtolocal(''.join(exctb[:-1])),
1973 2014 encoding.strtolocal(''.join(causetb)),
1974 2015 encoding.strtolocal(''.join(exconly)),
1975 2016 )
1976 2017 else:
1977 2018 output = traceback.format_exception(exc[0], exc[1], exc[2])
1978 2019 self.write_err(encoding.strtolocal(''.join(output)))
1979 2020 return self.tracebackflag or force
1980 2021
1981 2022 def geteditor(self):
1982 2023 '''return editor to use'''
1983 2024 if pycompat.sysplatform == b'plan9':
1984 2025 # vi is the MIPS instruction simulator on Plan 9. We
1985 2026 # instead default to E to plumb commit messages to
1986 2027 # avoid confusion.
1987 2028 editor = b'E'
1988 2029 elif pycompat.isdarwin:
1989 2030 # vi on darwin is POSIX compatible to a fault, and that includes
1990 2031 # exiting non-zero if you make any mistake when running an ex
1991 2032 # command. Proof: `vi -c ':unknown' -c ':qa'; echo $?` produces 1,
1992 2033 # while s/vi/vim/ doesn't.
1993 2034 editor = b'vim'
1994 2035 else:
1995 2036 editor = b'vi'
1996 2037 return encoding.environ.get(b"HGEDITOR") or self.config(
1997 2038 b"ui", b"editor", editor
1998 2039 )
1999 2040
2000 2041 @util.propertycache
2001 2042 def _progbar(self):
2002 2043 """setup the progbar singleton to the ui object"""
2003 2044 if (
2004 2045 self.quiet
2005 2046 or self.debugflag
2006 2047 or self.configbool(b'progress', b'disable')
2007 2048 or not progress.shouldprint(self)
2008 2049 ):
2009 2050 return None
2010 2051 return getprogbar(self)
2011 2052
2012 2053 def _progclear(self):
2013 2054 """clear progress bar output if any. use it before any output"""
2014 2055 if not haveprogbar(): # nothing loaded yet
2015 2056 return
2016 2057 if self._progbar is not None and self._progbar.printed:
2017 2058 self._progbar.clear()
2018 2059
2019 2060 def makeprogress(self, topic, unit=b"", total=None):
2020 2061 """Create a progress helper for the specified topic"""
2021 2062 if getattr(self._fmsgerr, 'structured', False):
2022 2063 # channel for machine-readable output with metadata, just send
2023 2064 # raw information
2024 2065 # TODO: consider porting some useful information (e.g. estimated
2025 2066 # time) from progbar. we might want to support update delay to
2026 2067 # reduce the cost of transferring progress messages.
2027 2068 def updatebar(topic, pos, item, unit, total):
2028 2069 self._fmsgerr.write(
2029 2070 None,
2030 2071 type=b'progress',
2031 2072 topic=topic,
2032 2073 pos=pos,
2033 2074 item=item,
2034 2075 unit=unit,
2035 2076 total=total,
2036 2077 )
2037 2078
2038 2079 elif self._progbar is not None:
2039 2080 updatebar = self._progbar.progress
2040 2081 else:
2041 2082
2042 2083 def updatebar(topic, pos, item, unit, total):
2043 2084 pass
2044 2085
2045 2086 return scmutil.progress(self, updatebar, topic, unit, total)
2046 2087
2047 2088 def getlogger(self, name):
2048 2089 """Returns a logger of the given name; or None if not registered"""
2049 2090 return self._loggers.get(name)
2050 2091
2051 2092 def setlogger(self, name, logger):
2052 2093 """Install logger which can be identified later by the given name
2053 2094
2054 2095 More than one loggers can be registered. Use extension or module
2055 2096 name to uniquely identify the logger instance.
2056 2097 """
2057 2098 self._loggers[name] = logger
2058 2099
2059 2100 def log(self, event, msgfmt, *msgargs, **opts):
2060 2101 """hook for logging facility extensions
2061 2102
2062 2103 event should be a readily-identifiable subsystem, which will
2063 2104 allow filtering.
2064 2105
2065 2106 msgfmt should be a newline-terminated format string to log, and
2066 2107 *msgargs are %-formatted into it.
2067 2108
2068 2109 **opts currently has no defined meanings.
2069 2110 """
2070 2111 if not self._loggers:
2071 2112 return
2072 2113 activeloggers = [
2073 2114 l for l in pycompat.itervalues(self._loggers) if l.tracked(event)
2074 2115 ]
2075 2116 if not activeloggers:
2076 2117 return
2077 2118 msg = msgfmt % msgargs
2078 2119 opts = pycompat.byteskwargs(opts)
2079 2120 # guard against recursion from e.g. ui.debug()
2080 2121 registeredloggers = self._loggers
2081 2122 self._loggers = {}
2082 2123 try:
2083 2124 for logger in activeloggers:
2084 2125 logger.log(self, event, msg, opts)
2085 2126 finally:
2086 2127 self._loggers = registeredloggers
2087 2128
2088 2129 def label(self, msg, label):
2089 2130 """style msg based on supplied label
2090 2131
2091 2132 If some color mode is enabled, this will add the necessary control
2092 2133 characters to apply such color. In addition, 'debug' color mode adds
2093 2134 markup showing which label affects a piece of text.
2094 2135
2095 2136 ui.write(s, 'label') is equivalent to
2096 2137 ui.write(ui.label(s, 'label')).
2097 2138 """
2098 2139 if self._colormode is not None:
2099 2140 return color.colorlabel(self, msg, label)
2100 2141 return msg
2101 2142
2102 2143 def develwarn(self, msg, stacklevel=1, config=None):
2103 2144 """issue a developer warning message
2104 2145
2105 2146 Use 'stacklevel' to report the offender some layers further up in the
2106 2147 stack.
2107 2148 """
2108 2149 if not self.configbool(b'devel', b'all-warnings'):
2109 2150 if config is None or not self.configbool(b'devel', config):
2110 2151 return
2111 2152 msg = b'devel-warn: ' + msg
2112 2153 stacklevel += 1 # get in develwarn
2113 2154 if self.tracebackflag:
2114 2155 util.debugstacktrace(msg, stacklevel, self._ferr, self._fout)
2115 2156 self.log(
2116 2157 b'develwarn',
2117 2158 b'%s at:\n%s'
2118 2159 % (msg, b''.join(util.getstackframes(stacklevel))),
2119 2160 )
2120 2161 else:
2121 2162 curframe = inspect.currentframe()
2122 2163 calframe = inspect.getouterframes(curframe, 2)
2123 2164 fname, lineno, fmsg = calframe[stacklevel][1:4]
2124 2165 fname, fmsg = pycompat.sysbytes(fname), pycompat.sysbytes(fmsg)
2125 2166 self.write_err(b'%s at: %s:%d (%s)\n' % (msg, fname, lineno, fmsg))
2126 2167 self.log(
2127 2168 b'develwarn', b'%s at: %s:%d (%s)\n', msg, fname, lineno, fmsg
2128 2169 )
2129 2170
2130 2171 # avoid cycles
2131 2172 del curframe
2132 2173 del calframe
2133 2174
2134 2175 def deprecwarn(self, msg, version, stacklevel=2):
2135 2176 """issue a deprecation warning
2136 2177
2137 2178 - msg: message explaining what is deprecated and how to upgrade,
2138 2179 - version: last version where the API will be supported,
2139 2180 """
2140 2181 if not (
2141 2182 self.configbool(b'devel', b'all-warnings')
2142 2183 or self.configbool(b'devel', b'deprec-warn')
2143 2184 ):
2144 2185 return
2145 2186 msg += (
2146 2187 b"\n(compatibility will be dropped after Mercurial-%s,"
2147 2188 b" update your code.)"
2148 2189 ) % version
2149 2190 self.develwarn(msg, stacklevel=stacklevel, config=b'deprec-warn')
2150 2191
2151 2192 def exportableenviron(self):
2152 2193 """The environment variables that are safe to export, e.g. through
2153 2194 hgweb.
2154 2195 """
2155 2196 return self._exportableenviron
2156 2197
2157 2198 @contextlib.contextmanager
2158 2199 def configoverride(self, overrides, source=b""):
2159 2200 """Context manager for temporary config overrides
2160 2201 `overrides` must be a dict of the following structure:
2161 2202 {(section, name) : value}"""
2162 2203 backups = {}
2163 2204 try:
2164 2205 for (section, name), value in overrides.items():
2165 2206 backups[(section, name)] = self.backupconfig(section, name)
2166 2207 self.setconfig(section, name, value, source)
2167 2208 yield
2168 2209 finally:
2169 2210 for __, backup in backups.items():
2170 2211 self.restoreconfig(backup)
2171 2212 # just restoring ui.quiet config to the previous value is not enough
2172 2213 # as it does not update ui.quiet class member
2173 2214 if (b'ui', b'quiet') in overrides:
2174 2215 self.fixconfig(section=b'ui')
2175 2216
2176 2217 def estimatememory(self):
2177 2218 """Provide an estimate for the available system memory in Bytes.
2178 2219
2179 2220 This can be overriden via ui.available-memory. It returns None, if
2180 2221 no estimate can be computed.
2181 2222 """
2182 2223 value = self.config(b'ui', b'available-memory')
2183 2224 if value is not None:
2184 2225 try:
2185 2226 return util.sizetoint(value)
2186 2227 except error.ParseError:
2187 2228 raise error.ConfigError(
2188 2229 _(b"ui.available-memory value is invalid ('%s')") % value
2189 2230 )
2190 2231 return util._estimatememory()
2191 2232
2192 2233
2193 2234 # we instantiate one globally shared progress bar to avoid
2194 2235 # competing progress bars when multiple UI objects get created
2195 2236 _progresssingleton = None
2196 2237
2197 2238
2198 2239 def getprogbar(ui):
2199 2240 global _progresssingleton
2200 2241 if _progresssingleton is None:
2201 2242 # passing 'ui' object to the singleton is fishy,
2202 2243 # this is how the extension used to work but feel free to rework it.
2203 2244 _progresssingleton = progress.progbar(ui)
2204 2245 return _progresssingleton
2205 2246
2206 2247
2207 2248 def haveprogbar():
2208 2249 return _progresssingleton is not None
2209 2250
2210 2251
2211 2252 def _selectmsgdests(ui):
2212 2253 name = ui.config(b'ui', b'message-output')
2213 2254 if name == b'channel':
2214 2255 if ui.fmsg:
2215 2256 return ui.fmsg, ui.fmsg
2216 2257 else:
2217 2258 # fall back to ferr if channel isn't ready so that status/error
2218 2259 # messages can be printed
2219 2260 return ui.ferr, ui.ferr
2220 2261 if name == b'stdio':
2221 2262 return ui.fout, ui.ferr
2222 2263 if name == b'stderr':
2223 2264 return ui.ferr, ui.ferr
2224 2265 raise error.Abort(b'invalid ui.message-output destination: %s' % name)
2225 2266
2226 2267
2227 2268 def _writemsgwith(write, dest, *args, **opts):
2228 2269 """Write ui message with the given ui._write*() function
2229 2270
2230 2271 The specified message type is translated to 'ui.<type>' label if the dest
2231 2272 isn't a structured channel, so that the message will be colorized.
2232 2273 """
2233 2274 # TODO: maybe change 'type' to a mandatory option
2234 2275 if 'type' in opts and not getattr(dest, 'structured', False):
2235 2276 opts['label'] = opts.get('label', b'') + b' ui.%s' % opts.pop('type')
2236 2277 write(dest, *args, **opts)
@@ -1,443 +1,443
1 1 Show all commands except debug commands
2 2 $ hg debugcomplete
3 3 abort
4 4 add
5 5 addremove
6 6 annotate
7 7 archive
8 8 backout
9 9 bisect
10 10 bookmarks
11 11 branch
12 12 branches
13 13 bundle
14 14 cat
15 15 clone
16 16 commit
17 17 config
18 18 continue
19 19 copy
20 20 diff
21 21 export
22 22 files
23 23 forget
24 24 graft
25 25 grep
26 26 heads
27 27 help
28 28 identify
29 29 import
30 30 incoming
31 31 init
32 32 locate
33 33 log
34 34 manifest
35 35 merge
36 36 outgoing
37 37 parents
38 38 paths
39 39 phase
40 40 pull
41 41 purge
42 42 push
43 43 recover
44 44 remove
45 45 rename
46 46 resolve
47 47 revert
48 48 rollback
49 49 root
50 50 serve
51 51 shelve
52 52 status
53 53 summary
54 54 tag
55 55 tags
56 56 tip
57 57 unbundle
58 58 unshelve
59 59 update
60 60 verify
61 61 version
62 62
63 63 Show all commands that start with "a"
64 64 $ hg debugcomplete a
65 65 abort
66 66 add
67 67 addremove
68 68 annotate
69 69 archive
70 70
71 71 Do not show debug commands if there are other candidates
72 72 $ hg debugcomplete d
73 73 diff
74 74
75 75 Show debug commands if there are no other candidates
76 76 $ hg debugcomplete debug
77 77 debugancestor
78 78 debugantivirusrunning
79 79 debugapplystreamclonebundle
80 80 debugbackupbundle
81 81 debugbuilddag
82 82 debugbundle
83 83 debugcapabilities
84 84 debugchangedfiles
85 85 debugcheckstate
86 86 debugcolor
87 87 debugcommands
88 88 debugcomplete
89 89 debugconfig
90 90 debugcreatestreamclonebundle
91 91 debugdag
92 92 debugdata
93 93 debugdate
94 94 debugdeltachain
95 95 debugdirstate
96 96 debugdiscovery
97 97 debugdownload
98 98 debugextensions
99 99 debugfileset
100 100 debugformat
101 101 debugfsinfo
102 102 debuggetbundle
103 103 debugignore
104 104 debugindex
105 105 debugindexdot
106 106 debugindexstats
107 107 debuginstall
108 108 debugknown
109 109 debuglabelcomplete
110 110 debuglocks
111 111 debugmanifestfulltextcache
112 112 debugmergestate
113 113 debugnamecomplete
114 114 debugnodemap
115 115 debugobsolete
116 116 debugp1copies
117 117 debugp2copies
118 118 debugpathcomplete
119 119 debugpathcopies
120 120 debugpeer
121 121 debugpickmergetool
122 122 debugpushkey
123 123 debugpvec
124 124 debugrebuilddirstate
125 125 debugrebuildfncache
126 126 debugrename
127 127 debugrequires
128 128 debugrevlog
129 129 debugrevlogindex
130 130 debugrevspec
131 131 debugserve
132 132 debugsetparents
133 133 debugshell
134 134 debugsidedata
135 135 debugssl
136 136 debugstrip
137 137 debugsub
138 138 debugsuccessorssets
139 139 debugtagscache
140 140 debugtemplate
141 141 debuguigetpass
142 142 debuguiprompt
143 143 debugupdatecaches
144 144 debugupgraderepo
145 145 debugwalk
146 146 debugwhyunstable
147 147 debugwireargs
148 148 debugwireproto
149 149
150 150 Do not show the alias of a debug command if there are other candidates
151 151 (this should hide rawcommit)
152 152 $ hg debugcomplete r
153 153 recover
154 154 remove
155 155 rename
156 156 resolve
157 157 revert
158 158 rollback
159 159 root
160 160 Show the alias of a debug command if there are no other candidates
161 161 $ hg debugcomplete rawc
162 162
163 163
164 164 Show the global options
165 165 $ hg debugcomplete --options | sort
166 166 --color
167 167 --config
168 168 --cwd
169 169 --debug
170 170 --debugger
171 171 --encoding
172 172 --encodingmode
173 173 --help
174 174 --hidden
175 175 --noninteractive
176 176 --pager
177 177 --profile
178 178 --quiet
179 179 --repository
180 180 --time
181 181 --traceback
182 182 --verbose
183 183 --version
184 184 -R
185 185 -h
186 186 -q
187 187 -v
188 188 -y
189 189
190 190 Show the options for the "serve" command
191 191 $ hg debugcomplete --options serve | sort
192 192 --accesslog
193 193 --address
194 194 --certificate
195 195 --cmdserver
196 196 --color
197 197 --config
198 198 --cwd
199 199 --daemon
200 200 --daemon-postexec
201 201 --debug
202 202 --debugger
203 203 --encoding
204 204 --encodingmode
205 205 --errorlog
206 206 --help
207 207 --hidden
208 208 --ipv6
209 209 --name
210 210 --noninteractive
211 211 --pager
212 212 --pid-file
213 213 --port
214 214 --prefix
215 215 --print-url
216 216 --profile
217 217 --quiet
218 218 --repository
219 219 --stdio
220 220 --style
221 221 --subrepos
222 222 --templates
223 223 --time
224 224 --traceback
225 225 --verbose
226 226 --version
227 227 --web-conf
228 228 -6
229 229 -A
230 230 -E
231 231 -R
232 232 -S
233 233 -a
234 234 -d
235 235 -h
236 236 -n
237 237 -p
238 238 -q
239 239 -t
240 240 -v
241 241 -y
242 242
243 243 Show an error if we use --options with an ambiguous abbreviation
244 244 $ hg debugcomplete --options s
245 245 hg: command 's' is ambiguous:
246 246 serve shelve showconfig status summary
247 247 [10]
248 248
249 249 Show all commands + options
250 250 $ hg debugcommands
251 251 abort: dry-run
252 252 add: include, exclude, subrepos, dry-run
253 253 addremove: similarity, subrepos, include, exclude, dry-run
254 254 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, skip, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, include, exclude, template
255 255 archive: no-decode, prefix, rev, type, subrepos, include, exclude
256 256 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
257 257 bisect: reset, good, bad, skip, extend, command, noupdate
258 258 bookmarks: force, rev, delete, rename, inactive, list, template
259 259 branch: force, clean, rev
260 260 branches: active, closed, rev, template
261 261 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
262 262 cat: output, rev, decode, include, exclude, template
263 263 clone: noupdate, updaterev, rev, branch, pull, uncompressed, stream, ssh, remotecmd, insecure
264 264 commit: addremove, close-branch, amend, secret, edit, force-close-branch, interactive, include, exclude, message, logfile, date, user, subrepos
265 config: untrusted, edit, local, source, shared, non-shared, global, template
265 config: untrusted, exp-all-known, edit, local, source, shared, non-shared, global, template
266 266 continue: dry-run
267 267 copy: forget, after, at-rev, force, include, exclude, dry-run
268 268 debugancestor:
269 269 debugantivirusrunning:
270 270 debugapplystreamclonebundle:
271 271 debugbackupbundle: recover, patch, git, limit, no-merges, stat, graph, style, template
272 272 debugbuilddag: mergeable-file, overwritten-file, new-file
273 273 debugbundle: all, part-type, spec
274 274 debugcapabilities:
275 275 debugchangedfiles: compute
276 276 debugcheckstate:
277 277 debugcolor: style
278 278 debugcommands:
279 279 debugcomplete: options
280 280 debugcreatestreamclonebundle:
281 281 debugdag: tags, branches, dots, spaces
282 282 debugdata: changelog, manifest, dir
283 283 debugdate: extended
284 284 debugdeltachain: changelog, manifest, dir, template
285 285 debugdirstate: nodates, dates, datesort, dirs
286 286 debugdiscovery: old, nonheads, rev, seed, local-as-revs, remote-as-revs, ssh, remotecmd, insecure, template
287 287 debugdownload: output
288 288 debugextensions: template
289 289 debugfileset: rev, all-files, show-matcher, show-stage
290 290 debugformat: template
291 291 debugfsinfo:
292 292 debuggetbundle: head, common, type
293 293 debugignore:
294 294 debugindex: changelog, manifest, dir, template
295 295 debugindexdot: changelog, manifest, dir
296 296 debugindexstats:
297 297 debuginstall: template
298 298 debugknown:
299 299 debuglabelcomplete:
300 300 debuglocks: force-free-lock, force-free-wlock, set-lock, set-wlock
301 301 debugmanifestfulltextcache: clear, add
302 302 debugmergestate: style, template
303 303 debugnamecomplete:
304 304 debugnodemap: dump-new, dump-disk, check, metadata
305 305 debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template
306 306 debugp1copies: rev
307 307 debugp2copies: rev
308 308 debugpathcomplete: full, normal, added, removed
309 309 debugpathcopies: include, exclude
310 310 debugpeer:
311 311 debugpickmergetool: rev, changedelete, include, exclude, tool
312 312 debugpushkey:
313 313 debugpvec:
314 314 debugrebuilddirstate: rev, minimal
315 315 debugrebuildfncache:
316 316 debugrename: rev
317 317 debugrequires:
318 318 debugrevlog: changelog, manifest, dir, dump
319 319 debugrevlogindex: changelog, manifest, dir, format
320 320 debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized
321 321 debugserve: sshstdio, logiofd, logiofile
322 322 debugsetparents:
323 323 debugshell:
324 324 debugsidedata: changelog, manifest, dir
325 325 debugssl:
326 326 debugstrip: rev, force, no-backup, nobackup, , keep, bookmark, soft
327 327 debugsub: rev
328 328 debugsuccessorssets: closest
329 329 debugtagscache:
330 330 debugtemplate: rev, define
331 331 debuguigetpass: prompt
332 332 debuguiprompt: prompt
333 333 debugupdatecaches:
334 334 debugupgraderepo: optimize, run, backup, changelog, manifest, filelogs
335 335 debugwalk: include, exclude
336 336 debugwhyunstable:
337 337 debugwireargs: three, four, five, ssh, remotecmd, insecure
338 338 debugwireproto: localssh, peer, noreadstderr, nologhandshake, ssh, remotecmd, insecure
339 339 diff: rev, from, to, change, text, git, binary, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, unified, stat, root, include, exclude, subrepos
340 340 export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template
341 341 files: rev, print0, include, exclude, template, subrepos
342 342 forget: interactive, include, exclude, dry-run
343 343 graft: rev, base, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run
344 344 grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude
345 345 heads: rev, topo, active, closed, style, template
346 346 help: extension, command, keyword, system
347 347 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure, template
348 348 import: strip, base, secret, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
349 349 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
350 350 init: ssh, remotecmd, insecure
351 351 locate: rev, print0, fullpath, include, exclude
352 352 log: follow, follow-first, date, copies, keyword, rev, line-range, removed, only-merges, user, only-branch, branch, bookmark, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
353 353 manifest: rev, all, template
354 354 merge: force, rev, preview, abort, tool
355 355 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
356 356 parents: rev, style, template
357 357 paths: template
358 358 phase: public, draft, secret, force, rev
359 359 pull: update, force, confirm, rev, bookmark, branch, ssh, remotecmd, insecure
360 360 purge: abort-on-err, all, ignored, dirs, files, print, print0, confirm, include, exclude
361 361 push: force, rev, bookmark, all-bookmarks, branch, new-branch, pushvars, publish, ssh, remotecmd, insecure
362 362 recover: verify
363 363 remove: after, force, subrepos, include, exclude, dry-run
364 364 rename: forget, after, at-rev, force, include, exclude, dry-run
365 365 resolve: all, list, mark, unmark, no-status, re-merge, tool, include, exclude, template
366 366 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
367 367 rollback: dry-run, force
368 368 root: template
369 369 serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, print-url, subrepos
370 370 shelve: addremove, unknown, cleanup, date, delete, edit, keep, list, message, name, patch, interactive, stat, include, exclude
371 371 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
372 372 summary: remote
373 373 tag: force, local, rev, remove, edit, message, date, user
374 374 tags: template
375 375 tip: patch, git, style, template
376 376 unbundle: update
377 377 unshelve: abort, continue, interactive, keep, name, tool, date
378 378 update: clean, check, merge, date, rev, tool
379 379 verify: full
380 380 version: template
381 381
382 382 $ hg init a
383 383 $ cd a
384 384 $ echo fee > fee
385 385 $ hg ci -q -Amfee
386 386 $ hg tag fee
387 387 $ mkdir fie
388 388 $ echo dead > fie/dead
389 389 $ echo live > fie/live
390 390 $ hg bookmark fo
391 391 $ hg branch -q fie
392 392 $ hg ci -q -Amfie
393 393 $ echo fo > fo
394 394 $ hg branch -qf default
395 395 $ hg ci -q -Amfo
396 396 $ echo Fum > Fum
397 397 $ hg ci -q -AmFum
398 398 $ hg bookmark Fum
399 399
400 400 Test debugpathcomplete
401 401
402 402 $ hg debugpathcomplete f
403 403 fee
404 404 fie
405 405 fo
406 406 $ hg debugpathcomplete -f f
407 407 fee
408 408 fie/dead
409 409 fie/live
410 410 fo
411 411
412 412 $ hg rm Fum
413 413 $ hg debugpathcomplete -r F
414 414 Fum
415 415
416 416 Test debugnamecomplete
417 417
418 418 $ hg debugnamecomplete
419 419 Fum
420 420 default
421 421 fee
422 422 fie
423 423 fo
424 424 tip
425 425 $ hg debugnamecomplete f
426 426 fee
427 427 fie
428 428 fo
429 429
430 430 Test debuglabelcomplete, a deprecated name for debugnamecomplete that is still
431 431 used for completions in some shells.
432 432
433 433 $ hg debuglabelcomplete
434 434 Fum
435 435 default
436 436 fee
437 437 fie
438 438 fo
439 439 tip
440 440 $ hg debuglabelcomplete f
441 441 fee
442 442 fie
443 443 fo
@@ -1,520 +1,546
1 1 Windows needs ';' as a file separator in an environment variable, and MSYS
2 2 doesn't automatically convert it in every case.
3 3
4 4 #if windows
5 5 $ path_list_var() {
6 6 > echo $1 | sed 's/:/;/'
7 7 > }
8 8 #else
9 9 $ path_list_var() {
10 10 > echo $1
11 11 > }
12 12 #endif
13 13
14 14
15 15 hide outer repo
16 16 $ hg init
17 17
18 18 Invalid syntax: no value
19 19
20 20 $ cat > .hg/hgrc << EOF
21 21 > novaluekey
22 22 > EOF
23 23 $ hg showconfig
24 24 config error at $TESTTMP/.hg/hgrc:1: novaluekey
25 25 [30]
26 26
27 27 Invalid syntax: no key
28 28
29 29 $ cat > .hg/hgrc << EOF
30 30 > =nokeyvalue
31 31 > EOF
32 32 $ hg showconfig
33 33 config error at $TESTTMP/.hg/hgrc:1: =nokeyvalue
34 34 [30]
35 35
36 36 Test hint about invalid syntax from leading white space
37 37
38 38 $ cat > .hg/hgrc << EOF
39 39 > key=value
40 40 > EOF
41 41 $ hg showconfig
42 42 config error at $TESTTMP/.hg/hgrc:1: unexpected leading whitespace: key=value
43 43 [30]
44 44
45 45 $ cat > .hg/hgrc << EOF
46 46 > [section]
47 47 > key=value
48 48 > EOF
49 49 $ hg showconfig
50 50 config error at $TESTTMP/.hg/hgrc:1: unexpected leading whitespace: [section]
51 51 [30]
52 52
53 53 Reset hgrc
54 54
55 55 $ echo > .hg/hgrc
56 56
57 57 Test case sensitive configuration
58 58
59 59 $ cat <<EOF >> $HGRCPATH
60 60 > [Section]
61 61 > KeY = Case Sensitive
62 62 > key = lower case
63 63 > EOF
64 64
65 65 $ hg showconfig Section
66 66 Section.KeY=Case Sensitive
67 67 Section.key=lower case
68 68
69 69 $ hg showconfig Section -Tjson
70 70 [
71 71 {
72 72 "defaultvalue": null,
73 73 "name": "Section.KeY",
74 74 "source": "*.hgrc:*", (glob)
75 75 "value": "Case Sensitive"
76 76 },
77 77 {
78 78 "defaultvalue": null,
79 79 "name": "Section.key",
80 80 "source": "*.hgrc:*", (glob)
81 81 "value": "lower case"
82 82 }
83 83 ]
84 84 $ hg showconfig Section.KeY -Tjson
85 85 [
86 86 {
87 87 "defaultvalue": null,
88 88 "name": "Section.KeY",
89 89 "source": "*.hgrc:*", (glob)
90 90 "value": "Case Sensitive"
91 91 }
92 92 ]
93 93 $ hg showconfig -Tjson | tail -7
94 94 {
95 95 "defaultvalue": null,
96 96 "name": "*", (glob)
97 97 "source": "*", (glob)
98 98 "value": "*" (glob)
99 99 }
100 100 ]
101 101
102 102 Test config default of various types:
103 103
104 104 {"defaultvalue": ""} for -T'json(defaultvalue)' looks weird, but that's
105 105 how the templater works. Unknown keywords are evaluated to "".
106 106
107 107 dynamicdefault
108 108
109 109 $ hg config --config alias.foo= alias -Tjson
110 110 [
111 111 {
112 112 "name": "alias.foo",
113 113 "source": "--config",
114 114 "value": ""
115 115 }
116 116 ]
117 117 $ hg config --config alias.foo= alias -T'json(defaultvalue)'
118 118 [
119 119 {"defaultvalue": ""}
120 120 ]
121 121 $ hg config --config alias.foo= alias -T'{defaultvalue}\n'
122 122
123 123
124 124 null
125 125
126 126 $ hg config --config auth.cookiefile= auth -Tjson
127 127 [
128 128 {
129 129 "defaultvalue": null,
130 130 "name": "auth.cookiefile",
131 131 "source": "--config",
132 132 "value": ""
133 133 }
134 134 ]
135 135 $ hg config --config auth.cookiefile= auth -T'json(defaultvalue)'
136 136 [
137 137 {"defaultvalue": null}
138 138 ]
139 139 $ hg config --config auth.cookiefile= auth -T'{defaultvalue}\n'
140 140
141 141
142 142 false
143 143
144 144 $ hg config --config commands.commit.post-status= commands -Tjson
145 145 [
146 146 {
147 147 "defaultvalue": false,
148 148 "name": "commands.commit.post-status",
149 149 "source": "--config",
150 150 "value": ""
151 151 }
152 152 ]
153 153 $ hg config --config commands.commit.post-status= commands -T'json(defaultvalue)'
154 154 [
155 155 {"defaultvalue": false}
156 156 ]
157 157 $ hg config --config commands.commit.post-status= commands -T'{defaultvalue}\n'
158 158 False
159 159
160 160 true
161 161
162 162 $ hg config --config format.dotencode= format.dotencode -Tjson
163 163 [
164 164 {
165 165 "defaultvalue": true,
166 166 "name": "format.dotencode",
167 167 "source": "--config",
168 168 "value": ""
169 169 }
170 170 ]
171 171 $ hg config --config format.dotencode= format.dotencode -T'json(defaultvalue)'
172 172 [
173 173 {"defaultvalue": true}
174 174 ]
175 175 $ hg config --config format.dotencode= format.dotencode -T'{defaultvalue}\n'
176 176 True
177 177
178 178 bytes
179 179
180 180 $ hg config --config commands.resolve.mark-check= commands -Tjson
181 181 [
182 182 {
183 183 "defaultvalue": "none",
184 184 "name": "commands.resolve.mark-check",
185 185 "source": "--config",
186 186 "value": ""
187 187 }
188 188 ]
189 189 $ hg config --config commands.resolve.mark-check= commands -T'json(defaultvalue)'
190 190 [
191 191 {"defaultvalue": "none"}
192 192 ]
193 193 $ hg config --config commands.resolve.mark-check= commands -T'{defaultvalue}\n'
194 194 none
195 195
196 196 empty list
197 197
198 198 $ hg config --config commands.show.aliasprefix= commands -Tjson
199 199 [
200 200 {
201 201 "defaultvalue": [],
202 202 "name": "commands.show.aliasprefix",
203 203 "source": "--config",
204 204 "value": ""
205 205 }
206 206 ]
207 207 $ hg config --config commands.show.aliasprefix= commands -T'json(defaultvalue)'
208 208 [
209 209 {"defaultvalue": []}
210 210 ]
211 211 $ hg config --config commands.show.aliasprefix= commands -T'{defaultvalue}\n'
212 212
213 213
214 214 nonempty list
215 215
216 216 $ hg config --config progress.format= progress -Tjson
217 217 [
218 218 {
219 219 "defaultvalue": ["topic", "bar", "number", "estimate"],
220 220 "name": "progress.format",
221 221 "source": "--config",
222 222 "value": ""
223 223 }
224 224 ]
225 225 $ hg config --config progress.format= progress -T'json(defaultvalue)'
226 226 [
227 227 {"defaultvalue": ["topic", "bar", "number", "estimate"]}
228 228 ]
229 229 $ hg config --config progress.format= progress -T'{defaultvalue}\n'
230 230 topic bar number estimate
231 231
232 232 int
233 233
234 234 $ hg config --config profiling.freq= profiling -Tjson
235 235 [
236 236 {
237 237 "defaultvalue": 1000,
238 238 "name": "profiling.freq",
239 239 "source": "--config",
240 240 "value": ""
241 241 }
242 242 ]
243 243 $ hg config --config profiling.freq= profiling -T'json(defaultvalue)'
244 244 [
245 245 {"defaultvalue": 1000}
246 246 ]
247 247 $ hg config --config profiling.freq= profiling -T'{defaultvalue}\n'
248 248 1000
249 249
250 250 float
251 251
252 252 $ hg config --config profiling.showmax= profiling -Tjson
253 253 [
254 254 {
255 255 "defaultvalue": 0.999,
256 256 "name": "profiling.showmax",
257 257 "source": "--config",
258 258 "value": ""
259 259 }
260 260 ]
261 261 $ hg config --config profiling.showmax= profiling -T'json(defaultvalue)'
262 262 [
263 263 {"defaultvalue": 0.999}
264 264 ]
265 265 $ hg config --config profiling.showmax= profiling -T'{defaultvalue}\n'
266 266 0.999
267 267
268 268 Test empty config source:
269 269
270 270 $ cat <<EOF > emptysource.py
271 271 > def reposetup(ui, repo):
272 272 > ui.setconfig(b'empty', b'source', b'value')
273 273 > EOF
274 274 $ cp .hg/hgrc .hg/hgrc.orig
275 275 $ cat <<EOF >> .hg/hgrc
276 276 > [extensions]
277 277 > emptysource = `pwd`/emptysource.py
278 278 > EOF
279 279
280 280 $ hg config --source empty.source
281 281 none: value
282 282 $ hg config empty.source -Tjson
283 283 [
284 284 {
285 285 "defaultvalue": null,
286 286 "name": "empty.source",
287 287 "source": "",
288 288 "value": "value"
289 289 }
290 290 ]
291 291
292 292 $ cp .hg/hgrc.orig .hg/hgrc
293 293
294 294 Test "%unset"
295 295
296 296 $ cat >> $HGRCPATH <<EOF
297 297 > [unsettest]
298 298 > local-hgrcpath = should be unset (HGRCPATH)
299 299 > %unset local-hgrcpath
300 300 >
301 301 > global = should be unset (HGRCPATH)
302 302 >
303 303 > both = should be unset (HGRCPATH)
304 304 >
305 305 > set-after-unset = should be unset (HGRCPATH)
306 306 > EOF
307 307
308 308 $ cat >> .hg/hgrc <<EOF
309 309 > [unsettest]
310 310 > local-hgrc = should be unset (.hg/hgrc)
311 311 > %unset local-hgrc
312 312 >
313 313 > %unset global
314 314 >
315 315 > both = should be unset (.hg/hgrc)
316 316 > %unset both
317 317 >
318 318 > set-after-unset = should be unset (.hg/hgrc)
319 319 > %unset set-after-unset
320 320 > set-after-unset = should be set (.hg/hgrc)
321 321 > EOF
322 322
323 323 $ hg showconfig unsettest
324 324 unsettest.set-after-unset=should be set (.hg/hgrc)
325 325
326 326 Test exit code when no config matches
327 327
328 328 $ hg config Section.idontexist
329 329 [1]
330 330
331 331 sub-options in [paths] aren't expanded
332 332
333 333 $ cat > .hg/hgrc << EOF
334 334 > [paths]
335 335 > foo = ~/foo
336 336 > foo:suboption = ~/foo
337 337 > EOF
338 338
339 339 $ hg showconfig paths
340 340 paths.foo=~/foo
341 341 paths.foo:suboption=~/foo
342 342
343 343 note: The path expansion no longer happens at the config level, but the path is
344 344 still expanded:
345 345
346 346 $ hg path | grep foo
347 347 foo = $TESTTMP/foo
348 348
349 349 edit failure
350 350
351 351 $ HGEDITOR=false hg config --edit
352 352 abort: edit failed: false exited with status 1
353 353 [10]
354 354
355 355 config affected by environment variables
356 356
357 357 $ EDITOR=e1 VISUAL=e2 hg config --source | grep 'ui\.editor'
358 358 $VISUAL: ui.editor=e2
359 359
360 360 $ VISUAL=e2 hg config --source --config ui.editor=e3 | grep 'ui\.editor'
361 361 --config: ui.editor=e3
362 362
363 363 $ PAGER=p1 hg config --source | grep 'pager\.pager'
364 364 $PAGER: pager.pager=p1
365 365
366 366 $ PAGER=p1 hg config --source --config pager.pager=p2 | grep 'pager\.pager'
367 367 --config: pager.pager=p2
368 368
369 369 verify that aliases are evaluated as well
370 370
371 371 $ hg init aliastest
372 372 $ cd aliastest
373 373 $ cat > .hg/hgrc << EOF
374 374 > [ui]
375 375 > user = repo user
376 376 > EOF
377 377 $ touch index
378 378 $ unset HGUSER
379 379 $ hg ci -Am test
380 380 adding index
381 381 $ hg log --template '{author}\n'
382 382 repo user
383 383 $ cd ..
384 384
385 385 alias has lower priority
386 386
387 387 $ hg init aliaspriority
388 388 $ cd aliaspriority
389 389 $ cat > .hg/hgrc << EOF
390 390 > [ui]
391 391 > user = alias user
392 392 > username = repo user
393 393 > EOF
394 394 $ touch index
395 395 $ unset HGUSER
396 396 $ hg ci -Am test
397 397 adding index
398 398 $ hg log --template '{author}\n'
399 399 repo user
400 400 $ cd ..
401 401
402 402 configs should be read in lexicographical order
403 403
404 404 $ mkdir configs
405 405 $ for i in `$TESTDIR/seq.py 10 99`; do
406 406 > printf "[section]\nkey=$i" > configs/$i.rc
407 407 > done
408 408 $ HGRCPATH=configs hg config section.key
409 409 99
410 410
411 Listing all config options
412 ==========================
413
414 The feature is experimental and behavior may varies. This test exists to make sure the code is run. We grep it to avoid too much variability in its current experimental state.
415
416 $ hg config --exp-all-known | grep commit
417 commands.commit.interactive.git=False
418 commands.commit.interactive.ignoreblanklines=False
419 commands.commit.interactive.ignorews=False
420 commands.commit.interactive.ignorewsamount=False
421 commands.commit.interactive.ignorewseol=False
422 commands.commit.interactive.nobinary=False
423 commands.commit.interactive.nodates=False
424 commands.commit.interactive.noprefix=False
425 commands.commit.interactive.showfunc=False
426 commands.commit.interactive.unified=None
427 commands.commit.interactive.word-diff=False
428 commands.commit.post-status=False
429 convert.git.committeractions=[*'messagedifferent'] (glob)
430 convert.svn.dangerous-set-commit-dates=False
431 experimental.copytrace.sourcecommitlimit=100
432 phases.new-commit=draft
433 ui.allowemptycommit=False
434 ui.commitsubrepos=False
435
436
411 437 Configuration priority
412 438 ======================
413 439
414 440 setup necessary file
415 441
416 442 $ cat > file-A.rc << EOF
417 443 > [config-test]
418 444 > basic = value-A
419 445 > pre-include= value-A
420 446 > %include ./included.rc
421 447 > post-include= value-A
422 448 > [command-templates]
423 449 > log = "value-A\n"
424 450 > EOF
425 451
426 452 $ cat > file-B.rc << EOF
427 453 > [config-test]
428 454 > basic = value-B
429 455 > [ui]
430 456 > logtemplate = "value-B\n"
431 457 > EOF
432 458
433 459
434 460 $ cat > included.rc << EOF
435 461 > [config-test]
436 462 > pre-include= value-included
437 463 > post-include= value-included
438 464 > EOF
439 465
440 466 $ cat > file-C.rc << EOF
441 467 > %include ./included-alias-C.rc
442 468 > [ui]
443 469 > logtemplate = "value-C\n"
444 470 > EOF
445 471
446 472 $ cat > included-alias-C.rc << EOF
447 473 > [command-templates]
448 474 > log = "value-included\n"
449 475 > EOF
450 476
451 477
452 478 $ cat > file-D.rc << EOF
453 479 > [command-templates]
454 480 > log = "value-D\n"
455 481 > %include ./included-alias-D.rc
456 482 > EOF
457 483
458 484 $ cat > included-alias-D.rc << EOF
459 485 > [ui]
460 486 > logtemplate = "value-included\n"
461 487 > EOF
462 488
463 489 Simple order checking
464 490 ---------------------
465 491
466 492 If file B is read after file A, value from B overwrite value from A.
467 493
468 494 $ HGRCPATH=`path_list_var "file-A.rc:file-B.rc"` hg config config-test.basic
469 495 value-B
470 496
471 497 Ordering from include
472 498 ---------------------
473 499
474 500 value from an include overwrite value defined before the include, but not the one defined after the include
475 501
476 502 $ HGRCPATH="file-A.rc" hg config config-test.pre-include
477 503 value-included
478 504 $ HGRCPATH="file-A.rc" hg config config-test.post-include
479 505 value-A
480 506
481 507 command line override
482 508 ---------------------
483 509
484 510 $ HGRCPATH=`path_list_var "file-A.rc:file-B.rc"` hg config config-test.basic --config config-test.basic=value-CLI
485 511 value-CLI
486 512
487 513 Alias ordering
488 514 --------------
489 515
490 516 The official config is now `command-templates.log`, the historical
491 517 `ui.logtemplate` is a valid alternative for it.
492 518
493 519 When both are defined, The config value read the last "win", this should keep
494 520 being true if the config have other alias. In other word, the config value read
495 521 earlier will be considered "lower level" and the config read later would be
496 522 considered "higher level". And higher level values wins.
497 523
498 524 $ HGRCPATH="file-A.rc" hg log -r .
499 525 value-A
500 526 $ HGRCPATH="file-B.rc" hg log -r .
501 527 value-B
502 528 $ HGRCPATH=`path_list_var "file-A.rc:file-B.rc"` hg log -r .
503 529 value-B
504 530
505 531 Alias and include
506 532 -----------------
507 533
508 534 The pre/post include priority should also apply when tie-breaking alternatives.
509 535 See the case above for details about the two config options used.
510 536
511 537 $ HGRCPATH="file-C.rc" hg log -r .
512 538 value-C
513 539 $ HGRCPATH="file-D.rc" hg log -r .
514 540 value-included
515 541
516 542 command line override
517 543 ---------------------
518 544
519 545 $ HGRCPATH=`path_list_var "file-A.rc:file-B.rc"` hg log -r . --config ui.logtemplate="value-CLI\n"
520 546 value-CLI
General Comments 0
You need to be logged in to leave comments. Login now