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