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