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