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