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