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