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