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