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