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