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