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