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