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