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