##// END OF EJS Templates
registrar: replace "cmdtype" with an intent-based mechanism (API)...
Gregory Szorc -
r37734:dfc51a48 default
parent child Browse files
Show More
@@ -1,5652 +1,5667 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import difflib
11 11 import errno
12 12 import os
13 13 import re
14 14 import sys
15 15
16 16 from .i18n import _
17 17 from .node import (
18 18 hex,
19 19 nullid,
20 20 nullrev,
21 21 short,
22 22 )
23 23 from . import (
24 24 archival,
25 25 bookmarks,
26 26 bundle2,
27 27 changegroup,
28 28 cmdutil,
29 29 copies,
30 30 debugcommands as debugcommandsmod,
31 31 destutil,
32 32 dirstateguard,
33 33 discovery,
34 34 encoding,
35 35 error,
36 36 exchange,
37 37 extensions,
38 38 formatter,
39 39 graphmod,
40 40 hbisect,
41 41 help,
42 42 hg,
43 43 lock as lockmod,
44 44 logcmdutil,
45 45 merge as mergemod,
46 46 obsolete,
47 47 obsutil,
48 48 patch,
49 49 phases,
50 50 pycompat,
51 51 rcutil,
52 52 registrar,
53 53 revsetlang,
54 54 rewriteutil,
55 55 scmutil,
56 56 server,
57 57 streamclone,
58 58 tags as tagsmod,
59 59 templatekw,
60 60 ui as uimod,
61 61 util,
62 62 wireprotoserver,
63 63 )
64 64 from .utils import (
65 65 dateutil,
66 66 procutil,
67 67 stringutil,
68 68 )
69 69
70 70 release = lockmod.release
71 71
72 72 table = {}
73 73 table.update(debugcommandsmod.command._table)
74 74
75 75 command = registrar.command(table)
76 readonly = registrar.command.readonly
76 INTENT_READONLY = registrar.INTENT_READONLY
77 77
78 78 # common command options
79 79
80 80 globalopts = [
81 81 ('R', 'repository', '',
82 82 _('repository root directory or name of overlay bundle file'),
83 83 _('REPO')),
84 84 ('', 'cwd', '',
85 85 _('change working directory'), _('DIR')),
86 86 ('y', 'noninteractive', None,
87 87 _('do not prompt, automatically pick the first choice for all prompts')),
88 88 ('q', 'quiet', None, _('suppress output')),
89 89 ('v', 'verbose', None, _('enable additional output')),
90 90 ('', 'color', '',
91 91 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
92 92 # and should not be translated
93 93 _("when to colorize (boolean, always, auto, never, or debug)"),
94 94 _('TYPE')),
95 95 ('', 'config', [],
96 96 _('set/override config option (use \'section.name=value\')'),
97 97 _('CONFIG')),
98 98 ('', 'debug', None, _('enable debugging output')),
99 99 ('', 'debugger', None, _('start debugger')),
100 100 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
101 101 _('ENCODE')),
102 102 ('', 'encodingmode', encoding.encodingmode,
103 103 _('set the charset encoding mode'), _('MODE')),
104 104 ('', 'traceback', None, _('always print a traceback on exception')),
105 105 ('', 'time', None, _('time how long the command takes')),
106 106 ('', 'profile', None, _('print command execution profile')),
107 107 ('', 'version', None, _('output version information and exit')),
108 108 ('h', 'help', None, _('display help and exit')),
109 109 ('', 'hidden', False, _('consider hidden changesets')),
110 110 ('', 'pager', 'auto',
111 111 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
112 112 ]
113 113
114 114 dryrunopts = cmdutil.dryrunopts
115 115 remoteopts = cmdutil.remoteopts
116 116 walkopts = cmdutil.walkopts
117 117 commitopts = cmdutil.commitopts
118 118 commitopts2 = cmdutil.commitopts2
119 119 formatteropts = cmdutil.formatteropts
120 120 templateopts = cmdutil.templateopts
121 121 logopts = cmdutil.logopts
122 122 diffopts = cmdutil.diffopts
123 123 diffwsopts = cmdutil.diffwsopts
124 124 diffopts2 = cmdutil.diffopts2
125 125 mergetoolopts = cmdutil.mergetoolopts
126 126 similarityopts = cmdutil.similarityopts
127 127 subrepoopts = cmdutil.subrepoopts
128 128 debugrevlogopts = cmdutil.debugrevlogopts
129 129
130 130 # Commands start here, listed alphabetically
131 131
132 132 @command('^add',
133 133 walkopts + subrepoopts + dryrunopts,
134 134 _('[OPTION]... [FILE]...'),
135 135 inferrepo=True)
136 136 def add(ui, repo, *pats, **opts):
137 137 """add the specified files on the next commit
138 138
139 139 Schedule files to be version controlled and added to the
140 140 repository.
141 141
142 142 The files will be added to the repository at the next commit. To
143 143 undo an add before that, see :hg:`forget`.
144 144
145 145 If no names are given, add all files to the repository (except
146 146 files matching ``.hgignore``).
147 147
148 148 .. container:: verbose
149 149
150 150 Examples:
151 151
152 152 - New (unknown) files are added
153 153 automatically by :hg:`add`::
154 154
155 155 $ ls
156 156 foo.c
157 157 $ hg status
158 158 ? foo.c
159 159 $ hg add
160 160 adding foo.c
161 161 $ hg status
162 162 A foo.c
163 163
164 164 - Specific files to be added can be specified::
165 165
166 166 $ ls
167 167 bar.c foo.c
168 168 $ hg status
169 169 ? bar.c
170 170 ? foo.c
171 171 $ hg add bar.c
172 172 $ hg status
173 173 A bar.c
174 174 ? foo.c
175 175
176 176 Returns 0 if all files are successfully added.
177 177 """
178 178
179 179 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
180 180 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
181 181 return rejected and 1 or 0
182 182
183 183 @command('addremove',
184 184 similarityopts + subrepoopts + walkopts + dryrunopts,
185 185 _('[OPTION]... [FILE]...'),
186 186 inferrepo=True)
187 187 def addremove(ui, repo, *pats, **opts):
188 188 """add all new files, delete all missing files
189 189
190 190 Add all new files and remove all missing files from the
191 191 repository.
192 192
193 193 Unless names are given, new files are ignored if they match any of
194 194 the patterns in ``.hgignore``. As with add, these changes take
195 195 effect at the next commit.
196 196
197 197 Use the -s/--similarity option to detect renamed files. This
198 198 option takes a percentage between 0 (disabled) and 100 (files must
199 199 be identical) as its parameter. With a parameter greater than 0,
200 200 this compares every removed file with every added file and records
201 201 those similar enough as renames. Detecting renamed files this way
202 202 can be expensive. After using this option, :hg:`status -C` can be
203 203 used to check which files were identified as moved or renamed. If
204 204 not specified, -s/--similarity defaults to 100 and only renames of
205 205 identical files are detected.
206 206
207 207 .. container:: verbose
208 208
209 209 Examples:
210 210
211 211 - A number of files (bar.c and foo.c) are new,
212 212 while foobar.c has been removed (without using :hg:`remove`)
213 213 from the repository::
214 214
215 215 $ ls
216 216 bar.c foo.c
217 217 $ hg status
218 218 ! foobar.c
219 219 ? bar.c
220 220 ? foo.c
221 221 $ hg addremove
222 222 adding bar.c
223 223 adding foo.c
224 224 removing foobar.c
225 225 $ hg status
226 226 A bar.c
227 227 A foo.c
228 228 R foobar.c
229 229
230 230 - A file foobar.c was moved to foo.c without using :hg:`rename`.
231 231 Afterwards, it was edited slightly::
232 232
233 233 $ ls
234 234 foo.c
235 235 $ hg status
236 236 ! foobar.c
237 237 ? foo.c
238 238 $ hg addremove --similarity 90
239 239 removing foobar.c
240 240 adding foo.c
241 241 recording removal of foobar.c as rename to foo.c (94% similar)
242 242 $ hg status -C
243 243 A foo.c
244 244 foobar.c
245 245 R foobar.c
246 246
247 247 Returns 0 if all files are successfully added.
248 248 """
249 249 opts = pycompat.byteskwargs(opts)
250 250 if not opts.get('similarity'):
251 251 opts['similarity'] = '100'
252 252 matcher = scmutil.match(repo[None], pats, opts)
253 253 return scmutil.addremove(repo, matcher, "", opts)
254 254
255 255 @command('^annotate|blame',
256 256 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
257 257 ('', 'follow', None,
258 258 _('follow copies/renames and list the filename (DEPRECATED)')),
259 259 ('', 'no-follow', None, _("don't follow copies and renames")),
260 260 ('a', 'text', None, _('treat all files as text')),
261 261 ('u', 'user', None, _('list the author (long with -v)')),
262 262 ('f', 'file', None, _('list the filename')),
263 263 ('d', 'date', None, _('list the date (short with -q)')),
264 264 ('n', 'number', None, _('list the revision number (default)')),
265 265 ('c', 'changeset', None, _('list the changeset')),
266 266 ('l', 'line-number', None, _('show line number at the first appearance')),
267 267 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
268 268 ] + diffwsopts + walkopts + formatteropts,
269 269 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
270 270 inferrepo=True)
271 271 def annotate(ui, repo, *pats, **opts):
272 272 """show changeset information by line for each file
273 273
274 274 List changes in files, showing the revision id responsible for
275 275 each line.
276 276
277 277 This command is useful for discovering when a change was made and
278 278 by whom.
279 279
280 280 If you include --file, --user, or --date, the revision number is
281 281 suppressed unless you also include --number.
282 282
283 283 Without the -a/--text option, annotate will avoid processing files
284 284 it detects as binary. With -a, annotate will annotate the file
285 285 anyway, although the results will probably be neither useful
286 286 nor desirable.
287 287
288 288 Returns 0 on success.
289 289 """
290 290 opts = pycompat.byteskwargs(opts)
291 291 if not pats:
292 292 raise error.Abort(_('at least one filename or pattern is required'))
293 293
294 294 if opts.get('follow'):
295 295 # --follow is deprecated and now just an alias for -f/--file
296 296 # to mimic the behavior of Mercurial before version 1.5
297 297 opts['file'] = True
298 298
299 299 rev = opts.get('rev')
300 300 if rev:
301 301 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
302 302 ctx = scmutil.revsingle(repo, rev)
303 303
304 304 rootfm = ui.formatter('annotate', opts)
305 305 if ui.quiet:
306 306 datefunc = dateutil.shortdate
307 307 else:
308 308 datefunc = dateutil.datestr
309 309 if ctx.rev() is None:
310 310 def hexfn(node):
311 311 if node is None:
312 312 return None
313 313 else:
314 314 return rootfm.hexfunc(node)
315 315 if opts.get('changeset'):
316 316 # omit "+" suffix which is appended to node hex
317 317 def formatrev(rev):
318 318 if rev is None:
319 319 return '%d' % ctx.p1().rev()
320 320 else:
321 321 return '%d' % rev
322 322 else:
323 323 def formatrev(rev):
324 324 if rev is None:
325 325 return '%d+' % ctx.p1().rev()
326 326 else:
327 327 return '%d ' % rev
328 328 def formathex(hex):
329 329 if hex is None:
330 330 return '%s+' % rootfm.hexfunc(ctx.p1().node())
331 331 else:
332 332 return '%s ' % hex
333 333 else:
334 334 hexfn = rootfm.hexfunc
335 335 formatrev = formathex = pycompat.bytestr
336 336
337 337 opmap = [('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
338 338 ('number', ' ', lambda x: x.fctx.rev(), formatrev),
339 339 ('changeset', ' ', lambda x: hexfn(x.fctx.node()), formathex),
340 340 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
341 341 ('file', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
342 342 ('line_number', ':', lambda x: x.lineno, pycompat.bytestr),
343 343 ]
344 344 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
345 345
346 346 if (not opts.get('user') and not opts.get('changeset')
347 347 and not opts.get('date') and not opts.get('file')):
348 348 opts['number'] = True
349 349
350 350 linenumber = opts.get('line_number') is not None
351 351 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
352 352 raise error.Abort(_('at least one of -n/-c is required for -l'))
353 353
354 354 ui.pager('annotate')
355 355
356 356 if rootfm.isplain():
357 357 def makefunc(get, fmt):
358 358 return lambda x: fmt(get(x))
359 359 else:
360 360 def makefunc(get, fmt):
361 361 return get
362 362 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
363 363 if opts.get(op)]
364 364 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
365 365 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
366 366 if opts.get(op))
367 367
368 368 def bad(x, y):
369 369 raise error.Abort("%s: %s" % (x, y))
370 370
371 371 m = scmutil.match(ctx, pats, opts, badfn=bad)
372 372
373 373 follow = not opts.get('no_follow')
374 374 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
375 375 whitespace=True)
376 376 skiprevs = opts.get('skip')
377 377 if skiprevs:
378 378 skiprevs = scmutil.revrange(repo, skiprevs)
379 379
380 380 for abs in ctx.walk(m):
381 381 fctx = ctx[abs]
382 382 rootfm.startitem()
383 383 rootfm.data(abspath=abs, path=m.rel(abs))
384 384 if not opts.get('text') and fctx.isbinary():
385 385 rootfm.plain(_("%s: binary file\n")
386 386 % ((pats and m.rel(abs)) or abs))
387 387 continue
388 388
389 389 fm = rootfm.nested('lines', tmpl='{rev}: {line}')
390 390 lines = fctx.annotate(follow=follow, skiprevs=skiprevs,
391 391 diffopts=diffopts)
392 392 if not lines:
393 393 fm.end()
394 394 continue
395 395 formats = []
396 396 pieces = []
397 397
398 398 for f, sep in funcmap:
399 399 l = [f(n) for n in lines]
400 400 if fm.isplain():
401 401 sizes = [encoding.colwidth(x) for x in l]
402 402 ml = max(sizes)
403 403 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
404 404 else:
405 405 formats.append(['%s' for x in l])
406 406 pieces.append(l)
407 407
408 408 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
409 409 fm.startitem()
410 410 fm.context(fctx=n.fctx)
411 411 fm.write(fields, "".join(f), *p)
412 412 if n.skip:
413 413 fmt = "* %s"
414 414 else:
415 415 fmt = ": %s"
416 416 fm.write('line', fmt, n.text)
417 417
418 418 if not lines[-1].text.endswith('\n'):
419 419 fm.plain('\n')
420 420 fm.end()
421 421
422 422 rootfm.end()
423 423
424 424 @command('archive',
425 425 [('', 'no-decode', None, _('do not pass files through decoders')),
426 426 ('p', 'prefix', '', _('directory prefix for files in archive'),
427 427 _('PREFIX')),
428 428 ('r', 'rev', '', _('revision to distribute'), _('REV')),
429 429 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
430 430 ] + subrepoopts + walkopts,
431 431 _('[OPTION]... DEST'))
432 432 def archive(ui, repo, dest, **opts):
433 433 '''create an unversioned archive of a repository revision
434 434
435 435 By default, the revision used is the parent of the working
436 436 directory; use -r/--rev to specify a different revision.
437 437
438 438 The archive type is automatically detected based on file
439 439 extension (to override, use -t/--type).
440 440
441 441 .. container:: verbose
442 442
443 443 Examples:
444 444
445 445 - create a zip file containing the 1.0 release::
446 446
447 447 hg archive -r 1.0 project-1.0.zip
448 448
449 449 - create a tarball excluding .hg files::
450 450
451 451 hg archive project.tar.gz -X ".hg*"
452 452
453 453 Valid types are:
454 454
455 455 :``files``: a directory full of files (default)
456 456 :``tar``: tar archive, uncompressed
457 457 :``tbz2``: tar archive, compressed using bzip2
458 458 :``tgz``: tar archive, compressed using gzip
459 459 :``uzip``: zip archive, uncompressed
460 460 :``zip``: zip archive, compressed using deflate
461 461
462 462 The exact name of the destination archive or directory is given
463 463 using a format string; see :hg:`help export` for details.
464 464
465 465 Each member added to an archive file has a directory prefix
466 466 prepended. Use -p/--prefix to specify a format string for the
467 467 prefix. The default is the basename of the archive, with suffixes
468 468 removed.
469 469
470 470 Returns 0 on success.
471 471 '''
472 472
473 473 opts = pycompat.byteskwargs(opts)
474 474 rev = opts.get('rev')
475 475 if rev:
476 476 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
477 477 ctx = scmutil.revsingle(repo, rev)
478 478 if not ctx:
479 479 raise error.Abort(_('no working directory: please specify a revision'))
480 480 node = ctx.node()
481 481 dest = cmdutil.makefilename(ctx, dest)
482 482 if os.path.realpath(dest) == repo.root:
483 483 raise error.Abort(_('repository root cannot be destination'))
484 484
485 485 kind = opts.get('type') or archival.guesskind(dest) or 'files'
486 486 prefix = opts.get('prefix')
487 487
488 488 if dest == '-':
489 489 if kind == 'files':
490 490 raise error.Abort(_('cannot archive plain files to stdout'))
491 491 dest = cmdutil.makefileobj(ctx, dest)
492 492 if not prefix:
493 493 prefix = os.path.basename(repo.root) + '-%h'
494 494
495 495 prefix = cmdutil.makefilename(ctx, prefix)
496 496 match = scmutil.match(ctx, [], opts)
497 497 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
498 498 match, prefix, subrepos=opts.get('subrepos'))
499 499
500 500 @command('backout',
501 501 [('', 'merge', None, _('merge with old dirstate parent after backout')),
502 502 ('', 'commit', None,
503 503 _('commit if no conflicts were encountered (DEPRECATED)')),
504 504 ('', 'no-commit', None, _('do not commit')),
505 505 ('', 'parent', '',
506 506 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
507 507 ('r', 'rev', '', _('revision to backout'), _('REV')),
508 508 ('e', 'edit', False, _('invoke editor on commit messages')),
509 509 ] + mergetoolopts + walkopts + commitopts + commitopts2,
510 510 _('[OPTION]... [-r] REV'))
511 511 def backout(ui, repo, node=None, rev=None, **opts):
512 512 '''reverse effect of earlier changeset
513 513
514 514 Prepare a new changeset with the effect of REV undone in the
515 515 current working directory. If no conflicts were encountered,
516 516 it will be committed immediately.
517 517
518 518 If REV is the parent of the working directory, then this new changeset
519 519 is committed automatically (unless --no-commit is specified).
520 520
521 521 .. note::
522 522
523 523 :hg:`backout` cannot be used to fix either an unwanted or
524 524 incorrect merge.
525 525
526 526 .. container:: verbose
527 527
528 528 Examples:
529 529
530 530 - Reverse the effect of the parent of the working directory.
531 531 This backout will be committed immediately::
532 532
533 533 hg backout -r .
534 534
535 535 - Reverse the effect of previous bad revision 23::
536 536
537 537 hg backout -r 23
538 538
539 539 - Reverse the effect of previous bad revision 23 and
540 540 leave changes uncommitted::
541 541
542 542 hg backout -r 23 --no-commit
543 543 hg commit -m "Backout revision 23"
544 544
545 545 By default, the pending changeset will have one parent,
546 546 maintaining a linear history. With --merge, the pending
547 547 changeset will instead have two parents: the old parent of the
548 548 working directory and a new child of REV that simply undoes REV.
549 549
550 550 Before version 1.7, the behavior without --merge was equivalent
551 551 to specifying --merge followed by :hg:`update --clean .` to
552 552 cancel the merge and leave the child of REV as a head to be
553 553 merged separately.
554 554
555 555 See :hg:`help dates` for a list of formats valid for -d/--date.
556 556
557 557 See :hg:`help revert` for a way to restore files to the state
558 558 of another revision.
559 559
560 560 Returns 0 on success, 1 if nothing to backout or there are unresolved
561 561 files.
562 562 '''
563 563 wlock = lock = None
564 564 try:
565 565 wlock = repo.wlock()
566 566 lock = repo.lock()
567 567 return _dobackout(ui, repo, node, rev, **opts)
568 568 finally:
569 569 release(lock, wlock)
570 570
571 571 def _dobackout(ui, repo, node=None, rev=None, **opts):
572 572 opts = pycompat.byteskwargs(opts)
573 573 if opts.get('commit') and opts.get('no_commit'):
574 574 raise error.Abort(_("cannot use --commit with --no-commit"))
575 575 if opts.get('merge') and opts.get('no_commit'):
576 576 raise error.Abort(_("cannot use --merge with --no-commit"))
577 577
578 578 if rev and node:
579 579 raise error.Abort(_("please specify just one revision"))
580 580
581 581 if not rev:
582 582 rev = node
583 583
584 584 if not rev:
585 585 raise error.Abort(_("please specify a revision to backout"))
586 586
587 587 date = opts.get('date')
588 588 if date:
589 589 opts['date'] = dateutil.parsedate(date)
590 590
591 591 cmdutil.checkunfinished(repo)
592 592 cmdutil.bailifchanged(repo)
593 593 node = scmutil.revsingle(repo, rev).node()
594 594
595 595 op1, op2 = repo.dirstate.parents()
596 596 if not repo.changelog.isancestor(node, op1):
597 597 raise error.Abort(_('cannot backout change that is not an ancestor'))
598 598
599 599 p1, p2 = repo.changelog.parents(node)
600 600 if p1 == nullid:
601 601 raise error.Abort(_('cannot backout a change with no parents'))
602 602 if p2 != nullid:
603 603 if not opts.get('parent'):
604 604 raise error.Abort(_('cannot backout a merge changeset'))
605 605 p = repo.lookup(opts['parent'])
606 606 if p not in (p1, p2):
607 607 raise error.Abort(_('%s is not a parent of %s') %
608 608 (short(p), short(node)))
609 609 parent = p
610 610 else:
611 611 if opts.get('parent'):
612 612 raise error.Abort(_('cannot use --parent on non-merge changeset'))
613 613 parent = p1
614 614
615 615 # the backout should appear on the same branch
616 616 branch = repo.dirstate.branch()
617 617 bheads = repo.branchheads(branch)
618 618 rctx = scmutil.revsingle(repo, hex(parent))
619 619 if not opts.get('merge') and op1 != node:
620 620 dsguard = dirstateguard.dirstateguard(repo, 'backout')
621 621 try:
622 622 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
623 623 'backout')
624 624 stats = mergemod.update(repo, parent, True, True, node, False)
625 625 repo.setparents(op1, op2)
626 626 dsguard.close()
627 627 hg._showstats(repo, stats)
628 628 if stats.unresolvedcount:
629 629 repo.ui.status(_("use 'hg resolve' to retry unresolved "
630 630 "file merges\n"))
631 631 return 1
632 632 finally:
633 633 ui.setconfig('ui', 'forcemerge', '', '')
634 634 lockmod.release(dsguard)
635 635 else:
636 636 hg.clean(repo, node, show_stats=False)
637 637 repo.dirstate.setbranch(branch)
638 638 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
639 639
640 640 if opts.get('no_commit'):
641 641 msg = _("changeset %s backed out, "
642 642 "don't forget to commit.\n")
643 643 ui.status(msg % short(node))
644 644 return 0
645 645
646 646 def commitfunc(ui, repo, message, match, opts):
647 647 editform = 'backout'
648 648 e = cmdutil.getcommiteditor(editform=editform,
649 649 **pycompat.strkwargs(opts))
650 650 if not message:
651 651 # we don't translate commit messages
652 652 message = "Backed out changeset %s" % short(node)
653 653 e = cmdutil.getcommiteditor(edit=True, editform=editform)
654 654 return repo.commit(message, opts.get('user'), opts.get('date'),
655 655 match, editor=e)
656 656 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
657 657 if not newnode:
658 658 ui.status(_("nothing changed\n"))
659 659 return 1
660 660 cmdutil.commitstatus(repo, newnode, branch, bheads)
661 661
662 662 def nice(node):
663 663 return '%d:%s' % (repo.changelog.rev(node), short(node))
664 664 ui.status(_('changeset %s backs out changeset %s\n') %
665 665 (nice(repo.changelog.tip()), nice(node)))
666 666 if opts.get('merge') and op1 != node:
667 667 hg.clean(repo, op1, show_stats=False)
668 668 ui.status(_('merging with changeset %s\n')
669 669 % nice(repo.changelog.tip()))
670 670 try:
671 671 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
672 672 'backout')
673 673 return hg.merge(repo, hex(repo.changelog.tip()))
674 674 finally:
675 675 ui.setconfig('ui', 'forcemerge', '', '')
676 676 return 0
677 677
678 678 @command('bisect',
679 679 [('r', 'reset', False, _('reset bisect state')),
680 680 ('g', 'good', False, _('mark changeset good')),
681 681 ('b', 'bad', False, _('mark changeset bad')),
682 682 ('s', 'skip', False, _('skip testing changeset')),
683 683 ('e', 'extend', False, _('extend the bisect range')),
684 684 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
685 685 ('U', 'noupdate', False, _('do not update to target'))],
686 686 _("[-gbsr] [-U] [-c CMD] [REV]"))
687 687 def bisect(ui, repo, rev=None, extra=None, command=None,
688 688 reset=None, good=None, bad=None, skip=None, extend=None,
689 689 noupdate=None):
690 690 """subdivision search of changesets
691 691
692 692 This command helps to find changesets which introduce problems. To
693 693 use, mark the earliest changeset you know exhibits the problem as
694 694 bad, then mark the latest changeset which is free from the problem
695 695 as good. Bisect will update your working directory to a revision
696 696 for testing (unless the -U/--noupdate option is specified). Once
697 697 you have performed tests, mark the working directory as good or
698 698 bad, and bisect will either update to another candidate changeset
699 699 or announce that it has found the bad revision.
700 700
701 701 As a shortcut, you can also use the revision argument to mark a
702 702 revision as good or bad without checking it out first.
703 703
704 704 If you supply a command, it will be used for automatic bisection.
705 705 The environment variable HG_NODE will contain the ID of the
706 706 changeset being tested. The exit status of the command will be
707 707 used to mark revisions as good or bad: status 0 means good, 125
708 708 means to skip the revision, 127 (command not found) will abort the
709 709 bisection, and any other non-zero exit status means the revision
710 710 is bad.
711 711
712 712 .. container:: verbose
713 713
714 714 Some examples:
715 715
716 716 - start a bisection with known bad revision 34, and good revision 12::
717 717
718 718 hg bisect --bad 34
719 719 hg bisect --good 12
720 720
721 721 - advance the current bisection by marking current revision as good or
722 722 bad::
723 723
724 724 hg bisect --good
725 725 hg bisect --bad
726 726
727 727 - mark the current revision, or a known revision, to be skipped (e.g. if
728 728 that revision is not usable because of another issue)::
729 729
730 730 hg bisect --skip
731 731 hg bisect --skip 23
732 732
733 733 - skip all revisions that do not touch directories ``foo`` or ``bar``::
734 734
735 735 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
736 736
737 737 - forget the current bisection::
738 738
739 739 hg bisect --reset
740 740
741 741 - use 'make && make tests' to automatically find the first broken
742 742 revision::
743 743
744 744 hg bisect --reset
745 745 hg bisect --bad 34
746 746 hg bisect --good 12
747 747 hg bisect --command "make && make tests"
748 748
749 749 - see all changesets whose states are already known in the current
750 750 bisection::
751 751
752 752 hg log -r "bisect(pruned)"
753 753
754 754 - see the changeset currently being bisected (especially useful
755 755 if running with -U/--noupdate)::
756 756
757 757 hg log -r "bisect(current)"
758 758
759 759 - see all changesets that took part in the current bisection::
760 760
761 761 hg log -r "bisect(range)"
762 762
763 763 - you can even get a nice graph::
764 764
765 765 hg log --graph -r "bisect(range)"
766 766
767 767 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
768 768
769 769 Returns 0 on success.
770 770 """
771 771 # backward compatibility
772 772 if rev in "good bad reset init".split():
773 773 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
774 774 cmd, rev, extra = rev, extra, None
775 775 if cmd == "good":
776 776 good = True
777 777 elif cmd == "bad":
778 778 bad = True
779 779 else:
780 780 reset = True
781 781 elif extra:
782 782 raise error.Abort(_('incompatible arguments'))
783 783
784 784 incompatibles = {
785 785 '--bad': bad,
786 786 '--command': bool(command),
787 787 '--extend': extend,
788 788 '--good': good,
789 789 '--reset': reset,
790 790 '--skip': skip,
791 791 }
792 792
793 793 enabled = [x for x in incompatibles if incompatibles[x]]
794 794
795 795 if len(enabled) > 1:
796 796 raise error.Abort(_('%s and %s are incompatible') %
797 797 tuple(sorted(enabled)[0:2]))
798 798
799 799 if reset:
800 800 hbisect.resetstate(repo)
801 801 return
802 802
803 803 state = hbisect.load_state(repo)
804 804
805 805 # update state
806 806 if good or bad or skip:
807 807 if rev:
808 808 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
809 809 else:
810 810 nodes = [repo.lookup('.')]
811 811 if good:
812 812 state['good'] += nodes
813 813 elif bad:
814 814 state['bad'] += nodes
815 815 elif skip:
816 816 state['skip'] += nodes
817 817 hbisect.save_state(repo, state)
818 818 if not (state['good'] and state['bad']):
819 819 return
820 820
821 821 def mayupdate(repo, node, show_stats=True):
822 822 """common used update sequence"""
823 823 if noupdate:
824 824 return
825 825 cmdutil.checkunfinished(repo)
826 826 cmdutil.bailifchanged(repo)
827 827 return hg.clean(repo, node, show_stats=show_stats)
828 828
829 829 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
830 830
831 831 if command:
832 832 changesets = 1
833 833 if noupdate:
834 834 try:
835 835 node = state['current'][0]
836 836 except LookupError:
837 837 raise error.Abort(_('current bisect revision is unknown - '
838 838 'start a new bisect to fix'))
839 839 else:
840 840 node, p2 = repo.dirstate.parents()
841 841 if p2 != nullid:
842 842 raise error.Abort(_('current bisect revision is a merge'))
843 843 if rev:
844 844 node = repo[scmutil.revsingle(repo, rev, node)].node()
845 845 try:
846 846 while changesets:
847 847 # update state
848 848 state['current'] = [node]
849 849 hbisect.save_state(repo, state)
850 850 status = ui.system(command, environ={'HG_NODE': hex(node)},
851 851 blockedtag='bisect_check')
852 852 if status == 125:
853 853 transition = "skip"
854 854 elif status == 0:
855 855 transition = "good"
856 856 # status < 0 means process was killed
857 857 elif status == 127:
858 858 raise error.Abort(_("failed to execute %s") % command)
859 859 elif status < 0:
860 860 raise error.Abort(_("%s killed") % command)
861 861 else:
862 862 transition = "bad"
863 863 state[transition].append(node)
864 864 ctx = repo[node]
865 865 ui.status(_('changeset %d:%s: %s\n') % (ctx.rev(), ctx,
866 866 transition))
867 867 hbisect.checkstate(state)
868 868 # bisect
869 869 nodes, changesets, bgood = hbisect.bisect(repo, state)
870 870 # update to next check
871 871 node = nodes[0]
872 872 mayupdate(repo, node, show_stats=False)
873 873 finally:
874 874 state['current'] = [node]
875 875 hbisect.save_state(repo, state)
876 876 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
877 877 return
878 878
879 879 hbisect.checkstate(state)
880 880
881 881 # actually bisect
882 882 nodes, changesets, good = hbisect.bisect(repo, state)
883 883 if extend:
884 884 if not changesets:
885 885 extendnode = hbisect.extendrange(repo, state, nodes, good)
886 886 if extendnode is not None:
887 887 ui.write(_("Extending search to changeset %d:%s\n")
888 888 % (extendnode.rev(), extendnode))
889 889 state['current'] = [extendnode.node()]
890 890 hbisect.save_state(repo, state)
891 891 return mayupdate(repo, extendnode.node())
892 892 raise error.Abort(_("nothing to extend"))
893 893
894 894 if changesets == 0:
895 895 hbisect.printresult(ui, repo, state, displayer, nodes, good)
896 896 else:
897 897 assert len(nodes) == 1 # only a single node can be tested next
898 898 node = nodes[0]
899 899 # compute the approximate number of remaining tests
900 900 tests, size = 0, 2
901 901 while size <= changesets:
902 902 tests, size = tests + 1, size * 2
903 903 rev = repo.changelog.rev(node)
904 904 ui.write(_("Testing changeset %d:%s "
905 905 "(%d changesets remaining, ~%d tests)\n")
906 906 % (rev, short(node), changesets, tests))
907 907 state['current'] = [node]
908 908 hbisect.save_state(repo, state)
909 909 return mayupdate(repo, node)
910 910
911 911 @command('bookmarks|bookmark',
912 912 [('f', 'force', False, _('force')),
913 913 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
914 914 ('d', 'delete', False, _('delete a given bookmark')),
915 915 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
916 916 ('i', 'inactive', False, _('mark a bookmark inactive')),
917 917 ] + formatteropts,
918 918 _('hg bookmarks [OPTIONS]... [NAME]...'))
919 919 def bookmark(ui, repo, *names, **opts):
920 920 '''create a new bookmark or list existing bookmarks
921 921
922 922 Bookmarks are labels on changesets to help track lines of development.
923 923 Bookmarks are unversioned and can be moved, renamed and deleted.
924 924 Deleting or moving a bookmark has no effect on the associated changesets.
925 925
926 926 Creating or updating to a bookmark causes it to be marked as 'active'.
927 927 The active bookmark is indicated with a '*'.
928 928 When a commit is made, the active bookmark will advance to the new commit.
929 929 A plain :hg:`update` will also advance an active bookmark, if possible.
930 930 Updating away from a bookmark will cause it to be deactivated.
931 931
932 932 Bookmarks can be pushed and pulled between repositories (see
933 933 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
934 934 diverged, a new 'divergent bookmark' of the form 'name@path' will
935 935 be created. Using :hg:`merge` will resolve the divergence.
936 936
937 937 Specifying bookmark as '.' to -m or -d options is equivalent to specifying
938 938 the active bookmark's name.
939 939
940 940 A bookmark named '@' has the special property that :hg:`clone` will
941 941 check it out by default if it exists.
942 942
943 943 .. container:: verbose
944 944
945 945 Examples:
946 946
947 947 - create an active bookmark for a new line of development::
948 948
949 949 hg book new-feature
950 950
951 951 - create an inactive bookmark as a place marker::
952 952
953 953 hg book -i reviewed
954 954
955 955 - create an inactive bookmark on another changeset::
956 956
957 957 hg book -r .^ tested
958 958
959 959 - rename bookmark turkey to dinner::
960 960
961 961 hg book -m turkey dinner
962 962
963 963 - move the '@' bookmark from another branch::
964 964
965 965 hg book -f @
966 966 '''
967 967 force = opts.get(r'force')
968 968 rev = opts.get(r'rev')
969 969 delete = opts.get(r'delete')
970 970 rename = opts.get(r'rename')
971 971 inactive = opts.get(r'inactive')
972 972
973 973 if delete and rename:
974 974 raise error.Abort(_("--delete and --rename are incompatible"))
975 975 if delete and rev:
976 976 raise error.Abort(_("--rev is incompatible with --delete"))
977 977 if rename and rev:
978 978 raise error.Abort(_("--rev is incompatible with --rename"))
979 979 if not names and (delete or rev):
980 980 raise error.Abort(_("bookmark name required"))
981 981
982 982 if delete or rename or names or inactive:
983 983 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
984 984 if delete:
985 985 names = pycompat.maplist(repo._bookmarks.expandname, names)
986 986 bookmarks.delete(repo, tr, names)
987 987 elif rename:
988 988 if not names:
989 989 raise error.Abort(_("new bookmark name required"))
990 990 elif len(names) > 1:
991 991 raise error.Abort(_("only one new bookmark name allowed"))
992 992 rename = repo._bookmarks.expandname(rename)
993 993 bookmarks.rename(repo, tr, rename, names[0], force, inactive)
994 994 elif names:
995 995 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
996 996 elif inactive:
997 997 if len(repo._bookmarks) == 0:
998 998 ui.status(_("no bookmarks set\n"))
999 999 elif not repo._activebookmark:
1000 1000 ui.status(_("no active bookmark\n"))
1001 1001 else:
1002 1002 bookmarks.deactivate(repo)
1003 1003 else: # show bookmarks
1004 1004 bookmarks.printbookmarks(ui, repo, **opts)
1005 1005
1006 1006 @command('branch',
1007 1007 [('f', 'force', None,
1008 1008 _('set branch name even if it shadows an existing branch')),
1009 1009 ('C', 'clean', None, _('reset branch name to parent branch name')),
1010 1010 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
1011 1011 ],
1012 1012 _('[-fC] [NAME]'))
1013 1013 def branch(ui, repo, label=None, **opts):
1014 1014 """set or show the current branch name
1015 1015
1016 1016 .. note::
1017 1017
1018 1018 Branch names are permanent and global. Use :hg:`bookmark` to create a
1019 1019 light-weight bookmark instead. See :hg:`help glossary` for more
1020 1020 information about named branches and bookmarks.
1021 1021
1022 1022 With no argument, show the current branch name. With one argument,
1023 1023 set the working directory branch name (the branch will not exist
1024 1024 in the repository until the next commit). Standard practice
1025 1025 recommends that primary development take place on the 'default'
1026 1026 branch.
1027 1027
1028 1028 Unless -f/--force is specified, branch will not let you set a
1029 1029 branch name that already exists.
1030 1030
1031 1031 Use -C/--clean to reset the working directory branch to that of
1032 1032 the parent of the working directory, negating a previous branch
1033 1033 change.
1034 1034
1035 1035 Use the command :hg:`update` to switch to an existing branch. Use
1036 1036 :hg:`commit --close-branch` to mark this branch head as closed.
1037 1037 When all heads of a branch are closed, the branch will be
1038 1038 considered closed.
1039 1039
1040 1040 Returns 0 on success.
1041 1041 """
1042 1042 opts = pycompat.byteskwargs(opts)
1043 1043 revs = opts.get('rev')
1044 1044 if label:
1045 1045 label = label.strip()
1046 1046
1047 1047 if not opts.get('clean') and not label:
1048 1048 if revs:
1049 1049 raise error.Abort(_("no branch name specified for the revisions"))
1050 1050 ui.write("%s\n" % repo.dirstate.branch())
1051 1051 return
1052 1052
1053 1053 with repo.wlock():
1054 1054 if opts.get('clean'):
1055 1055 label = repo[None].p1().branch()
1056 1056 repo.dirstate.setbranch(label)
1057 1057 ui.status(_('reset working directory to branch %s\n') % label)
1058 1058 elif label:
1059 1059
1060 1060 scmutil.checknewlabel(repo, label, 'branch')
1061 1061 if revs:
1062 1062 return cmdutil.changebranch(ui, repo, revs, label)
1063 1063
1064 1064 if not opts.get('force') and label in repo.branchmap():
1065 1065 if label not in [p.branch() for p in repo[None].parents()]:
1066 1066 raise error.Abort(_('a branch of the same name already'
1067 1067 ' exists'),
1068 1068 # i18n: "it" refers to an existing branch
1069 1069 hint=_("use 'hg update' to switch to it"))
1070 1070
1071 1071 repo.dirstate.setbranch(label)
1072 1072 ui.status(_('marked working directory as branch %s\n') % label)
1073 1073
1074 1074 # find any open named branches aside from default
1075 1075 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1076 1076 if n != "default" and not c]
1077 1077 if not others:
1078 1078 ui.status(_('(branches are permanent and global, '
1079 1079 'did you want a bookmark?)\n'))
1080 1080
1081 1081 @command('branches',
1082 1082 [('a', 'active', False,
1083 1083 _('show only branches that have unmerged heads (DEPRECATED)')),
1084 1084 ('c', 'closed', False, _('show normal and closed branches')),
1085 1085 ] + formatteropts,
1086 _('[-c]'), cmdtype=readonly)
1086 _('[-c]'),
1087 intents={INTENT_READONLY})
1087 1088 def branches(ui, repo, active=False, closed=False, **opts):
1088 1089 """list repository named branches
1089 1090
1090 1091 List the repository's named branches, indicating which ones are
1091 1092 inactive. If -c/--closed is specified, also list branches which have
1092 1093 been marked closed (see :hg:`commit --close-branch`).
1093 1094
1094 1095 Use the command :hg:`update` to switch to an existing branch.
1095 1096
1096 1097 Returns 0.
1097 1098 """
1098 1099
1099 1100 opts = pycompat.byteskwargs(opts)
1100 1101 ui.pager('branches')
1101 1102 fm = ui.formatter('branches', opts)
1102 1103 hexfunc = fm.hexfunc
1103 1104
1104 1105 allheads = set(repo.heads())
1105 1106 branches = []
1106 1107 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1107 1108 isactive = False
1108 1109 if not isclosed:
1109 1110 openheads = set(repo.branchmap().iteropen(heads))
1110 1111 isactive = bool(openheads & allheads)
1111 1112 branches.append((tag, repo[tip], isactive, not isclosed))
1112 1113 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1113 1114 reverse=True)
1114 1115
1115 1116 for tag, ctx, isactive, isopen in branches:
1116 1117 if active and not isactive:
1117 1118 continue
1118 1119 if isactive:
1119 1120 label = 'branches.active'
1120 1121 notice = ''
1121 1122 elif not isopen:
1122 1123 if not closed:
1123 1124 continue
1124 1125 label = 'branches.closed'
1125 1126 notice = _(' (closed)')
1126 1127 else:
1127 1128 label = 'branches.inactive'
1128 1129 notice = _(' (inactive)')
1129 1130 current = (tag == repo.dirstate.branch())
1130 1131 if current:
1131 1132 label = 'branches.current'
1132 1133
1133 1134 fm.startitem()
1134 1135 fm.write('branch', '%s', tag, label=label)
1135 1136 rev = ctx.rev()
1136 1137 padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
1137 1138 fmt = ' ' * padsize + ' %d:%s'
1138 1139 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1139 1140 label='log.changeset changeset.%s' % ctx.phasestr())
1140 1141 fm.context(ctx=ctx)
1141 1142 fm.data(active=isactive, closed=not isopen, current=current)
1142 1143 if not ui.quiet:
1143 1144 fm.plain(notice)
1144 1145 fm.plain('\n')
1145 1146 fm.end()
1146 1147
1147 1148 @command('bundle',
1148 1149 [('f', 'force', None, _('run even when the destination is unrelated')),
1149 1150 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1150 1151 _('REV')),
1151 1152 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1152 1153 _('BRANCH')),
1153 1154 ('', 'base', [],
1154 1155 _('a base changeset assumed to be available at the destination'),
1155 1156 _('REV')),
1156 1157 ('a', 'all', None, _('bundle all changesets in the repository')),
1157 1158 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1158 1159 ] + remoteopts,
1159 1160 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1160 1161 def bundle(ui, repo, fname, dest=None, **opts):
1161 1162 """create a bundle file
1162 1163
1163 1164 Generate a bundle file containing data to be transferred to another
1164 1165 repository.
1165 1166
1166 1167 To create a bundle containing all changesets, use -a/--all
1167 1168 (or --base null). Otherwise, hg assumes the destination will have
1168 1169 all the nodes you specify with --base parameters. Otherwise, hg
1169 1170 will assume the repository has all the nodes in destination, or
1170 1171 default-push/default if no destination is specified, where destination
1171 1172 is the repository you provide through DEST option.
1172 1173
1173 1174 You can change bundle format with the -t/--type option. See
1174 1175 :hg:`help bundlespec` for documentation on this format. By default,
1175 1176 the most appropriate format is used and compression defaults to
1176 1177 bzip2.
1177 1178
1178 1179 The bundle file can then be transferred using conventional means
1179 1180 and applied to another repository with the unbundle or pull
1180 1181 command. This is useful when direct push and pull are not
1181 1182 available or when exporting an entire repository is undesirable.
1182 1183
1183 1184 Applying bundles preserves all changeset contents including
1184 1185 permissions, copy/rename information, and revision history.
1185 1186
1186 1187 Returns 0 on success, 1 if no changes found.
1187 1188 """
1188 1189 opts = pycompat.byteskwargs(opts)
1189 1190 revs = None
1190 1191 if 'rev' in opts:
1191 1192 revstrings = opts['rev']
1192 1193 revs = scmutil.revrange(repo, revstrings)
1193 1194 if revstrings and not revs:
1194 1195 raise error.Abort(_('no commits to bundle'))
1195 1196
1196 1197 bundletype = opts.get('type', 'bzip2').lower()
1197 1198 try:
1198 1199 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1199 1200 except error.UnsupportedBundleSpecification as e:
1200 1201 raise error.Abort(pycompat.bytestr(e),
1201 1202 hint=_("see 'hg help bundlespec' for supported "
1202 1203 "values for --type"))
1203 1204 cgversion = bundlespec.contentopts["cg.version"]
1204 1205
1205 1206 # Packed bundles are a pseudo bundle format for now.
1206 1207 if cgversion == 's1':
1207 1208 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1208 1209 hint=_("use 'hg debugcreatestreamclonebundle'"))
1209 1210
1210 1211 if opts.get('all'):
1211 1212 if dest:
1212 1213 raise error.Abort(_("--all is incompatible with specifying "
1213 1214 "a destination"))
1214 1215 if opts.get('base'):
1215 1216 ui.warn(_("ignoring --base because --all was specified\n"))
1216 1217 base = ['null']
1217 1218 else:
1218 1219 base = scmutil.revrange(repo, opts.get('base'))
1219 1220 if cgversion not in changegroup.supportedoutgoingversions(repo):
1220 1221 raise error.Abort(_("repository does not support bundle version %s") %
1221 1222 cgversion)
1222 1223
1223 1224 if base:
1224 1225 if dest:
1225 1226 raise error.Abort(_("--base is incompatible with specifying "
1226 1227 "a destination"))
1227 1228 common = [repo[rev].node() for rev in base]
1228 1229 heads = [repo[r].node() for r in revs] if revs else None
1229 1230 outgoing = discovery.outgoing(repo, common, heads)
1230 1231 else:
1231 1232 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1232 1233 dest, branches = hg.parseurl(dest, opts.get('branch'))
1233 1234 other = hg.peer(repo, opts, dest)
1234 1235 revs = [repo[r].hex() for r in revs]
1235 1236 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1236 1237 heads = revs and map(repo.lookup, revs) or revs
1237 1238 outgoing = discovery.findcommonoutgoing(repo, other,
1238 1239 onlyheads=heads,
1239 1240 force=opts.get('force'),
1240 1241 portable=True)
1241 1242
1242 1243 if not outgoing.missing:
1243 1244 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1244 1245 return 1
1245 1246
1246 1247 bcompression = bundlespec.compression
1247 1248 if cgversion == '01': #bundle1
1248 1249 if bcompression is None:
1249 1250 bcompression = 'UN'
1250 1251 bversion = 'HG10' + bcompression
1251 1252 bcompression = None
1252 1253 elif cgversion in ('02', '03'):
1253 1254 bversion = 'HG20'
1254 1255 else:
1255 1256 raise error.ProgrammingError(
1256 1257 'bundle: unexpected changegroup version %s' % cgversion)
1257 1258
1258 1259 # TODO compression options should be derived from bundlespec parsing.
1259 1260 # This is a temporary hack to allow adjusting bundle compression
1260 1261 # level without a) formalizing the bundlespec changes to declare it
1261 1262 # b) introducing a command flag.
1262 1263 compopts = {}
1263 1264 complevel = ui.configint('experimental', 'bundlecomplevel')
1264 1265 if complevel is not None:
1265 1266 compopts['level'] = complevel
1266 1267
1267 1268 # Allow overriding the bundling of obsmarker in phases through
1268 1269 # configuration while we don't have a bundle version that include them
1269 1270 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1270 1271 bundlespec.contentopts['obsolescence'] = True
1271 1272 if repo.ui.configbool('experimental', 'bundle-phases'):
1272 1273 bundlespec.contentopts['phases'] = True
1273 1274
1274 1275 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1275 1276 bundlespec.contentopts, compression=bcompression,
1276 1277 compopts=compopts)
1277 1278
1278 1279 @command('cat',
1279 1280 [('o', 'output', '',
1280 1281 _('print output to file with formatted name'), _('FORMAT')),
1281 1282 ('r', 'rev', '', _('print the given revision'), _('REV')),
1282 1283 ('', 'decode', None, _('apply any matching decode filter')),
1283 1284 ] + walkopts + formatteropts,
1284 1285 _('[OPTION]... FILE...'),
1285 inferrepo=True, cmdtype=readonly)
1286 inferrepo=True,
1287 intents={INTENT_READONLY})
1286 1288 def cat(ui, repo, file1, *pats, **opts):
1287 1289 """output the current or given revision of files
1288 1290
1289 1291 Print the specified files as they were at the given revision. If
1290 1292 no revision is given, the parent of the working directory is used.
1291 1293
1292 1294 Output may be to a file, in which case the name of the file is
1293 1295 given using a template string. See :hg:`help templates`. In addition
1294 1296 to the common template keywords, the following formatting rules are
1295 1297 supported:
1296 1298
1297 1299 :``%%``: literal "%" character
1298 1300 :``%s``: basename of file being printed
1299 1301 :``%d``: dirname of file being printed, or '.' if in repository root
1300 1302 :``%p``: root-relative path name of file being printed
1301 1303 :``%H``: changeset hash (40 hexadecimal digits)
1302 1304 :``%R``: changeset revision number
1303 1305 :``%h``: short-form changeset hash (12 hexadecimal digits)
1304 1306 :``%r``: zero-padded changeset revision number
1305 1307 :``%b``: basename of the exporting repository
1306 1308 :``\\``: literal "\\" character
1307 1309
1308 1310 Returns 0 on success.
1309 1311 """
1310 1312 opts = pycompat.byteskwargs(opts)
1311 1313 rev = opts.get('rev')
1312 1314 if rev:
1313 1315 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1314 1316 ctx = scmutil.revsingle(repo, rev)
1315 1317 m = scmutil.match(ctx, (file1,) + pats, opts)
1316 1318 fntemplate = opts.pop('output', '')
1317 1319 if cmdutil.isstdiofilename(fntemplate):
1318 1320 fntemplate = ''
1319 1321
1320 1322 if fntemplate:
1321 1323 fm = formatter.nullformatter(ui, 'cat', opts)
1322 1324 else:
1323 1325 ui.pager('cat')
1324 1326 fm = ui.formatter('cat', opts)
1325 1327 with fm:
1326 1328 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1327 1329 **pycompat.strkwargs(opts))
1328 1330
1329 1331 @command('^clone',
1330 1332 [('U', 'noupdate', None, _('the clone will include an empty working '
1331 1333 'directory (only a repository)')),
1332 1334 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1333 1335 _('REV')),
1334 1336 ('r', 'rev', [], _('do not clone everything, but include this changeset'
1335 1337 ' and its ancestors'), _('REV')),
1336 1338 ('b', 'branch', [], _('do not clone everything, but include this branch\'s'
1337 1339 ' changesets and their ancestors'), _('BRANCH')),
1338 1340 ('', 'pull', None, _('use pull protocol to copy metadata')),
1339 1341 ('', 'uncompressed', None,
1340 1342 _('an alias to --stream (DEPRECATED)')),
1341 1343 ('', 'stream', None,
1342 1344 _('clone with minimal data processing')),
1343 1345 ] + remoteopts,
1344 1346 _('[OPTION]... SOURCE [DEST]'),
1345 1347 norepo=True)
1346 1348 def clone(ui, source, dest=None, **opts):
1347 1349 """make a copy of an existing repository
1348 1350
1349 1351 Create a copy of an existing repository in a new directory.
1350 1352
1351 1353 If no destination directory name is specified, it defaults to the
1352 1354 basename of the source.
1353 1355
1354 1356 The location of the source is added to the new repository's
1355 1357 ``.hg/hgrc`` file, as the default to be used for future pulls.
1356 1358
1357 1359 Only local paths and ``ssh://`` URLs are supported as
1358 1360 destinations. For ``ssh://`` destinations, no working directory or
1359 1361 ``.hg/hgrc`` will be created on the remote side.
1360 1362
1361 1363 If the source repository has a bookmark called '@' set, that
1362 1364 revision will be checked out in the new repository by default.
1363 1365
1364 1366 To check out a particular version, use -u/--update, or
1365 1367 -U/--noupdate to create a clone with no working directory.
1366 1368
1367 1369 To pull only a subset of changesets, specify one or more revisions
1368 1370 identifiers with -r/--rev or branches with -b/--branch. The
1369 1371 resulting clone will contain only the specified changesets and
1370 1372 their ancestors. These options (or 'clone src#rev dest') imply
1371 1373 --pull, even for local source repositories.
1372 1374
1373 1375 In normal clone mode, the remote normalizes repository data into a common
1374 1376 exchange format and the receiving end translates this data into its local
1375 1377 storage format. --stream activates a different clone mode that essentially
1376 1378 copies repository files from the remote with minimal data processing. This
1377 1379 significantly reduces the CPU cost of a clone both remotely and locally.
1378 1380 However, it often increases the transferred data size by 30-40%. This can
1379 1381 result in substantially faster clones where I/O throughput is plentiful,
1380 1382 especially for larger repositories. A side-effect of --stream clones is
1381 1383 that storage settings and requirements on the remote are applied locally:
1382 1384 a modern client may inherit legacy or inefficient storage used by the
1383 1385 remote or a legacy Mercurial client may not be able to clone from a
1384 1386 modern Mercurial remote.
1385 1387
1386 1388 .. note::
1387 1389
1388 1390 Specifying a tag will include the tagged changeset but not the
1389 1391 changeset containing the tag.
1390 1392
1391 1393 .. container:: verbose
1392 1394
1393 1395 For efficiency, hardlinks are used for cloning whenever the
1394 1396 source and destination are on the same filesystem (note this
1395 1397 applies only to the repository data, not to the working
1396 1398 directory). Some filesystems, such as AFS, implement hardlinking
1397 1399 incorrectly, but do not report errors. In these cases, use the
1398 1400 --pull option to avoid hardlinking.
1399 1401
1400 1402 Mercurial will update the working directory to the first applicable
1401 1403 revision from this list:
1402 1404
1403 1405 a) null if -U or the source repository has no changesets
1404 1406 b) if -u . and the source repository is local, the first parent of
1405 1407 the source repository's working directory
1406 1408 c) the changeset specified with -u (if a branch name, this means the
1407 1409 latest head of that branch)
1408 1410 d) the changeset specified with -r
1409 1411 e) the tipmost head specified with -b
1410 1412 f) the tipmost head specified with the url#branch source syntax
1411 1413 g) the revision marked with the '@' bookmark, if present
1412 1414 h) the tipmost head of the default branch
1413 1415 i) tip
1414 1416
1415 1417 When cloning from servers that support it, Mercurial may fetch
1416 1418 pre-generated data from a server-advertised URL or inline from the
1417 1419 same stream. When this is done, hooks operating on incoming changesets
1418 1420 and changegroups may fire more than once, once for each pre-generated
1419 1421 bundle and as well as for any additional remaining data. In addition,
1420 1422 if an error occurs, the repository may be rolled back to a partial
1421 1423 clone. This behavior may change in future releases.
1422 1424 See :hg:`help -e clonebundles` for more.
1423 1425
1424 1426 Examples:
1425 1427
1426 1428 - clone a remote repository to a new directory named hg/::
1427 1429
1428 1430 hg clone https://www.mercurial-scm.org/repo/hg/
1429 1431
1430 1432 - create a lightweight local clone::
1431 1433
1432 1434 hg clone project/ project-feature/
1433 1435
1434 1436 - clone from an absolute path on an ssh server (note double-slash)::
1435 1437
1436 1438 hg clone ssh://user@server//home/projects/alpha/
1437 1439
1438 1440 - do a streaming clone while checking out a specified version::
1439 1441
1440 1442 hg clone --stream http://server/repo -u 1.5
1441 1443
1442 1444 - create a repository without changesets after a particular revision::
1443 1445
1444 1446 hg clone -r 04e544 experimental/ good/
1445 1447
1446 1448 - clone (and track) a particular named branch::
1447 1449
1448 1450 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1449 1451
1450 1452 See :hg:`help urls` for details on specifying URLs.
1451 1453
1452 1454 Returns 0 on success.
1453 1455 """
1454 1456 opts = pycompat.byteskwargs(opts)
1455 1457 if opts.get('noupdate') and opts.get('updaterev'):
1456 1458 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1457 1459
1458 1460 r = hg.clone(ui, opts, source, dest,
1459 1461 pull=opts.get('pull'),
1460 1462 stream=opts.get('stream') or opts.get('uncompressed'),
1461 1463 revs=opts.get('rev'),
1462 1464 update=opts.get('updaterev') or not opts.get('noupdate'),
1463 1465 branch=opts.get('branch'),
1464 1466 shareopts=opts.get('shareopts'))
1465 1467
1466 1468 return r is None
1467 1469
1468 1470 @command('^commit|ci',
1469 1471 [('A', 'addremove', None,
1470 1472 _('mark new/missing files as added/removed before committing')),
1471 1473 ('', 'close-branch', None,
1472 1474 _('mark a branch head as closed')),
1473 1475 ('', 'amend', None, _('amend the parent of the working directory')),
1474 1476 ('s', 'secret', None, _('use the secret phase for committing')),
1475 1477 ('e', 'edit', None, _('invoke editor on commit messages')),
1476 1478 ('i', 'interactive', None, _('use interactive mode')),
1477 1479 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1478 1480 _('[OPTION]... [FILE]...'),
1479 1481 inferrepo=True)
1480 1482 def commit(ui, repo, *pats, **opts):
1481 1483 """commit the specified files or all outstanding changes
1482 1484
1483 1485 Commit changes to the given files into the repository. Unlike a
1484 1486 centralized SCM, this operation is a local operation. See
1485 1487 :hg:`push` for a way to actively distribute your changes.
1486 1488
1487 1489 If a list of files is omitted, all changes reported by :hg:`status`
1488 1490 will be committed.
1489 1491
1490 1492 If you are committing the result of a merge, do not provide any
1491 1493 filenames or -I/-X filters.
1492 1494
1493 1495 If no commit message is specified, Mercurial starts your
1494 1496 configured editor where you can enter a message. In case your
1495 1497 commit fails, you will find a backup of your message in
1496 1498 ``.hg/last-message.txt``.
1497 1499
1498 1500 The --close-branch flag can be used to mark the current branch
1499 1501 head closed. When all heads of a branch are closed, the branch
1500 1502 will be considered closed and no longer listed.
1501 1503
1502 1504 The --amend flag can be used to amend the parent of the
1503 1505 working directory with a new commit that contains the changes
1504 1506 in the parent in addition to those currently reported by :hg:`status`,
1505 1507 if there are any. The old commit is stored in a backup bundle in
1506 1508 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1507 1509 on how to restore it).
1508 1510
1509 1511 Message, user and date are taken from the amended commit unless
1510 1512 specified. When a message isn't specified on the command line,
1511 1513 the editor will open with the message of the amended commit.
1512 1514
1513 1515 It is not possible to amend public changesets (see :hg:`help phases`)
1514 1516 or changesets that have children.
1515 1517
1516 1518 See :hg:`help dates` for a list of formats valid for -d/--date.
1517 1519
1518 1520 Returns 0 on success, 1 if nothing changed.
1519 1521
1520 1522 .. container:: verbose
1521 1523
1522 1524 Examples:
1523 1525
1524 1526 - commit all files ending in .py::
1525 1527
1526 1528 hg commit --include "set:**.py"
1527 1529
1528 1530 - commit all non-binary files::
1529 1531
1530 1532 hg commit --exclude "set:binary()"
1531 1533
1532 1534 - amend the current commit and set the date to now::
1533 1535
1534 1536 hg commit --amend --date now
1535 1537 """
1536 1538 wlock = lock = None
1537 1539 try:
1538 1540 wlock = repo.wlock()
1539 1541 lock = repo.lock()
1540 1542 return _docommit(ui, repo, *pats, **opts)
1541 1543 finally:
1542 1544 release(lock, wlock)
1543 1545
1544 1546 def _docommit(ui, repo, *pats, **opts):
1545 1547 if opts.get(r'interactive'):
1546 1548 opts.pop(r'interactive')
1547 1549 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1548 1550 cmdutil.recordfilter, *pats,
1549 1551 **opts)
1550 1552 # ret can be 0 (no changes to record) or the value returned by
1551 1553 # commit(), 1 if nothing changed or None on success.
1552 1554 return 1 if ret == 0 else ret
1553 1555
1554 1556 opts = pycompat.byteskwargs(opts)
1555 1557 if opts.get('subrepos'):
1556 1558 if opts.get('amend'):
1557 1559 raise error.Abort(_('cannot amend with --subrepos'))
1558 1560 # Let --subrepos on the command line override config setting.
1559 1561 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1560 1562
1561 1563 cmdutil.checkunfinished(repo, commit=True)
1562 1564
1563 1565 branch = repo[None].branch()
1564 1566 bheads = repo.branchheads(branch)
1565 1567
1566 1568 extra = {}
1567 1569 if opts.get('close_branch'):
1568 1570 extra['close'] = '1'
1569 1571
1570 1572 if not bheads:
1571 1573 raise error.Abort(_('can only close branch heads'))
1572 1574 elif opts.get('amend'):
1573 1575 if repo[None].parents()[0].p1().branch() != branch and \
1574 1576 repo[None].parents()[0].p2().branch() != branch:
1575 1577 raise error.Abort(_('can only close branch heads'))
1576 1578
1577 1579 if opts.get('amend'):
1578 1580 if ui.configbool('ui', 'commitsubrepos'):
1579 1581 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1580 1582
1581 1583 old = repo['.']
1582 1584 rewriteutil.precheck(repo, [old.rev()], 'amend')
1583 1585
1584 1586 # Currently histedit gets confused if an amend happens while histedit
1585 1587 # is in progress. Since we have a checkunfinished command, we are
1586 1588 # temporarily honoring it.
1587 1589 #
1588 1590 # Note: eventually this guard will be removed. Please do not expect
1589 1591 # this behavior to remain.
1590 1592 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1591 1593 cmdutil.checkunfinished(repo)
1592 1594
1593 1595 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1594 1596 if node == old.node():
1595 1597 ui.status(_("nothing changed\n"))
1596 1598 return 1
1597 1599 else:
1598 1600 def commitfunc(ui, repo, message, match, opts):
1599 1601 overrides = {}
1600 1602 if opts.get('secret'):
1601 1603 overrides[('phases', 'new-commit')] = 'secret'
1602 1604
1603 1605 baseui = repo.baseui
1604 1606 with baseui.configoverride(overrides, 'commit'):
1605 1607 with ui.configoverride(overrides, 'commit'):
1606 1608 editform = cmdutil.mergeeditform(repo[None],
1607 1609 'commit.normal')
1608 1610 editor = cmdutil.getcommiteditor(
1609 1611 editform=editform, **pycompat.strkwargs(opts))
1610 1612 return repo.commit(message,
1611 1613 opts.get('user'),
1612 1614 opts.get('date'),
1613 1615 match,
1614 1616 editor=editor,
1615 1617 extra=extra)
1616 1618
1617 1619 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1618 1620
1619 1621 if not node:
1620 1622 stat = cmdutil.postcommitstatus(repo, pats, opts)
1621 1623 if stat[3]:
1622 1624 ui.status(_("nothing changed (%d missing files, see "
1623 1625 "'hg status')\n") % len(stat[3]))
1624 1626 else:
1625 1627 ui.status(_("nothing changed\n"))
1626 1628 return 1
1627 1629
1628 1630 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1629 1631
1630 1632 @command('config|showconfig|debugconfig',
1631 1633 [('u', 'untrusted', None, _('show untrusted configuration options')),
1632 1634 ('e', 'edit', None, _('edit user config')),
1633 1635 ('l', 'local', None, _('edit repository config')),
1634 1636 ('g', 'global', None, _('edit global config'))] + formatteropts,
1635 1637 _('[-u] [NAME]...'),
1636 optionalrepo=True, cmdtype=readonly)
1638 optionalrepo=True,
1639 intents={INTENT_READONLY})
1637 1640 def config(ui, repo, *values, **opts):
1638 1641 """show combined config settings from all hgrc files
1639 1642
1640 1643 With no arguments, print names and values of all config items.
1641 1644
1642 1645 With one argument of the form section.name, print just the value
1643 1646 of that config item.
1644 1647
1645 1648 With multiple arguments, print names and values of all config
1646 1649 items with matching section names or section.names.
1647 1650
1648 1651 With --edit, start an editor on the user-level config file. With
1649 1652 --global, edit the system-wide config file. With --local, edit the
1650 1653 repository-level config file.
1651 1654
1652 1655 With --debug, the source (filename and line number) is printed
1653 1656 for each config item.
1654 1657
1655 1658 See :hg:`help config` for more information about config files.
1656 1659
1657 1660 Returns 0 on success, 1 if NAME does not exist.
1658 1661
1659 1662 """
1660 1663
1661 1664 opts = pycompat.byteskwargs(opts)
1662 1665 if opts.get('edit') or opts.get('local') or opts.get('global'):
1663 1666 if opts.get('local') and opts.get('global'):
1664 1667 raise error.Abort(_("can't use --local and --global together"))
1665 1668
1666 1669 if opts.get('local'):
1667 1670 if not repo:
1668 1671 raise error.Abort(_("can't use --local outside a repository"))
1669 1672 paths = [repo.vfs.join('hgrc')]
1670 1673 elif opts.get('global'):
1671 1674 paths = rcutil.systemrcpath()
1672 1675 else:
1673 1676 paths = rcutil.userrcpath()
1674 1677
1675 1678 for f in paths:
1676 1679 if os.path.exists(f):
1677 1680 break
1678 1681 else:
1679 1682 if opts.get('global'):
1680 1683 samplehgrc = uimod.samplehgrcs['global']
1681 1684 elif opts.get('local'):
1682 1685 samplehgrc = uimod.samplehgrcs['local']
1683 1686 else:
1684 1687 samplehgrc = uimod.samplehgrcs['user']
1685 1688
1686 1689 f = paths[0]
1687 1690 fp = open(f, "wb")
1688 1691 fp.write(util.tonativeeol(samplehgrc))
1689 1692 fp.close()
1690 1693
1691 1694 editor = ui.geteditor()
1692 1695 ui.system("%s \"%s\"" % (editor, f),
1693 1696 onerr=error.Abort, errprefix=_("edit failed"),
1694 1697 blockedtag='config_edit')
1695 1698 return
1696 1699 ui.pager('config')
1697 1700 fm = ui.formatter('config', opts)
1698 1701 for t, f in rcutil.rccomponents():
1699 1702 if t == 'path':
1700 1703 ui.debug('read config from: %s\n' % f)
1701 1704 elif t == 'items':
1702 1705 for section, name, value, source in f:
1703 1706 ui.debug('set config by: %s\n' % source)
1704 1707 else:
1705 1708 raise error.ProgrammingError('unknown rctype: %s' % t)
1706 1709 untrusted = bool(opts.get('untrusted'))
1707 1710
1708 1711 selsections = selentries = []
1709 1712 if values:
1710 1713 selsections = [v for v in values if '.' not in v]
1711 1714 selentries = [v for v in values if '.' in v]
1712 1715 uniquesel = (len(selentries) == 1 and not selsections)
1713 1716 selsections = set(selsections)
1714 1717 selentries = set(selentries)
1715 1718
1716 1719 matched = False
1717 1720 for section, name, value in ui.walkconfig(untrusted=untrusted):
1718 1721 source = ui.configsource(section, name, untrusted)
1719 1722 value = pycompat.bytestr(value)
1720 1723 if fm.isplain():
1721 1724 source = source or 'none'
1722 1725 value = value.replace('\n', '\\n')
1723 1726 entryname = section + '.' + name
1724 1727 if values and not (section in selsections or entryname in selentries):
1725 1728 continue
1726 1729 fm.startitem()
1727 1730 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1728 1731 if uniquesel:
1729 1732 fm.data(name=entryname)
1730 1733 fm.write('value', '%s\n', value)
1731 1734 else:
1732 1735 fm.write('name value', '%s=%s\n', entryname, value)
1733 1736 matched = True
1734 1737 fm.end()
1735 1738 if matched:
1736 1739 return 0
1737 1740 return 1
1738 1741
1739 1742 @command('copy|cp',
1740 1743 [('A', 'after', None, _('record a copy that has already occurred')),
1741 1744 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1742 1745 ] + walkopts + dryrunopts,
1743 1746 _('[OPTION]... [SOURCE]... DEST'))
1744 1747 def copy(ui, repo, *pats, **opts):
1745 1748 """mark files as copied for the next commit
1746 1749
1747 1750 Mark dest as having copies of source files. If dest is a
1748 1751 directory, copies are put in that directory. If dest is a file,
1749 1752 the source must be a single file.
1750 1753
1751 1754 By default, this command copies the contents of files as they
1752 1755 exist in the working directory. If invoked with -A/--after, the
1753 1756 operation is recorded, but no copying is performed.
1754 1757
1755 1758 This command takes effect with the next commit. To undo a copy
1756 1759 before that, see :hg:`revert`.
1757 1760
1758 1761 Returns 0 on success, 1 if errors are encountered.
1759 1762 """
1760 1763 opts = pycompat.byteskwargs(opts)
1761 1764 with repo.wlock(False):
1762 1765 return cmdutil.copy(ui, repo, pats, opts)
1763 1766
1764 1767 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1765 1768 def debugcommands(ui, cmd='', *args):
1766 1769 """list all available commands and options"""
1767 1770 for cmd, vals in sorted(table.iteritems()):
1768 1771 cmd = cmd.split('|')[0].strip('^')
1769 1772 opts = ', '.join([i[1] for i in vals[1]])
1770 1773 ui.write('%s: %s\n' % (cmd, opts))
1771 1774
1772 1775 @command('debugcomplete',
1773 1776 [('o', 'options', None, _('show the command options'))],
1774 1777 _('[-o] CMD'),
1775 1778 norepo=True)
1776 1779 def debugcomplete(ui, cmd='', **opts):
1777 1780 """returns the completion list associated with the given command"""
1778 1781
1779 1782 if opts.get(r'options'):
1780 1783 options = []
1781 1784 otables = [globalopts]
1782 1785 if cmd:
1783 1786 aliases, entry = cmdutil.findcmd(cmd, table, False)
1784 1787 otables.append(entry[1])
1785 1788 for t in otables:
1786 1789 for o in t:
1787 1790 if "(DEPRECATED)" in o[3]:
1788 1791 continue
1789 1792 if o[0]:
1790 1793 options.append('-%s' % o[0])
1791 1794 options.append('--%s' % o[1])
1792 1795 ui.write("%s\n" % "\n".join(options))
1793 1796 return
1794 1797
1795 1798 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1796 1799 if ui.verbose:
1797 1800 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1798 1801 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1799 1802
1800 1803 @command('^diff',
1801 1804 [('r', 'rev', [], _('revision'), _('REV')),
1802 1805 ('c', 'change', '', _('change made by revision'), _('REV'))
1803 1806 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1804 1807 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1805 inferrepo=True, cmdtype=readonly)
1808 inferrepo=True,
1809 intents={INTENT_READONLY})
1806 1810 def diff(ui, repo, *pats, **opts):
1807 1811 """diff repository (or selected files)
1808 1812
1809 1813 Show differences between revisions for the specified files.
1810 1814
1811 1815 Differences between files are shown using the unified diff format.
1812 1816
1813 1817 .. note::
1814 1818
1815 1819 :hg:`diff` may generate unexpected results for merges, as it will
1816 1820 default to comparing against the working directory's first
1817 1821 parent changeset if no revisions are specified.
1818 1822
1819 1823 When two revision arguments are given, then changes are shown
1820 1824 between those revisions. If only one revision is specified then
1821 1825 that revision is compared to the working directory, and, when no
1822 1826 revisions are specified, the working directory files are compared
1823 1827 to its first parent.
1824 1828
1825 1829 Alternatively you can specify -c/--change with a revision to see
1826 1830 the changes in that changeset relative to its first parent.
1827 1831
1828 1832 Without the -a/--text option, diff will avoid generating diffs of
1829 1833 files it detects as binary. With -a, diff will generate a diff
1830 1834 anyway, probably with undesirable results.
1831 1835
1832 1836 Use the -g/--git option to generate diffs in the git extended diff
1833 1837 format. For more information, read :hg:`help diffs`.
1834 1838
1835 1839 .. container:: verbose
1836 1840
1837 1841 Examples:
1838 1842
1839 1843 - compare a file in the current working directory to its parent::
1840 1844
1841 1845 hg diff foo.c
1842 1846
1843 1847 - compare two historical versions of a directory, with rename info::
1844 1848
1845 1849 hg diff --git -r 1.0:1.2 lib/
1846 1850
1847 1851 - get change stats relative to the last change on some date::
1848 1852
1849 1853 hg diff --stat -r "date('may 2')"
1850 1854
1851 1855 - diff all newly-added files that contain a keyword::
1852 1856
1853 1857 hg diff "set:added() and grep(GNU)"
1854 1858
1855 1859 - compare a revision and its parents::
1856 1860
1857 1861 hg diff -c 9353 # compare against first parent
1858 1862 hg diff -r 9353^:9353 # same using revset syntax
1859 1863 hg diff -r 9353^2:9353 # compare against the second parent
1860 1864
1861 1865 Returns 0 on success.
1862 1866 """
1863 1867
1864 1868 opts = pycompat.byteskwargs(opts)
1865 1869 revs = opts.get('rev')
1866 1870 change = opts.get('change')
1867 1871 stat = opts.get('stat')
1868 1872 reverse = opts.get('reverse')
1869 1873
1870 1874 if revs and change:
1871 1875 msg = _('cannot specify --rev and --change at the same time')
1872 1876 raise error.Abort(msg)
1873 1877 elif change:
1874 1878 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
1875 1879 ctx2 = scmutil.revsingle(repo, change, None)
1876 1880 ctx1 = ctx2.p1()
1877 1881 else:
1878 1882 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
1879 1883 ctx1, ctx2 = scmutil.revpair(repo, revs)
1880 1884 node1, node2 = ctx1.node(), ctx2.node()
1881 1885
1882 1886 if reverse:
1883 1887 node1, node2 = node2, node1
1884 1888
1885 1889 diffopts = patch.diffallopts(ui, opts)
1886 1890 m = scmutil.match(ctx2, pats, opts)
1887 1891 ui.pager('diff')
1888 1892 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1889 1893 listsubrepos=opts.get('subrepos'),
1890 1894 root=opts.get('root'))
1891 1895
1892 1896 @command('^export',
1893 1897 [('o', 'output', '',
1894 1898 _('print output to file with formatted name'), _('FORMAT')),
1895 1899 ('', 'switch-parent', None, _('diff against the second parent')),
1896 1900 ('r', 'rev', [], _('revisions to export'), _('REV')),
1897 1901 ] + diffopts + formatteropts,
1898 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'), cmdtype=readonly)
1902 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
1903 intents={INTENT_READONLY})
1899 1904 def export(ui, repo, *changesets, **opts):
1900 1905 """dump the header and diffs for one or more changesets
1901 1906
1902 1907 Print the changeset header and diffs for one or more revisions.
1903 1908 If no revision is given, the parent of the working directory is used.
1904 1909
1905 1910 The information shown in the changeset header is: author, date,
1906 1911 branch name (if non-default), changeset hash, parent(s) and commit
1907 1912 comment.
1908 1913
1909 1914 .. note::
1910 1915
1911 1916 :hg:`export` may generate unexpected diff output for merge
1912 1917 changesets, as it will compare the merge changeset against its
1913 1918 first parent only.
1914 1919
1915 1920 Output may be to a file, in which case the name of the file is
1916 1921 given using a template string. See :hg:`help templates`. In addition
1917 1922 to the common template keywords, the following formatting rules are
1918 1923 supported:
1919 1924
1920 1925 :``%%``: literal "%" character
1921 1926 :``%H``: changeset hash (40 hexadecimal digits)
1922 1927 :``%N``: number of patches being generated
1923 1928 :``%R``: changeset revision number
1924 1929 :``%b``: basename of the exporting repository
1925 1930 :``%h``: short-form changeset hash (12 hexadecimal digits)
1926 1931 :``%m``: first line of the commit message (only alphanumeric characters)
1927 1932 :``%n``: zero-padded sequence number, starting at 1
1928 1933 :``%r``: zero-padded changeset revision number
1929 1934 :``\\``: literal "\\" character
1930 1935
1931 1936 Without the -a/--text option, export will avoid generating diffs
1932 1937 of files it detects as binary. With -a, export will generate a
1933 1938 diff anyway, probably with undesirable results.
1934 1939
1935 1940 Use the -g/--git option to generate diffs in the git extended diff
1936 1941 format. See :hg:`help diffs` for more information.
1937 1942
1938 1943 With the --switch-parent option, the diff will be against the
1939 1944 second parent. It can be useful to review a merge.
1940 1945
1941 1946 .. container:: verbose
1942 1947
1943 1948 Examples:
1944 1949
1945 1950 - use export and import to transplant a bugfix to the current
1946 1951 branch::
1947 1952
1948 1953 hg export -r 9353 | hg import -
1949 1954
1950 1955 - export all the changesets between two revisions to a file with
1951 1956 rename information::
1952 1957
1953 1958 hg export --git -r 123:150 > changes.txt
1954 1959
1955 1960 - split outgoing changes into a series of patches with
1956 1961 descriptive names::
1957 1962
1958 1963 hg export -r "outgoing()" -o "%n-%m.patch"
1959 1964
1960 1965 Returns 0 on success.
1961 1966 """
1962 1967 opts = pycompat.byteskwargs(opts)
1963 1968 changesets += tuple(opts.get('rev', []))
1964 1969 if not changesets:
1965 1970 changesets = ['.']
1966 1971 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
1967 1972 revs = scmutil.revrange(repo, changesets)
1968 1973 if not revs:
1969 1974 raise error.Abort(_("export requires at least one changeset"))
1970 1975 if len(revs) > 1:
1971 1976 ui.note(_('exporting patches:\n'))
1972 1977 else:
1973 1978 ui.note(_('exporting patch:\n'))
1974 1979
1975 1980 fntemplate = opts.get('output')
1976 1981 if cmdutil.isstdiofilename(fntemplate):
1977 1982 fntemplate = ''
1978 1983
1979 1984 if fntemplate:
1980 1985 fm = formatter.nullformatter(ui, 'export', opts)
1981 1986 else:
1982 1987 ui.pager('export')
1983 1988 fm = ui.formatter('export', opts)
1984 1989 with fm:
1985 1990 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
1986 1991 switch_parent=opts.get('switch_parent'),
1987 1992 opts=patch.diffallopts(ui, opts))
1988 1993
1989 1994 @command('files',
1990 1995 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
1991 1996 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
1992 1997 ] + walkopts + formatteropts + subrepoopts,
1993 _('[OPTION]... [FILE]...'), cmdtype=readonly)
1998 _('[OPTION]... [FILE]...'),
1999 intents={INTENT_READONLY})
1994 2000 def files(ui, repo, *pats, **opts):
1995 2001 """list tracked files
1996 2002
1997 2003 Print files under Mercurial control in the working directory or
1998 2004 specified revision for given files (excluding removed files).
1999 2005 Files can be specified as filenames or filesets.
2000 2006
2001 2007 If no files are given to match, this command prints the names
2002 2008 of all files under Mercurial control.
2003 2009
2004 2010 .. container:: verbose
2005 2011
2006 2012 Examples:
2007 2013
2008 2014 - list all files under the current directory::
2009 2015
2010 2016 hg files .
2011 2017
2012 2018 - shows sizes and flags for current revision::
2013 2019
2014 2020 hg files -vr .
2015 2021
2016 2022 - list all files named README::
2017 2023
2018 2024 hg files -I "**/README"
2019 2025
2020 2026 - list all binary files::
2021 2027
2022 2028 hg files "set:binary()"
2023 2029
2024 2030 - find files containing a regular expression::
2025 2031
2026 2032 hg files "set:grep('bob')"
2027 2033
2028 2034 - search tracked file contents with xargs and grep::
2029 2035
2030 2036 hg files -0 | xargs -0 grep foo
2031 2037
2032 2038 See :hg:`help patterns` and :hg:`help filesets` for more information
2033 2039 on specifying file patterns.
2034 2040
2035 2041 Returns 0 if a match is found, 1 otherwise.
2036 2042
2037 2043 """
2038 2044
2039 2045 opts = pycompat.byteskwargs(opts)
2040 2046 rev = opts.get('rev')
2041 2047 if rev:
2042 2048 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2043 2049 ctx = scmutil.revsingle(repo, rev, None)
2044 2050
2045 2051 end = '\n'
2046 2052 if opts.get('print0'):
2047 2053 end = '\0'
2048 2054 fmt = '%s' + end
2049 2055
2050 2056 m = scmutil.match(ctx, pats, opts)
2051 2057 ui.pager('files')
2052 2058 with ui.formatter('files', opts) as fm:
2053 2059 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2054 2060
2055 2061 @command(
2056 2062 '^forget',
2057 2063 walkopts + dryrunopts,
2058 2064 _('[OPTION]... FILE...'), inferrepo=True)
2059 2065 def forget(ui, repo, *pats, **opts):
2060 2066 """forget the specified files on the next commit
2061 2067
2062 2068 Mark the specified files so they will no longer be tracked
2063 2069 after the next commit.
2064 2070
2065 2071 This only removes files from the current branch, not from the
2066 2072 entire project history, and it does not delete them from the
2067 2073 working directory.
2068 2074
2069 2075 To delete the file from the working directory, see :hg:`remove`.
2070 2076
2071 2077 To undo a forget before the next commit, see :hg:`add`.
2072 2078
2073 2079 .. container:: verbose
2074 2080
2075 2081 Examples:
2076 2082
2077 2083 - forget newly-added binary files::
2078 2084
2079 2085 hg forget "set:added() and binary()"
2080 2086
2081 2087 - forget files that would be excluded by .hgignore::
2082 2088
2083 2089 hg forget "set:hgignore()"
2084 2090
2085 2091 Returns 0 on success.
2086 2092 """
2087 2093
2088 2094 opts = pycompat.byteskwargs(opts)
2089 2095 if not pats:
2090 2096 raise error.Abort(_('no files specified'))
2091 2097
2092 2098 m = scmutil.match(repo[None], pats, opts)
2093 2099 dryrun = opts.get('dry_run')
2094 2100 rejected = cmdutil.forget(ui, repo, m, prefix="",
2095 2101 explicitonly=False, dryrun=dryrun)[0]
2096 2102 return rejected and 1 or 0
2097 2103
2098 2104 @command(
2099 2105 'graft',
2100 2106 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2101 2107 ('c', 'continue', False, _('resume interrupted graft')),
2102 2108 ('e', 'edit', False, _('invoke editor on commit messages')),
2103 2109 ('', 'log', None, _('append graft info to log message')),
2104 2110 ('f', 'force', False, _('force graft')),
2105 2111 ('D', 'currentdate', False,
2106 2112 _('record the current date as commit date')),
2107 2113 ('U', 'currentuser', False,
2108 2114 _('record the current user as committer'), _('DATE'))]
2109 2115 + commitopts2 + mergetoolopts + dryrunopts,
2110 2116 _('[OPTION]... [-r REV]... REV...'))
2111 2117 def graft(ui, repo, *revs, **opts):
2112 2118 '''copy changes from other branches onto the current branch
2113 2119
2114 2120 This command uses Mercurial's merge logic to copy individual
2115 2121 changes from other branches without merging branches in the
2116 2122 history graph. This is sometimes known as 'backporting' or
2117 2123 'cherry-picking'. By default, graft will copy user, date, and
2118 2124 description from the source changesets.
2119 2125
2120 2126 Changesets that are ancestors of the current revision, that have
2121 2127 already been grafted, or that are merges will be skipped.
2122 2128
2123 2129 If --log is specified, log messages will have a comment appended
2124 2130 of the form::
2125 2131
2126 2132 (grafted from CHANGESETHASH)
2127 2133
2128 2134 If --force is specified, revisions will be grafted even if they
2129 2135 are already ancestors of, or have been grafted to, the destination.
2130 2136 This is useful when the revisions have since been backed out.
2131 2137
2132 2138 If a graft merge results in conflicts, the graft process is
2133 2139 interrupted so that the current merge can be manually resolved.
2134 2140 Once all conflicts are addressed, the graft process can be
2135 2141 continued with the -c/--continue option.
2136 2142
2137 2143 .. note::
2138 2144
2139 2145 The -c/--continue option does not reapply earlier options, except
2140 2146 for --force.
2141 2147
2142 2148 .. container:: verbose
2143 2149
2144 2150 Examples:
2145 2151
2146 2152 - copy a single change to the stable branch and edit its description::
2147 2153
2148 2154 hg update stable
2149 2155 hg graft --edit 9393
2150 2156
2151 2157 - graft a range of changesets with one exception, updating dates::
2152 2158
2153 2159 hg graft -D "2085::2093 and not 2091"
2154 2160
2155 2161 - continue a graft after resolving conflicts::
2156 2162
2157 2163 hg graft -c
2158 2164
2159 2165 - show the source of a grafted changeset::
2160 2166
2161 2167 hg log --debug -r .
2162 2168
2163 2169 - show revisions sorted by date::
2164 2170
2165 2171 hg log -r "sort(all(), date)"
2166 2172
2167 2173 See :hg:`help revisions` for more about specifying revisions.
2168 2174
2169 2175 Returns 0 on successful completion.
2170 2176 '''
2171 2177 with repo.wlock():
2172 2178 return _dograft(ui, repo, *revs, **opts)
2173 2179
2174 2180 def _dograft(ui, repo, *revs, **opts):
2175 2181 opts = pycompat.byteskwargs(opts)
2176 2182 if revs and opts.get('rev'):
2177 2183 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2178 2184 'revision ordering!\n'))
2179 2185
2180 2186 revs = list(revs)
2181 2187 revs.extend(opts.get('rev'))
2182 2188
2183 2189 if not opts.get('user') and opts.get('currentuser'):
2184 2190 opts['user'] = ui.username()
2185 2191 if not opts.get('date') and opts.get('currentdate'):
2186 2192 opts['date'] = "%d %d" % dateutil.makedate()
2187 2193
2188 2194 editor = cmdutil.getcommiteditor(editform='graft',
2189 2195 **pycompat.strkwargs(opts))
2190 2196
2191 2197 cont = False
2192 2198 if opts.get('continue'):
2193 2199 cont = True
2194 2200 if revs:
2195 2201 raise error.Abort(_("can't specify --continue and revisions"))
2196 2202 # read in unfinished revisions
2197 2203 try:
2198 2204 nodes = repo.vfs.read('graftstate').splitlines()
2199 2205 revs = [repo[node].rev() for node in nodes]
2200 2206 except IOError as inst:
2201 2207 if inst.errno != errno.ENOENT:
2202 2208 raise
2203 2209 cmdutil.wrongtooltocontinue(repo, _('graft'))
2204 2210 else:
2205 2211 if not revs:
2206 2212 raise error.Abort(_('no revisions specified'))
2207 2213 cmdutil.checkunfinished(repo)
2208 2214 cmdutil.bailifchanged(repo)
2209 2215 revs = scmutil.revrange(repo, revs)
2210 2216
2211 2217 skipped = set()
2212 2218 # check for merges
2213 2219 for rev in repo.revs('%ld and merge()', revs):
2214 2220 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2215 2221 skipped.add(rev)
2216 2222 revs = [r for r in revs if r not in skipped]
2217 2223 if not revs:
2218 2224 return -1
2219 2225
2220 2226 # Don't check in the --continue case, in effect retaining --force across
2221 2227 # --continues. That's because without --force, any revisions we decided to
2222 2228 # skip would have been filtered out here, so they wouldn't have made their
2223 2229 # way to the graftstate. With --force, any revisions we would have otherwise
2224 2230 # skipped would not have been filtered out, and if they hadn't been applied
2225 2231 # already, they'd have been in the graftstate.
2226 2232 if not (cont or opts.get('force')):
2227 2233 # check for ancestors of dest branch
2228 2234 crev = repo['.'].rev()
2229 2235 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2230 2236 # XXX make this lazy in the future
2231 2237 # don't mutate while iterating, create a copy
2232 2238 for rev in list(revs):
2233 2239 if rev in ancestors:
2234 2240 ui.warn(_('skipping ancestor revision %d:%s\n') %
2235 2241 (rev, repo[rev]))
2236 2242 # XXX remove on list is slow
2237 2243 revs.remove(rev)
2238 2244 if not revs:
2239 2245 return -1
2240 2246
2241 2247 # analyze revs for earlier grafts
2242 2248 ids = {}
2243 2249 for ctx in repo.set("%ld", revs):
2244 2250 ids[ctx.hex()] = ctx.rev()
2245 2251 n = ctx.extra().get('source')
2246 2252 if n:
2247 2253 ids[n] = ctx.rev()
2248 2254
2249 2255 # check ancestors for earlier grafts
2250 2256 ui.debug('scanning for duplicate grafts\n')
2251 2257
2252 2258 # The only changesets we can be sure doesn't contain grafts of any
2253 2259 # revs, are the ones that are common ancestors of *all* revs:
2254 2260 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2255 2261 ctx = repo[rev]
2256 2262 n = ctx.extra().get('source')
2257 2263 if n in ids:
2258 2264 try:
2259 2265 r = repo[n].rev()
2260 2266 except error.RepoLookupError:
2261 2267 r = None
2262 2268 if r in revs:
2263 2269 ui.warn(_('skipping revision %d:%s '
2264 2270 '(already grafted to %d:%s)\n')
2265 2271 % (r, repo[r], rev, ctx))
2266 2272 revs.remove(r)
2267 2273 elif ids[n] in revs:
2268 2274 if r is None:
2269 2275 ui.warn(_('skipping already grafted revision %d:%s '
2270 2276 '(%d:%s also has unknown origin %s)\n')
2271 2277 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2272 2278 else:
2273 2279 ui.warn(_('skipping already grafted revision %d:%s '
2274 2280 '(%d:%s also has origin %d:%s)\n')
2275 2281 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2276 2282 revs.remove(ids[n])
2277 2283 elif ctx.hex() in ids:
2278 2284 r = ids[ctx.hex()]
2279 2285 ui.warn(_('skipping already grafted revision %d:%s '
2280 2286 '(was grafted from %d:%s)\n') %
2281 2287 (r, repo[r], rev, ctx))
2282 2288 revs.remove(r)
2283 2289 if not revs:
2284 2290 return -1
2285 2291
2286 2292 for pos, ctx in enumerate(repo.set("%ld", revs)):
2287 2293 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2288 2294 ctx.description().split('\n', 1)[0])
2289 2295 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2290 2296 if names:
2291 2297 desc += ' (%s)' % ' '.join(names)
2292 2298 ui.status(_('grafting %s\n') % desc)
2293 2299 if opts.get('dry_run'):
2294 2300 continue
2295 2301
2296 2302 source = ctx.extra().get('source')
2297 2303 extra = {}
2298 2304 if source:
2299 2305 extra['source'] = source
2300 2306 extra['intermediate-source'] = ctx.hex()
2301 2307 else:
2302 2308 extra['source'] = ctx.hex()
2303 2309 user = ctx.user()
2304 2310 if opts.get('user'):
2305 2311 user = opts['user']
2306 2312 date = ctx.date()
2307 2313 if opts.get('date'):
2308 2314 date = opts['date']
2309 2315 message = ctx.description()
2310 2316 if opts.get('log'):
2311 2317 message += '\n(grafted from %s)' % ctx.hex()
2312 2318
2313 2319 # we don't merge the first commit when continuing
2314 2320 if not cont:
2315 2321 # perform the graft merge with p1(rev) as 'ancestor'
2316 2322 try:
2317 2323 # ui.forcemerge is an internal variable, do not document
2318 2324 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2319 2325 'graft')
2320 2326 stats = mergemod.graft(repo, ctx, ctx.p1(),
2321 2327 ['local', 'graft'])
2322 2328 finally:
2323 2329 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2324 2330 # report any conflicts
2325 2331 if stats.unresolvedcount > 0:
2326 2332 # write out state for --continue
2327 2333 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2328 2334 repo.vfs.write('graftstate', ''.join(nodelines))
2329 2335 extra = ''
2330 2336 if opts.get('user'):
2331 2337 extra += ' --user %s' % procutil.shellquote(opts['user'])
2332 2338 if opts.get('date'):
2333 2339 extra += ' --date %s' % procutil.shellquote(opts['date'])
2334 2340 if opts.get('log'):
2335 2341 extra += ' --log'
2336 2342 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2337 2343 raise error.Abort(
2338 2344 _("unresolved conflicts, can't continue"),
2339 2345 hint=hint)
2340 2346 else:
2341 2347 cont = False
2342 2348
2343 2349 # commit
2344 2350 node = repo.commit(text=message, user=user,
2345 2351 date=date, extra=extra, editor=editor)
2346 2352 if node is None:
2347 2353 ui.warn(
2348 2354 _('note: graft of %d:%s created no changes to commit\n') %
2349 2355 (ctx.rev(), ctx))
2350 2356
2351 2357 # remove state when we complete successfully
2352 2358 if not opts.get('dry_run'):
2353 2359 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2354 2360
2355 2361 return 0
2356 2362
2357 2363 @command('grep',
2358 2364 [('0', 'print0', None, _('end fields with NUL')),
2359 2365 ('', 'all', None, _('print all revisions that match')),
2360 2366 ('a', 'text', None, _('treat all files as text')),
2361 2367 ('f', 'follow', None,
2362 2368 _('follow changeset history,'
2363 2369 ' or file history across copies and renames')),
2364 2370 ('i', 'ignore-case', None, _('ignore case when matching')),
2365 2371 ('l', 'files-with-matches', None,
2366 2372 _('print only filenames and revisions that match')),
2367 2373 ('n', 'line-number', None, _('print matching line numbers')),
2368 2374 ('r', 'rev', [],
2369 2375 _('only search files changed within revision range'), _('REV')),
2370 2376 ('u', 'user', None, _('list the author (long with -v)')),
2371 2377 ('d', 'date', None, _('list the date (short with -q)')),
2372 2378 ] + formatteropts + walkopts,
2373 2379 _('[OPTION]... PATTERN [FILE]...'),
2374 inferrepo=True, cmdtype=readonly)
2380 inferrepo=True,
2381 intents={INTENT_READONLY})
2375 2382 def grep(ui, repo, pattern, *pats, **opts):
2376 2383 """search revision history for a pattern in specified files
2377 2384
2378 2385 Search revision history for a regular expression in the specified
2379 2386 files or the entire project.
2380 2387
2381 2388 By default, grep prints the most recent revision number for each
2382 2389 file in which it finds a match. To get it to print every revision
2383 2390 that contains a change in match status ("-" for a match that becomes
2384 2391 a non-match, or "+" for a non-match that becomes a match), use the
2385 2392 --all flag.
2386 2393
2387 2394 PATTERN can be any Python (roughly Perl-compatible) regular
2388 2395 expression.
2389 2396
2390 2397 If no FILEs are specified (and -f/--follow isn't set), all files in
2391 2398 the repository are searched, including those that don't exist in the
2392 2399 current branch or have been deleted in a prior changeset.
2393 2400
2394 2401 Returns 0 if a match is found, 1 otherwise.
2395 2402 """
2396 2403 opts = pycompat.byteskwargs(opts)
2397 2404 reflags = re.M
2398 2405 if opts.get('ignore_case'):
2399 2406 reflags |= re.I
2400 2407 try:
2401 2408 regexp = util.re.compile(pattern, reflags)
2402 2409 except re.error as inst:
2403 2410 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2404 2411 return 1
2405 2412 sep, eol = ':', '\n'
2406 2413 if opts.get('print0'):
2407 2414 sep = eol = '\0'
2408 2415
2409 2416 getfile = util.lrucachefunc(repo.file)
2410 2417
2411 2418 def matchlines(body):
2412 2419 begin = 0
2413 2420 linenum = 0
2414 2421 while begin < len(body):
2415 2422 match = regexp.search(body, begin)
2416 2423 if not match:
2417 2424 break
2418 2425 mstart, mend = match.span()
2419 2426 linenum += body.count('\n', begin, mstart) + 1
2420 2427 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2421 2428 begin = body.find('\n', mend) + 1 or len(body) + 1
2422 2429 lend = begin - 1
2423 2430 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2424 2431
2425 2432 class linestate(object):
2426 2433 def __init__(self, line, linenum, colstart, colend):
2427 2434 self.line = line
2428 2435 self.linenum = linenum
2429 2436 self.colstart = colstart
2430 2437 self.colend = colend
2431 2438
2432 2439 def __hash__(self):
2433 2440 return hash((self.linenum, self.line))
2434 2441
2435 2442 def __eq__(self, other):
2436 2443 return self.line == other.line
2437 2444
2438 2445 def findpos(self):
2439 2446 """Iterate all (start, end) indices of matches"""
2440 2447 yield self.colstart, self.colend
2441 2448 p = self.colend
2442 2449 while p < len(self.line):
2443 2450 m = regexp.search(self.line, p)
2444 2451 if not m:
2445 2452 break
2446 2453 yield m.span()
2447 2454 p = m.end()
2448 2455
2449 2456 matches = {}
2450 2457 copies = {}
2451 2458 def grepbody(fn, rev, body):
2452 2459 matches[rev].setdefault(fn, [])
2453 2460 m = matches[rev][fn]
2454 2461 for lnum, cstart, cend, line in matchlines(body):
2455 2462 s = linestate(line, lnum, cstart, cend)
2456 2463 m.append(s)
2457 2464
2458 2465 def difflinestates(a, b):
2459 2466 sm = difflib.SequenceMatcher(None, a, b)
2460 2467 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2461 2468 if tag == 'insert':
2462 2469 for i in xrange(blo, bhi):
2463 2470 yield ('+', b[i])
2464 2471 elif tag == 'delete':
2465 2472 for i in xrange(alo, ahi):
2466 2473 yield ('-', a[i])
2467 2474 elif tag == 'replace':
2468 2475 for i in xrange(alo, ahi):
2469 2476 yield ('-', a[i])
2470 2477 for i in xrange(blo, bhi):
2471 2478 yield ('+', b[i])
2472 2479
2473 2480 def display(fm, fn, ctx, pstates, states):
2474 2481 rev = ctx.rev()
2475 2482 if fm.isplain():
2476 2483 formatuser = ui.shortuser
2477 2484 else:
2478 2485 formatuser = str
2479 2486 if ui.quiet:
2480 2487 datefmt = '%Y-%m-%d'
2481 2488 else:
2482 2489 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2483 2490 found = False
2484 2491 @util.cachefunc
2485 2492 def binary():
2486 2493 flog = getfile(fn)
2487 2494 return stringutil.binary(flog.read(ctx.filenode(fn)))
2488 2495
2489 2496 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2490 2497 if opts.get('all'):
2491 2498 iter = difflinestates(pstates, states)
2492 2499 else:
2493 2500 iter = [('', l) for l in states]
2494 2501 for change, l in iter:
2495 2502 fm.startitem()
2496 2503 fm.data(node=fm.hexfunc(ctx.node()))
2497 2504 cols = [
2498 2505 ('filename', fn, True),
2499 2506 ('rev', rev, True),
2500 2507 ('linenumber', l.linenum, opts.get('line_number')),
2501 2508 ]
2502 2509 if opts.get('all'):
2503 2510 cols.append(('change', change, True))
2504 2511 cols.extend([
2505 2512 ('user', formatuser(ctx.user()), opts.get('user')),
2506 2513 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2507 2514 ])
2508 2515 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2509 2516 for name, data, cond in cols:
2510 2517 field = fieldnamemap.get(name, name)
2511 2518 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2512 2519 if cond and name != lastcol:
2513 2520 fm.plain(sep, label='grep.sep')
2514 2521 if not opts.get('files_with_matches'):
2515 2522 fm.plain(sep, label='grep.sep')
2516 2523 if not opts.get('text') and binary():
2517 2524 fm.plain(_(" Binary file matches"))
2518 2525 else:
2519 2526 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2520 2527 fm.plain(eol)
2521 2528 found = True
2522 2529 if opts.get('files_with_matches'):
2523 2530 break
2524 2531 return found
2525 2532
2526 2533 def displaymatches(fm, l):
2527 2534 p = 0
2528 2535 for s, e in l.findpos():
2529 2536 if p < s:
2530 2537 fm.startitem()
2531 2538 fm.write('text', '%s', l.line[p:s])
2532 2539 fm.data(matched=False)
2533 2540 fm.startitem()
2534 2541 fm.write('text', '%s', l.line[s:e], label='grep.match')
2535 2542 fm.data(matched=True)
2536 2543 p = e
2537 2544 if p < len(l.line):
2538 2545 fm.startitem()
2539 2546 fm.write('text', '%s', l.line[p:])
2540 2547 fm.data(matched=False)
2541 2548 fm.end()
2542 2549
2543 2550 skip = {}
2544 2551 revfiles = {}
2545 2552 match = scmutil.match(repo[None], pats, opts)
2546 2553 found = False
2547 2554 follow = opts.get('follow')
2548 2555
2549 2556 def prep(ctx, fns):
2550 2557 rev = ctx.rev()
2551 2558 pctx = ctx.p1()
2552 2559 parent = pctx.rev()
2553 2560 matches.setdefault(rev, {})
2554 2561 matches.setdefault(parent, {})
2555 2562 files = revfiles.setdefault(rev, [])
2556 2563 for fn in fns:
2557 2564 flog = getfile(fn)
2558 2565 try:
2559 2566 fnode = ctx.filenode(fn)
2560 2567 except error.LookupError:
2561 2568 continue
2562 2569
2563 2570 copied = flog.renamed(fnode)
2564 2571 copy = follow and copied and copied[0]
2565 2572 if copy:
2566 2573 copies.setdefault(rev, {})[fn] = copy
2567 2574 if fn in skip:
2568 2575 if copy:
2569 2576 skip[copy] = True
2570 2577 continue
2571 2578 files.append(fn)
2572 2579
2573 2580 if fn not in matches[rev]:
2574 2581 grepbody(fn, rev, flog.read(fnode))
2575 2582
2576 2583 pfn = copy or fn
2577 2584 if pfn not in matches[parent]:
2578 2585 try:
2579 2586 fnode = pctx.filenode(pfn)
2580 2587 grepbody(pfn, parent, flog.read(fnode))
2581 2588 except error.LookupError:
2582 2589 pass
2583 2590
2584 2591 ui.pager('grep')
2585 2592 fm = ui.formatter('grep', opts)
2586 2593 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2587 2594 rev = ctx.rev()
2588 2595 parent = ctx.p1().rev()
2589 2596 for fn in sorted(revfiles.get(rev, [])):
2590 2597 states = matches[rev][fn]
2591 2598 copy = copies.get(rev, {}).get(fn)
2592 2599 if fn in skip:
2593 2600 if copy:
2594 2601 skip[copy] = True
2595 2602 continue
2596 2603 pstates = matches.get(parent, {}).get(copy or fn, [])
2597 2604 if pstates or states:
2598 2605 r = display(fm, fn, ctx, pstates, states)
2599 2606 found = found or r
2600 2607 if r and not opts.get('all'):
2601 2608 skip[fn] = True
2602 2609 if copy:
2603 2610 skip[copy] = True
2604 2611 del revfiles[rev]
2605 2612 # We will keep the matches dict for the duration of the window
2606 2613 # clear the matches dict once the window is over
2607 2614 if not revfiles:
2608 2615 matches.clear()
2609 2616 fm.end()
2610 2617
2611 2618 return not found
2612 2619
2613 2620 @command('heads',
2614 2621 [('r', 'rev', '',
2615 2622 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2616 2623 ('t', 'topo', False, _('show topological heads only')),
2617 2624 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2618 2625 ('c', 'closed', False, _('show normal and closed branch heads')),
2619 2626 ] + templateopts,
2620 _('[-ct] [-r STARTREV] [REV]...'), cmdtype=readonly)
2627 _('[-ct] [-r STARTREV] [REV]...'),
2628 intents={INTENT_READONLY})
2621 2629 def heads(ui, repo, *branchrevs, **opts):
2622 2630 """show branch heads
2623 2631
2624 2632 With no arguments, show all open branch heads in the repository.
2625 2633 Branch heads are changesets that have no descendants on the
2626 2634 same branch. They are where development generally takes place and
2627 2635 are the usual targets for update and merge operations.
2628 2636
2629 2637 If one or more REVs are given, only open branch heads on the
2630 2638 branches associated with the specified changesets are shown. This
2631 2639 means that you can use :hg:`heads .` to see the heads on the
2632 2640 currently checked-out branch.
2633 2641
2634 2642 If -c/--closed is specified, also show branch heads marked closed
2635 2643 (see :hg:`commit --close-branch`).
2636 2644
2637 2645 If STARTREV is specified, only those heads that are descendants of
2638 2646 STARTREV will be displayed.
2639 2647
2640 2648 If -t/--topo is specified, named branch mechanics will be ignored and only
2641 2649 topological heads (changesets with no children) will be shown.
2642 2650
2643 2651 Returns 0 if matching heads are found, 1 if not.
2644 2652 """
2645 2653
2646 2654 opts = pycompat.byteskwargs(opts)
2647 2655 start = None
2648 2656 rev = opts.get('rev')
2649 2657 if rev:
2650 2658 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2651 2659 start = scmutil.revsingle(repo, rev, None).node()
2652 2660
2653 2661 if opts.get('topo'):
2654 2662 heads = [repo[h] for h in repo.heads(start)]
2655 2663 else:
2656 2664 heads = []
2657 2665 for branch in repo.branchmap():
2658 2666 heads += repo.branchheads(branch, start, opts.get('closed'))
2659 2667 heads = [repo[h] for h in heads]
2660 2668
2661 2669 if branchrevs:
2662 2670 branches = set(repo[r].branch()
2663 2671 for r in scmutil.revrange(repo, branchrevs))
2664 2672 heads = [h for h in heads if h.branch() in branches]
2665 2673
2666 2674 if opts.get('active') and branchrevs:
2667 2675 dagheads = repo.heads(start)
2668 2676 heads = [h for h in heads if h.node() in dagheads]
2669 2677
2670 2678 if branchrevs:
2671 2679 haveheads = set(h.branch() for h in heads)
2672 2680 if branches - haveheads:
2673 2681 headless = ', '.join(b for b in branches - haveheads)
2674 2682 msg = _('no open branch heads found on branches %s')
2675 2683 if opts.get('rev'):
2676 2684 msg += _(' (started at %s)') % opts['rev']
2677 2685 ui.warn((msg + '\n') % headless)
2678 2686
2679 2687 if not heads:
2680 2688 return 1
2681 2689
2682 2690 ui.pager('heads')
2683 2691 heads = sorted(heads, key=lambda x: -x.rev())
2684 2692 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
2685 2693 for ctx in heads:
2686 2694 displayer.show(ctx)
2687 2695 displayer.close()
2688 2696
2689 2697 @command('help',
2690 2698 [('e', 'extension', None, _('show only help for extensions')),
2691 2699 ('c', 'command', None, _('show only help for commands')),
2692 2700 ('k', 'keyword', None, _('show topics matching keyword')),
2693 2701 ('s', 'system', [], _('show help for specific platform(s)')),
2694 2702 ],
2695 2703 _('[-ecks] [TOPIC]'),
2696 norepo=True, cmdtype=readonly)
2704 norepo=True,
2705 intents={INTENT_READONLY})
2697 2706 def help_(ui, name=None, **opts):
2698 2707 """show help for a given topic or a help overview
2699 2708
2700 2709 With no arguments, print a list of commands with short help messages.
2701 2710
2702 2711 Given a topic, extension, or command name, print help for that
2703 2712 topic.
2704 2713
2705 2714 Returns 0 if successful.
2706 2715 """
2707 2716
2708 2717 keep = opts.get(r'system') or []
2709 2718 if len(keep) == 0:
2710 2719 if pycompat.sysplatform.startswith('win'):
2711 2720 keep.append('windows')
2712 2721 elif pycompat.sysplatform == 'OpenVMS':
2713 2722 keep.append('vms')
2714 2723 elif pycompat.sysplatform == 'plan9':
2715 2724 keep.append('plan9')
2716 2725 else:
2717 2726 keep.append('unix')
2718 2727 keep.append(pycompat.sysplatform.lower())
2719 2728 if ui.verbose:
2720 2729 keep.append('verbose')
2721 2730
2722 2731 commands = sys.modules[__name__]
2723 2732 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
2724 2733 ui.pager('help')
2725 2734 ui.write(formatted)
2726 2735
2727 2736
2728 2737 @command('identify|id',
2729 2738 [('r', 'rev', '',
2730 2739 _('identify the specified revision'), _('REV')),
2731 2740 ('n', 'num', None, _('show local revision number')),
2732 2741 ('i', 'id', None, _('show global revision id')),
2733 2742 ('b', 'branch', None, _('show branch')),
2734 2743 ('t', 'tags', None, _('show tags')),
2735 2744 ('B', 'bookmarks', None, _('show bookmarks')),
2736 2745 ] + remoteopts + formatteropts,
2737 2746 _('[-nibtB] [-r REV] [SOURCE]'),
2738 optionalrepo=True, cmdtype=readonly)
2747 optionalrepo=True,
2748 intents={INTENT_READONLY})
2739 2749 def identify(ui, repo, source=None, rev=None,
2740 2750 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2741 2751 """identify the working directory or specified revision
2742 2752
2743 2753 Print a summary identifying the repository state at REV using one or
2744 2754 two parent hash identifiers, followed by a "+" if the working
2745 2755 directory has uncommitted changes, the branch name (if not default),
2746 2756 a list of tags, and a list of bookmarks.
2747 2757
2748 2758 When REV is not given, print a summary of the current state of the
2749 2759 repository including the working directory. Specify -r. to get information
2750 2760 of the working directory parent without scanning uncommitted changes.
2751 2761
2752 2762 Specifying a path to a repository root or Mercurial bundle will
2753 2763 cause lookup to operate on that repository/bundle.
2754 2764
2755 2765 .. container:: verbose
2756 2766
2757 2767 Examples:
2758 2768
2759 2769 - generate a build identifier for the working directory::
2760 2770
2761 2771 hg id --id > build-id.dat
2762 2772
2763 2773 - find the revision corresponding to a tag::
2764 2774
2765 2775 hg id -n -r 1.3
2766 2776
2767 2777 - check the most recent revision of a remote repository::
2768 2778
2769 2779 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2770 2780
2771 2781 See :hg:`log` for generating more information about specific revisions,
2772 2782 including full hash identifiers.
2773 2783
2774 2784 Returns 0 if successful.
2775 2785 """
2776 2786
2777 2787 opts = pycompat.byteskwargs(opts)
2778 2788 if not repo and not source:
2779 2789 raise error.Abort(_("there is no Mercurial repository here "
2780 2790 "(.hg not found)"))
2781 2791
2782 2792 if ui.debugflag:
2783 2793 hexfunc = hex
2784 2794 else:
2785 2795 hexfunc = short
2786 2796 default = not (num or id or branch or tags or bookmarks)
2787 2797 output = []
2788 2798 revs = []
2789 2799
2790 2800 if source:
2791 2801 source, branches = hg.parseurl(ui.expandpath(source))
2792 2802 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2793 2803 repo = peer.local()
2794 2804 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2795 2805
2796 2806 fm = ui.formatter('identify', opts)
2797 2807 fm.startitem()
2798 2808
2799 2809 if not repo:
2800 2810 if num or branch or tags:
2801 2811 raise error.Abort(
2802 2812 _("can't query remote revision number, branch, or tags"))
2803 2813 if not rev and revs:
2804 2814 rev = revs[0]
2805 2815 if not rev:
2806 2816 rev = "tip"
2807 2817
2808 2818 remoterev = peer.lookup(rev)
2809 2819 hexrev = hexfunc(remoterev)
2810 2820 if default or id:
2811 2821 output = [hexrev]
2812 2822 fm.data(id=hexrev)
2813 2823
2814 2824 def getbms():
2815 2825 bms = []
2816 2826
2817 2827 if 'bookmarks' in peer.listkeys('namespaces'):
2818 2828 hexremoterev = hex(remoterev)
2819 2829 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2820 2830 if bmr == hexremoterev]
2821 2831
2822 2832 return sorted(bms)
2823 2833
2824 2834 bms = getbms()
2825 2835 if bookmarks:
2826 2836 output.extend(bms)
2827 2837 elif default and not ui.quiet:
2828 2838 # multiple bookmarks for a single parent separated by '/'
2829 2839 bm = '/'.join(bms)
2830 2840 if bm:
2831 2841 output.append(bm)
2832 2842
2833 2843 fm.data(node=hex(remoterev))
2834 2844 fm.data(bookmarks=fm.formatlist(bms, name='bookmark'))
2835 2845 else:
2836 2846 if rev:
2837 2847 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2838 2848 ctx = scmutil.revsingle(repo, rev, None)
2839 2849
2840 2850 if ctx.rev() is None:
2841 2851 ctx = repo[None]
2842 2852 parents = ctx.parents()
2843 2853 taglist = []
2844 2854 for p in parents:
2845 2855 taglist.extend(p.tags())
2846 2856
2847 2857 dirty = ""
2848 2858 if ctx.dirty(missing=True, merge=False, branch=False):
2849 2859 dirty = '+'
2850 2860 fm.data(dirty=dirty)
2851 2861
2852 2862 hexoutput = [hexfunc(p.node()) for p in parents]
2853 2863 if default or id:
2854 2864 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
2855 2865 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
2856 2866
2857 2867 if num:
2858 2868 numoutput = ["%d" % p.rev() for p in parents]
2859 2869 output.append("%s%s" % ('+'.join(numoutput), dirty))
2860 2870
2861 2871 fn = fm.nested('parents', tmpl='{rev}:{node|formatnode}', sep=' ')
2862 2872 for p in parents:
2863 2873 fn.startitem()
2864 2874 fn.data(rev=p.rev())
2865 2875 fn.data(node=p.hex())
2866 2876 fn.context(ctx=p)
2867 2877 fn.end()
2868 2878 else:
2869 2879 hexoutput = hexfunc(ctx.node())
2870 2880 if default or id:
2871 2881 output = [hexoutput]
2872 2882 fm.data(id=hexoutput)
2873 2883
2874 2884 if num:
2875 2885 output.append(pycompat.bytestr(ctx.rev()))
2876 2886 taglist = ctx.tags()
2877 2887
2878 2888 if default and not ui.quiet:
2879 2889 b = ctx.branch()
2880 2890 if b != 'default':
2881 2891 output.append("(%s)" % b)
2882 2892
2883 2893 # multiple tags for a single parent separated by '/'
2884 2894 t = '/'.join(taglist)
2885 2895 if t:
2886 2896 output.append(t)
2887 2897
2888 2898 # multiple bookmarks for a single parent separated by '/'
2889 2899 bm = '/'.join(ctx.bookmarks())
2890 2900 if bm:
2891 2901 output.append(bm)
2892 2902 else:
2893 2903 if branch:
2894 2904 output.append(ctx.branch())
2895 2905
2896 2906 if tags:
2897 2907 output.extend(taglist)
2898 2908
2899 2909 if bookmarks:
2900 2910 output.extend(ctx.bookmarks())
2901 2911
2902 2912 fm.data(node=ctx.hex())
2903 2913 fm.data(branch=ctx.branch())
2904 2914 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
2905 2915 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
2906 2916 fm.context(ctx=ctx)
2907 2917
2908 2918 fm.plain("%s\n" % ' '.join(output))
2909 2919 fm.end()
2910 2920
2911 2921 @command('import|patch',
2912 2922 [('p', 'strip', 1,
2913 2923 _('directory strip option for patch. This has the same '
2914 2924 'meaning as the corresponding patch option'), _('NUM')),
2915 2925 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2916 2926 ('e', 'edit', False, _('invoke editor on commit messages')),
2917 2927 ('f', 'force', None,
2918 2928 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2919 2929 ('', 'no-commit', None,
2920 2930 _("don't commit, just update the working directory")),
2921 2931 ('', 'bypass', None,
2922 2932 _("apply patch without touching the working directory")),
2923 2933 ('', 'partial', None,
2924 2934 _('commit even if some hunks fail')),
2925 2935 ('', 'exact', None,
2926 2936 _('abort if patch would apply lossily')),
2927 2937 ('', 'prefix', '',
2928 2938 _('apply patch to subdirectory'), _('DIR')),
2929 2939 ('', 'import-branch', None,
2930 2940 _('use any branch information in patch (implied by --exact)'))] +
2931 2941 commitopts + commitopts2 + similarityopts,
2932 2942 _('[OPTION]... PATCH...'))
2933 2943 def import_(ui, repo, patch1=None, *patches, **opts):
2934 2944 """import an ordered set of patches
2935 2945
2936 2946 Import a list of patches and commit them individually (unless
2937 2947 --no-commit is specified).
2938 2948
2939 2949 To read a patch from standard input (stdin), use "-" as the patch
2940 2950 name. If a URL is specified, the patch will be downloaded from
2941 2951 there.
2942 2952
2943 2953 Import first applies changes to the working directory (unless
2944 2954 --bypass is specified), import will abort if there are outstanding
2945 2955 changes.
2946 2956
2947 2957 Use --bypass to apply and commit patches directly to the
2948 2958 repository, without affecting the working directory. Without
2949 2959 --exact, patches will be applied on top of the working directory
2950 2960 parent revision.
2951 2961
2952 2962 You can import a patch straight from a mail message. Even patches
2953 2963 as attachments work (to use the body part, it must have type
2954 2964 text/plain or text/x-patch). From and Subject headers of email
2955 2965 message are used as default committer and commit message. All
2956 2966 text/plain body parts before first diff are added to the commit
2957 2967 message.
2958 2968
2959 2969 If the imported patch was generated by :hg:`export`, user and
2960 2970 description from patch override values from message headers and
2961 2971 body. Values given on command line with -m/--message and -u/--user
2962 2972 override these.
2963 2973
2964 2974 If --exact is specified, import will set the working directory to
2965 2975 the parent of each patch before applying it, and will abort if the
2966 2976 resulting changeset has a different ID than the one recorded in
2967 2977 the patch. This will guard against various ways that portable
2968 2978 patch formats and mail systems might fail to transfer Mercurial
2969 2979 data or metadata. See :hg:`bundle` for lossless transmission.
2970 2980
2971 2981 Use --partial to ensure a changeset will be created from the patch
2972 2982 even if some hunks fail to apply. Hunks that fail to apply will be
2973 2983 written to a <target-file>.rej file. Conflicts can then be resolved
2974 2984 by hand before :hg:`commit --amend` is run to update the created
2975 2985 changeset. This flag exists to let people import patches that
2976 2986 partially apply without losing the associated metadata (author,
2977 2987 date, description, ...).
2978 2988
2979 2989 .. note::
2980 2990
2981 2991 When no hunks apply cleanly, :hg:`import --partial` will create
2982 2992 an empty changeset, importing only the patch metadata.
2983 2993
2984 2994 With -s/--similarity, hg will attempt to discover renames and
2985 2995 copies in the patch in the same way as :hg:`addremove`.
2986 2996
2987 2997 It is possible to use external patch programs to perform the patch
2988 2998 by setting the ``ui.patch`` configuration option. For the default
2989 2999 internal tool, the fuzz can also be configured via ``patch.fuzz``.
2990 3000 See :hg:`help config` for more information about configuration
2991 3001 files and how to use these options.
2992 3002
2993 3003 See :hg:`help dates` for a list of formats valid for -d/--date.
2994 3004
2995 3005 .. container:: verbose
2996 3006
2997 3007 Examples:
2998 3008
2999 3009 - import a traditional patch from a website and detect renames::
3000 3010
3001 3011 hg import -s 80 http://example.com/bugfix.patch
3002 3012
3003 3013 - import a changeset from an hgweb server::
3004 3014
3005 3015 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3006 3016
3007 3017 - import all the patches in an Unix-style mbox::
3008 3018
3009 3019 hg import incoming-patches.mbox
3010 3020
3011 3021 - import patches from stdin::
3012 3022
3013 3023 hg import -
3014 3024
3015 3025 - attempt to exactly restore an exported changeset (not always
3016 3026 possible)::
3017 3027
3018 3028 hg import --exact proposed-fix.patch
3019 3029
3020 3030 - use an external tool to apply a patch which is too fuzzy for
3021 3031 the default internal tool.
3022 3032
3023 3033 hg import --config ui.patch="patch --merge" fuzzy.patch
3024 3034
3025 3035 - change the default fuzzing from 2 to a less strict 7
3026 3036
3027 3037 hg import --config ui.fuzz=7 fuzz.patch
3028 3038
3029 3039 Returns 0 on success, 1 on partial success (see --partial).
3030 3040 """
3031 3041
3032 3042 opts = pycompat.byteskwargs(opts)
3033 3043 if not patch1:
3034 3044 raise error.Abort(_('need at least one patch to import'))
3035 3045
3036 3046 patches = (patch1,) + patches
3037 3047
3038 3048 date = opts.get('date')
3039 3049 if date:
3040 3050 opts['date'] = dateutil.parsedate(date)
3041 3051
3042 3052 exact = opts.get('exact')
3043 3053 update = not opts.get('bypass')
3044 3054 if not update and opts.get('no_commit'):
3045 3055 raise error.Abort(_('cannot use --no-commit with --bypass'))
3046 3056 try:
3047 3057 sim = float(opts.get('similarity') or 0)
3048 3058 except ValueError:
3049 3059 raise error.Abort(_('similarity must be a number'))
3050 3060 if sim < 0 or sim > 100:
3051 3061 raise error.Abort(_('similarity must be between 0 and 100'))
3052 3062 if sim and not update:
3053 3063 raise error.Abort(_('cannot use --similarity with --bypass'))
3054 3064 if exact:
3055 3065 if opts.get('edit'):
3056 3066 raise error.Abort(_('cannot use --exact with --edit'))
3057 3067 if opts.get('prefix'):
3058 3068 raise error.Abort(_('cannot use --exact with --prefix'))
3059 3069
3060 3070 base = opts["base"]
3061 3071 wlock = dsguard = lock = tr = None
3062 3072 msgs = []
3063 3073 ret = 0
3064 3074
3065 3075
3066 3076 try:
3067 3077 wlock = repo.wlock()
3068 3078
3069 3079 if update:
3070 3080 cmdutil.checkunfinished(repo)
3071 3081 if (exact or not opts.get('force')):
3072 3082 cmdutil.bailifchanged(repo)
3073 3083
3074 3084 if not opts.get('no_commit'):
3075 3085 lock = repo.lock()
3076 3086 tr = repo.transaction('import')
3077 3087 else:
3078 3088 dsguard = dirstateguard.dirstateguard(repo, 'import')
3079 3089 parents = repo[None].parents()
3080 3090 for patchurl in patches:
3081 3091 if patchurl == '-':
3082 3092 ui.status(_('applying patch from stdin\n'))
3083 3093 patchfile = ui.fin
3084 3094 patchurl = 'stdin' # for error message
3085 3095 else:
3086 3096 patchurl = os.path.join(base, patchurl)
3087 3097 ui.status(_('applying %s\n') % patchurl)
3088 3098 patchfile = hg.openpath(ui, patchurl)
3089 3099
3090 3100 haspatch = False
3091 3101 for hunk in patch.split(patchfile):
3092 3102 with patch.extract(ui, hunk) as patchdata:
3093 3103 msg, node, rej = cmdutil.tryimportone(ui, repo, patchdata,
3094 3104 parents, opts,
3095 3105 msgs, hg.clean)
3096 3106 if msg:
3097 3107 haspatch = True
3098 3108 ui.note(msg + '\n')
3099 3109 if update or exact:
3100 3110 parents = repo[None].parents()
3101 3111 else:
3102 3112 parents = [repo[node]]
3103 3113 if rej:
3104 3114 ui.write_err(_("patch applied partially\n"))
3105 3115 ui.write_err(_("(fix the .rej files and run "
3106 3116 "`hg commit --amend`)\n"))
3107 3117 ret = 1
3108 3118 break
3109 3119
3110 3120 if not haspatch:
3111 3121 raise error.Abort(_('%s: no diffs found') % patchurl)
3112 3122
3113 3123 if tr:
3114 3124 tr.close()
3115 3125 if msgs:
3116 3126 repo.savecommitmessage('\n* * *\n'.join(msgs))
3117 3127 if dsguard:
3118 3128 dsguard.close()
3119 3129 return ret
3120 3130 finally:
3121 3131 if tr:
3122 3132 tr.release()
3123 3133 release(lock, dsguard, wlock)
3124 3134
3125 3135 @command('incoming|in',
3126 3136 [('f', 'force', None,
3127 3137 _('run even if remote repository is unrelated')),
3128 3138 ('n', 'newest-first', None, _('show newest record first')),
3129 3139 ('', 'bundle', '',
3130 3140 _('file to store the bundles into'), _('FILE')),
3131 3141 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3132 3142 ('B', 'bookmarks', False, _("compare bookmarks")),
3133 3143 ('b', 'branch', [],
3134 3144 _('a specific branch you would like to pull'), _('BRANCH')),
3135 3145 ] + logopts + remoteopts + subrepoopts,
3136 3146 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3137 3147 def incoming(ui, repo, source="default", **opts):
3138 3148 """show new changesets found in source
3139 3149
3140 3150 Show new changesets found in the specified path/URL or the default
3141 3151 pull location. These are the changesets that would have been pulled
3142 3152 by :hg:`pull` at the time you issued this command.
3143 3153
3144 3154 See pull for valid source format details.
3145 3155
3146 3156 .. container:: verbose
3147 3157
3148 3158 With -B/--bookmarks, the result of bookmark comparison between
3149 3159 local and remote repositories is displayed. With -v/--verbose,
3150 3160 status is also displayed for each bookmark like below::
3151 3161
3152 3162 BM1 01234567890a added
3153 3163 BM2 1234567890ab advanced
3154 3164 BM3 234567890abc diverged
3155 3165 BM4 34567890abcd changed
3156 3166
3157 3167 The action taken locally when pulling depends on the
3158 3168 status of each bookmark:
3159 3169
3160 3170 :``added``: pull will create it
3161 3171 :``advanced``: pull will update it
3162 3172 :``diverged``: pull will create a divergent bookmark
3163 3173 :``changed``: result depends on remote changesets
3164 3174
3165 3175 From the point of view of pulling behavior, bookmark
3166 3176 existing only in the remote repository are treated as ``added``,
3167 3177 even if it is in fact locally deleted.
3168 3178
3169 3179 .. container:: verbose
3170 3180
3171 3181 For remote repository, using --bundle avoids downloading the
3172 3182 changesets twice if the incoming is followed by a pull.
3173 3183
3174 3184 Examples:
3175 3185
3176 3186 - show incoming changes with patches and full description::
3177 3187
3178 3188 hg incoming -vp
3179 3189
3180 3190 - show incoming changes excluding merges, store a bundle::
3181 3191
3182 3192 hg in -vpM --bundle incoming.hg
3183 3193 hg pull incoming.hg
3184 3194
3185 3195 - briefly list changes inside a bundle::
3186 3196
3187 3197 hg in changes.hg -T "{desc|firstline}\\n"
3188 3198
3189 3199 Returns 0 if there are incoming changes, 1 otherwise.
3190 3200 """
3191 3201 opts = pycompat.byteskwargs(opts)
3192 3202 if opts.get('graph'):
3193 3203 logcmdutil.checkunsupportedgraphflags([], opts)
3194 3204 def display(other, chlist, displayer):
3195 3205 revdag = logcmdutil.graphrevs(other, chlist, opts)
3196 3206 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3197 3207 graphmod.asciiedges)
3198 3208
3199 3209 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3200 3210 return 0
3201 3211
3202 3212 if opts.get('bundle') and opts.get('subrepos'):
3203 3213 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3204 3214
3205 3215 if opts.get('bookmarks'):
3206 3216 source, branches = hg.parseurl(ui.expandpath(source),
3207 3217 opts.get('branch'))
3208 3218 other = hg.peer(repo, opts, source)
3209 3219 if 'bookmarks' not in other.listkeys('namespaces'):
3210 3220 ui.warn(_("remote doesn't support bookmarks\n"))
3211 3221 return 0
3212 3222 ui.pager('incoming')
3213 3223 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3214 3224 return bookmarks.incoming(ui, repo, other)
3215 3225
3216 3226 repo._subtoppath = ui.expandpath(source)
3217 3227 try:
3218 3228 return hg.incoming(ui, repo, source, opts)
3219 3229 finally:
3220 3230 del repo._subtoppath
3221 3231
3222 3232
3223 3233 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3224 3234 norepo=True)
3225 3235 def init(ui, dest=".", **opts):
3226 3236 """create a new repository in the given directory
3227 3237
3228 3238 Initialize a new repository in the given directory. If the given
3229 3239 directory does not exist, it will be created.
3230 3240
3231 3241 If no directory is given, the current directory is used.
3232 3242
3233 3243 It is possible to specify an ``ssh://`` URL as the destination.
3234 3244 See :hg:`help urls` for more information.
3235 3245
3236 3246 Returns 0 on success.
3237 3247 """
3238 3248 opts = pycompat.byteskwargs(opts)
3239 3249 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3240 3250
3241 3251 @command('locate',
3242 3252 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3243 3253 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3244 3254 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3245 3255 ] + walkopts,
3246 3256 _('[OPTION]... [PATTERN]...'))
3247 3257 def locate(ui, repo, *pats, **opts):
3248 3258 """locate files matching specific patterns (DEPRECATED)
3249 3259
3250 3260 Print files under Mercurial control in the working directory whose
3251 3261 names match the given patterns.
3252 3262
3253 3263 By default, this command searches all directories in the working
3254 3264 directory. To search just the current directory and its
3255 3265 subdirectories, use "--include .".
3256 3266
3257 3267 If no patterns are given to match, this command prints the names
3258 3268 of all files under Mercurial control in the working directory.
3259 3269
3260 3270 If you want to feed the output of this command into the "xargs"
3261 3271 command, use the -0 option to both this command and "xargs". This
3262 3272 will avoid the problem of "xargs" treating single filenames that
3263 3273 contain whitespace as multiple filenames.
3264 3274
3265 3275 See :hg:`help files` for a more versatile command.
3266 3276
3267 3277 Returns 0 if a match is found, 1 otherwise.
3268 3278 """
3269 3279 opts = pycompat.byteskwargs(opts)
3270 3280 if opts.get('print0'):
3271 3281 end = '\0'
3272 3282 else:
3273 3283 end = '\n'
3274 3284 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3275 3285
3276 3286 ret = 1
3277 3287 m = scmutil.match(ctx, pats, opts, default='relglob',
3278 3288 badfn=lambda x, y: False)
3279 3289
3280 3290 ui.pager('locate')
3281 3291 for abs in ctx.matches(m):
3282 3292 if opts.get('fullpath'):
3283 3293 ui.write(repo.wjoin(abs), end)
3284 3294 else:
3285 3295 ui.write(((pats and m.rel(abs)) or abs), end)
3286 3296 ret = 0
3287 3297
3288 3298 return ret
3289 3299
3290 3300 @command('^log|history',
3291 3301 [('f', 'follow', None,
3292 3302 _('follow changeset history, or file history across copies and renames')),
3293 3303 ('', 'follow-first', None,
3294 3304 _('only follow the first parent of merge changesets (DEPRECATED)')),
3295 3305 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3296 3306 ('C', 'copies', None, _('show copied files')),
3297 3307 ('k', 'keyword', [],
3298 3308 _('do case-insensitive search for a given text'), _('TEXT')),
3299 3309 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3300 3310 ('L', 'line-range', [],
3301 3311 _('follow line range of specified file (EXPERIMENTAL)'),
3302 3312 _('FILE,RANGE')),
3303 3313 ('', 'removed', None, _('include revisions where files were removed')),
3304 3314 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3305 3315 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3306 3316 ('', 'only-branch', [],
3307 3317 _('show only changesets within the given named branch (DEPRECATED)'),
3308 3318 _('BRANCH')),
3309 3319 ('b', 'branch', [],
3310 3320 _('show changesets within the given named branch'), _('BRANCH')),
3311 3321 ('P', 'prune', [],
3312 3322 _('do not display revision or any of its ancestors'), _('REV')),
3313 3323 ] + logopts + walkopts,
3314 3324 _('[OPTION]... [FILE]'),
3315 inferrepo=True, cmdtype=readonly)
3325 inferrepo=True,
3326 intents={INTENT_READONLY})
3316 3327 def log(ui, repo, *pats, **opts):
3317 3328 """show revision history of entire repository or files
3318 3329
3319 3330 Print the revision history of the specified files or the entire
3320 3331 project.
3321 3332
3322 3333 If no revision range is specified, the default is ``tip:0`` unless
3323 3334 --follow is set, in which case the working directory parent is
3324 3335 used as the starting revision.
3325 3336
3326 3337 File history is shown without following rename or copy history of
3327 3338 files. Use -f/--follow with a filename to follow history across
3328 3339 renames and copies. --follow without a filename will only show
3329 3340 ancestors of the starting revision.
3330 3341
3331 3342 By default this command prints revision number and changeset id,
3332 3343 tags, non-trivial parents, user, date and time, and a summary for
3333 3344 each commit. When the -v/--verbose switch is used, the list of
3334 3345 changed files and full commit message are shown.
3335 3346
3336 3347 With --graph the revisions are shown as an ASCII art DAG with the most
3337 3348 recent changeset at the top.
3338 3349 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3339 3350 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3340 3351 changeset from the lines below is a parent of the 'o' merge on the same
3341 3352 line.
3342 3353 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3343 3354 of a '|' indicates one or more revisions in a path are omitted.
3344 3355
3345 3356 .. container:: verbose
3346 3357
3347 3358 Use -L/--line-range FILE,M:N options to follow the history of lines
3348 3359 from M to N in FILE. With -p/--patch only diff hunks affecting
3349 3360 specified line range will be shown. This option requires --follow;
3350 3361 it can be specified multiple times. Currently, this option is not
3351 3362 compatible with --graph. This option is experimental.
3352 3363
3353 3364 .. note::
3354 3365
3355 3366 :hg:`log --patch` may generate unexpected diff output for merge
3356 3367 changesets, as it will only compare the merge changeset against
3357 3368 its first parent. Also, only files different from BOTH parents
3358 3369 will appear in files:.
3359 3370
3360 3371 .. note::
3361 3372
3362 3373 For performance reasons, :hg:`log FILE` may omit duplicate changes
3363 3374 made on branches and will not show removals or mode changes. To
3364 3375 see all such changes, use the --removed switch.
3365 3376
3366 3377 .. container:: verbose
3367 3378
3368 3379 .. note::
3369 3380
3370 3381 The history resulting from -L/--line-range options depends on diff
3371 3382 options; for instance if white-spaces are ignored, respective changes
3372 3383 with only white-spaces in specified line range will not be listed.
3373 3384
3374 3385 .. container:: verbose
3375 3386
3376 3387 Some examples:
3377 3388
3378 3389 - changesets with full descriptions and file lists::
3379 3390
3380 3391 hg log -v
3381 3392
3382 3393 - changesets ancestral to the working directory::
3383 3394
3384 3395 hg log -f
3385 3396
3386 3397 - last 10 commits on the current branch::
3387 3398
3388 3399 hg log -l 10 -b .
3389 3400
3390 3401 - changesets showing all modifications of a file, including removals::
3391 3402
3392 3403 hg log --removed file.c
3393 3404
3394 3405 - all changesets that touch a directory, with diffs, excluding merges::
3395 3406
3396 3407 hg log -Mp lib/
3397 3408
3398 3409 - all revision numbers that match a keyword::
3399 3410
3400 3411 hg log -k bug --template "{rev}\\n"
3401 3412
3402 3413 - the full hash identifier of the working directory parent::
3403 3414
3404 3415 hg log -r . --template "{node}\\n"
3405 3416
3406 3417 - list available log templates::
3407 3418
3408 3419 hg log -T list
3409 3420
3410 3421 - check if a given changeset is included in a tagged release::
3411 3422
3412 3423 hg log -r "a21ccf and ancestor(1.9)"
3413 3424
3414 3425 - find all changesets by some user in a date range::
3415 3426
3416 3427 hg log -k alice -d "may 2008 to jul 2008"
3417 3428
3418 3429 - summary of all changesets after the last tag::
3419 3430
3420 3431 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3421 3432
3422 3433 - changesets touching lines 13 to 23 for file.c::
3423 3434
3424 3435 hg log -L file.c,13:23
3425 3436
3426 3437 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3427 3438 main.c with patch::
3428 3439
3429 3440 hg log -L file.c,13:23 -L main.c,2:6 -p
3430 3441
3431 3442 See :hg:`help dates` for a list of formats valid for -d/--date.
3432 3443
3433 3444 See :hg:`help revisions` for more about specifying and ordering
3434 3445 revisions.
3435 3446
3436 3447 See :hg:`help templates` for more about pre-packaged styles and
3437 3448 specifying custom templates. The default template used by the log
3438 3449 command can be customized via the ``ui.logtemplate`` configuration
3439 3450 setting.
3440 3451
3441 3452 Returns 0 on success.
3442 3453
3443 3454 """
3444 3455 opts = pycompat.byteskwargs(opts)
3445 3456 linerange = opts.get('line_range')
3446 3457
3447 3458 if linerange and not opts.get('follow'):
3448 3459 raise error.Abort(_('--line-range requires --follow'))
3449 3460
3450 3461 if linerange and pats:
3451 3462 # TODO: take pats as patterns with no line-range filter
3452 3463 raise error.Abort(
3453 3464 _('FILE arguments are not compatible with --line-range option')
3454 3465 )
3455 3466
3456 3467 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3457 3468 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3458 3469 if linerange:
3459 3470 # TODO: should follow file history from logcmdutil._initialrevs(),
3460 3471 # then filter the result by logcmdutil._makerevset() and --limit
3461 3472 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3462 3473
3463 3474 getrenamed = None
3464 3475 if opts.get('copies'):
3465 3476 endrev = None
3466 3477 if opts.get('rev'):
3467 3478 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3468 3479 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3469 3480
3470 3481 ui.pager('log')
3471 3482 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3472 3483 buffered=True)
3473 3484 if opts.get('graph'):
3474 3485 displayfn = logcmdutil.displaygraphrevs
3475 3486 else:
3476 3487 displayfn = logcmdutil.displayrevs
3477 3488 displayfn(ui, repo, revs, displayer, getrenamed)
3478 3489
3479 3490 @command('manifest',
3480 3491 [('r', 'rev', '', _('revision to display'), _('REV')),
3481 3492 ('', 'all', False, _("list files from all revisions"))]
3482 3493 + formatteropts,
3483 _('[-r REV]'), cmdtype=readonly)
3494 _('[-r REV]'),
3495 intents={INTENT_READONLY})
3484 3496 def manifest(ui, repo, node=None, rev=None, **opts):
3485 3497 """output the current or given revision of the project manifest
3486 3498
3487 3499 Print a list of version controlled files for the given revision.
3488 3500 If no revision is given, the first parent of the working directory
3489 3501 is used, or the null revision if no revision is checked out.
3490 3502
3491 3503 With -v, print file permissions, symlink and executable bits.
3492 3504 With --debug, print file revision hashes.
3493 3505
3494 3506 If option --all is specified, the list of all files from all revisions
3495 3507 is printed. This includes deleted and renamed files.
3496 3508
3497 3509 Returns 0 on success.
3498 3510 """
3499 3511 opts = pycompat.byteskwargs(opts)
3500 3512 fm = ui.formatter('manifest', opts)
3501 3513
3502 3514 if opts.get('all'):
3503 3515 if rev or node:
3504 3516 raise error.Abort(_("can't specify a revision with --all"))
3505 3517
3506 3518 res = set()
3507 3519 for rev in repo:
3508 3520 ctx = repo[rev]
3509 3521 res |= set(ctx.files())
3510 3522
3511 3523 ui.pager('manifest')
3512 3524 for f in sorted(res):
3513 3525 fm.startitem()
3514 3526 fm.write("path", '%s\n', f)
3515 3527 fm.end()
3516 3528 return
3517 3529
3518 3530 if rev and node:
3519 3531 raise error.Abort(_("please specify just one revision"))
3520 3532
3521 3533 if not node:
3522 3534 node = rev
3523 3535
3524 3536 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3525 3537 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3526 3538 if node:
3527 3539 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3528 3540 ctx = scmutil.revsingle(repo, node)
3529 3541 mf = ctx.manifest()
3530 3542 ui.pager('manifest')
3531 3543 for f in ctx:
3532 3544 fm.startitem()
3533 3545 fl = ctx[f].flags()
3534 3546 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3535 3547 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3536 3548 fm.write('path', '%s\n', f)
3537 3549 fm.end()
3538 3550
3539 3551 @command('^merge',
3540 3552 [('f', 'force', None,
3541 3553 _('force a merge including outstanding changes (DEPRECATED)')),
3542 3554 ('r', 'rev', '', _('revision to merge'), _('REV')),
3543 3555 ('P', 'preview', None,
3544 3556 _('review revisions to merge (no merge is performed)')),
3545 3557 ('', 'abort', None, _('abort the ongoing merge')),
3546 3558 ] + mergetoolopts,
3547 3559 _('[-P] [[-r] REV]'))
3548 3560 def merge(ui, repo, node=None, **opts):
3549 3561 """merge another revision into working directory
3550 3562
3551 3563 The current working directory is updated with all changes made in
3552 3564 the requested revision since the last common predecessor revision.
3553 3565
3554 3566 Files that changed between either parent are marked as changed for
3555 3567 the next commit and a commit must be performed before any further
3556 3568 updates to the repository are allowed. The next commit will have
3557 3569 two parents.
3558 3570
3559 3571 ``--tool`` can be used to specify the merge tool used for file
3560 3572 merges. It overrides the HGMERGE environment variable and your
3561 3573 configuration files. See :hg:`help merge-tools` for options.
3562 3574
3563 3575 If no revision is specified, the working directory's parent is a
3564 3576 head revision, and the current branch contains exactly one other
3565 3577 head, the other head is merged with by default. Otherwise, an
3566 3578 explicit revision with which to merge with must be provided.
3567 3579
3568 3580 See :hg:`help resolve` for information on handling file conflicts.
3569 3581
3570 3582 To undo an uncommitted merge, use :hg:`merge --abort` which
3571 3583 will check out a clean copy of the original merge parent, losing
3572 3584 all changes.
3573 3585
3574 3586 Returns 0 on success, 1 if there are unresolved files.
3575 3587 """
3576 3588
3577 3589 opts = pycompat.byteskwargs(opts)
3578 3590 abort = opts.get('abort')
3579 3591 if abort and repo.dirstate.p2() == nullid:
3580 3592 cmdutil.wrongtooltocontinue(repo, _('merge'))
3581 3593 if abort:
3582 3594 if node:
3583 3595 raise error.Abort(_("cannot specify a node with --abort"))
3584 3596 if opts.get('rev'):
3585 3597 raise error.Abort(_("cannot specify both --rev and --abort"))
3586 3598 if opts.get('preview'):
3587 3599 raise error.Abort(_("cannot specify --preview with --abort"))
3588 3600 if opts.get('rev') and node:
3589 3601 raise error.Abort(_("please specify just one revision"))
3590 3602 if not node:
3591 3603 node = opts.get('rev')
3592 3604
3593 3605 if node:
3594 3606 node = scmutil.revsingle(repo, node).node()
3595 3607
3596 3608 if not node and not abort:
3597 3609 node = repo[destutil.destmerge(repo)].node()
3598 3610
3599 3611 if opts.get('preview'):
3600 3612 # find nodes that are ancestors of p2 but not of p1
3601 3613 p1 = repo.lookup('.')
3602 3614 p2 = node
3603 3615 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3604 3616
3605 3617 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3606 3618 for node in nodes:
3607 3619 displayer.show(repo[node])
3608 3620 displayer.close()
3609 3621 return 0
3610 3622
3611 3623 try:
3612 3624 # ui.forcemerge is an internal variable, do not document
3613 3625 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3614 3626 force = opts.get('force')
3615 3627 labels = ['working copy', 'merge rev']
3616 3628 return hg.merge(repo, node, force=force, mergeforce=force,
3617 3629 labels=labels, abort=abort)
3618 3630 finally:
3619 3631 ui.setconfig('ui', 'forcemerge', '', 'merge')
3620 3632
3621 3633 @command('outgoing|out',
3622 3634 [('f', 'force', None, _('run even when the destination is unrelated')),
3623 3635 ('r', 'rev', [],
3624 3636 _('a changeset intended to be included in the destination'), _('REV')),
3625 3637 ('n', 'newest-first', None, _('show newest record first')),
3626 3638 ('B', 'bookmarks', False, _('compare bookmarks')),
3627 3639 ('b', 'branch', [], _('a specific branch you would like to push'),
3628 3640 _('BRANCH')),
3629 3641 ] + logopts + remoteopts + subrepoopts,
3630 3642 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3631 3643 def outgoing(ui, repo, dest=None, **opts):
3632 3644 """show changesets not found in the destination
3633 3645
3634 3646 Show changesets not found in the specified destination repository
3635 3647 or the default push location. These are the changesets that would
3636 3648 be pushed if a push was requested.
3637 3649
3638 3650 See pull for details of valid destination formats.
3639 3651
3640 3652 .. container:: verbose
3641 3653
3642 3654 With -B/--bookmarks, the result of bookmark comparison between
3643 3655 local and remote repositories is displayed. With -v/--verbose,
3644 3656 status is also displayed for each bookmark like below::
3645 3657
3646 3658 BM1 01234567890a added
3647 3659 BM2 deleted
3648 3660 BM3 234567890abc advanced
3649 3661 BM4 34567890abcd diverged
3650 3662 BM5 4567890abcde changed
3651 3663
3652 3664 The action taken when pushing depends on the
3653 3665 status of each bookmark:
3654 3666
3655 3667 :``added``: push with ``-B`` will create it
3656 3668 :``deleted``: push with ``-B`` will delete it
3657 3669 :``advanced``: push will update it
3658 3670 :``diverged``: push with ``-B`` will update it
3659 3671 :``changed``: push with ``-B`` will update it
3660 3672
3661 3673 From the point of view of pushing behavior, bookmarks
3662 3674 existing only in the remote repository are treated as
3663 3675 ``deleted``, even if it is in fact added remotely.
3664 3676
3665 3677 Returns 0 if there are outgoing changes, 1 otherwise.
3666 3678 """
3667 3679 opts = pycompat.byteskwargs(opts)
3668 3680 if opts.get('graph'):
3669 3681 logcmdutil.checkunsupportedgraphflags([], opts)
3670 3682 o, other = hg._outgoing(ui, repo, dest, opts)
3671 3683 if not o:
3672 3684 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3673 3685 return
3674 3686
3675 3687 revdag = logcmdutil.graphrevs(repo, o, opts)
3676 3688 ui.pager('outgoing')
3677 3689 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
3678 3690 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3679 3691 graphmod.asciiedges)
3680 3692 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3681 3693 return 0
3682 3694
3683 3695 if opts.get('bookmarks'):
3684 3696 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3685 3697 dest, branches = hg.parseurl(dest, opts.get('branch'))
3686 3698 other = hg.peer(repo, opts, dest)
3687 3699 if 'bookmarks' not in other.listkeys('namespaces'):
3688 3700 ui.warn(_("remote doesn't support bookmarks\n"))
3689 3701 return 0
3690 3702 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3691 3703 ui.pager('outgoing')
3692 3704 return bookmarks.outgoing(ui, repo, other)
3693 3705
3694 3706 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3695 3707 try:
3696 3708 return hg.outgoing(ui, repo, dest, opts)
3697 3709 finally:
3698 3710 del repo._subtoppath
3699 3711
3700 3712 @command('parents',
3701 3713 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3702 3714 ] + templateopts,
3703 3715 _('[-r REV] [FILE]'),
3704 3716 inferrepo=True)
3705 3717 def parents(ui, repo, file_=None, **opts):
3706 3718 """show the parents of the working directory or revision (DEPRECATED)
3707 3719
3708 3720 Print the working directory's parent revisions. If a revision is
3709 3721 given via -r/--rev, the parent of that revision will be printed.
3710 3722 If a file argument is given, the revision in which the file was
3711 3723 last changed (before the working directory revision or the
3712 3724 argument to --rev if given) is printed.
3713 3725
3714 3726 This command is equivalent to::
3715 3727
3716 3728 hg log -r "p1()+p2()" or
3717 3729 hg log -r "p1(REV)+p2(REV)" or
3718 3730 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3719 3731 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3720 3732
3721 3733 See :hg:`summary` and :hg:`help revsets` for related information.
3722 3734
3723 3735 Returns 0 on success.
3724 3736 """
3725 3737
3726 3738 opts = pycompat.byteskwargs(opts)
3727 3739 rev = opts.get('rev')
3728 3740 if rev:
3729 3741 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3730 3742 ctx = scmutil.revsingle(repo, rev, None)
3731 3743
3732 3744 if file_:
3733 3745 m = scmutil.match(ctx, (file_,), opts)
3734 3746 if m.anypats() or len(m.files()) != 1:
3735 3747 raise error.Abort(_('can only specify an explicit filename'))
3736 3748 file_ = m.files()[0]
3737 3749 filenodes = []
3738 3750 for cp in ctx.parents():
3739 3751 if not cp:
3740 3752 continue
3741 3753 try:
3742 3754 filenodes.append(cp.filenode(file_))
3743 3755 except error.LookupError:
3744 3756 pass
3745 3757 if not filenodes:
3746 3758 raise error.Abort(_("'%s' not found in manifest!") % file_)
3747 3759 p = []
3748 3760 for fn in filenodes:
3749 3761 fctx = repo.filectx(file_, fileid=fn)
3750 3762 p.append(fctx.node())
3751 3763 else:
3752 3764 p = [cp.node() for cp in ctx.parents()]
3753 3765
3754 3766 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3755 3767 for n in p:
3756 3768 if n != nullid:
3757 3769 displayer.show(repo[n])
3758 3770 displayer.close()
3759 3771
3760 3772 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True,
3761 cmdtype=readonly)
3773 intents={INTENT_READONLY})
3762 3774 def paths(ui, repo, search=None, **opts):
3763 3775 """show aliases for remote repositories
3764 3776
3765 3777 Show definition of symbolic path name NAME. If no name is given,
3766 3778 show definition of all available names.
3767 3779
3768 3780 Option -q/--quiet suppresses all output when searching for NAME
3769 3781 and shows only the path names when listing all definitions.
3770 3782
3771 3783 Path names are defined in the [paths] section of your
3772 3784 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3773 3785 repository, ``.hg/hgrc`` is used, too.
3774 3786
3775 3787 The path names ``default`` and ``default-push`` have a special
3776 3788 meaning. When performing a push or pull operation, they are used
3777 3789 as fallbacks if no location is specified on the command-line.
3778 3790 When ``default-push`` is set, it will be used for push and
3779 3791 ``default`` will be used for pull; otherwise ``default`` is used
3780 3792 as the fallback for both. When cloning a repository, the clone
3781 3793 source is written as ``default`` in ``.hg/hgrc``.
3782 3794
3783 3795 .. note::
3784 3796
3785 3797 ``default`` and ``default-push`` apply to all inbound (e.g.
3786 3798 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3787 3799 and :hg:`bundle`) operations.
3788 3800
3789 3801 See :hg:`help urls` for more information.
3790 3802
3791 3803 Returns 0 on success.
3792 3804 """
3793 3805
3794 3806 opts = pycompat.byteskwargs(opts)
3795 3807 ui.pager('paths')
3796 3808 if search:
3797 3809 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3798 3810 if name == search]
3799 3811 else:
3800 3812 pathitems = sorted(ui.paths.iteritems())
3801 3813
3802 3814 fm = ui.formatter('paths', opts)
3803 3815 if fm.isplain():
3804 3816 hidepassword = util.hidepassword
3805 3817 else:
3806 3818 hidepassword = bytes
3807 3819 if ui.quiet:
3808 3820 namefmt = '%s\n'
3809 3821 else:
3810 3822 namefmt = '%s = '
3811 3823 showsubopts = not search and not ui.quiet
3812 3824
3813 3825 for name, path in pathitems:
3814 3826 fm.startitem()
3815 3827 fm.condwrite(not search, 'name', namefmt, name)
3816 3828 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3817 3829 for subopt, value in sorted(path.suboptions.items()):
3818 3830 assert subopt not in ('name', 'url')
3819 3831 if showsubopts:
3820 3832 fm.plain('%s:%s = ' % (name, subopt))
3821 3833 fm.condwrite(showsubopts, subopt, '%s\n', value)
3822 3834
3823 3835 fm.end()
3824 3836
3825 3837 if search and not pathitems:
3826 3838 if not ui.quiet:
3827 3839 ui.warn(_("not found!\n"))
3828 3840 return 1
3829 3841 else:
3830 3842 return 0
3831 3843
3832 3844 @command('phase',
3833 3845 [('p', 'public', False, _('set changeset phase to public')),
3834 3846 ('d', 'draft', False, _('set changeset phase to draft')),
3835 3847 ('s', 'secret', False, _('set changeset phase to secret')),
3836 3848 ('f', 'force', False, _('allow to move boundary backward')),
3837 3849 ('r', 'rev', [], _('target revision'), _('REV')),
3838 3850 ],
3839 3851 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3840 3852 def phase(ui, repo, *revs, **opts):
3841 3853 """set or show the current phase name
3842 3854
3843 3855 With no argument, show the phase name of the current revision(s).
3844 3856
3845 3857 With one of -p/--public, -d/--draft or -s/--secret, change the
3846 3858 phase value of the specified revisions.
3847 3859
3848 3860 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
3849 3861 lower phase to a higher phase. Phases are ordered as follows::
3850 3862
3851 3863 public < draft < secret
3852 3864
3853 3865 Returns 0 on success, 1 if some phases could not be changed.
3854 3866
3855 3867 (For more information about the phases concept, see :hg:`help phases`.)
3856 3868 """
3857 3869 opts = pycompat.byteskwargs(opts)
3858 3870 # search for a unique phase argument
3859 3871 targetphase = None
3860 3872 for idx, name in enumerate(phases.phasenames):
3861 3873 if opts[name]:
3862 3874 if targetphase is not None:
3863 3875 raise error.Abort(_('only one phase can be specified'))
3864 3876 targetphase = idx
3865 3877
3866 3878 # look for specified revision
3867 3879 revs = list(revs)
3868 3880 revs.extend(opts['rev'])
3869 3881 if not revs:
3870 3882 # display both parents as the second parent phase can influence
3871 3883 # the phase of a merge commit
3872 3884 revs = [c.rev() for c in repo[None].parents()]
3873 3885
3874 3886 revs = scmutil.revrange(repo, revs)
3875 3887
3876 3888 ret = 0
3877 3889 if targetphase is None:
3878 3890 # display
3879 3891 for r in revs:
3880 3892 ctx = repo[r]
3881 3893 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3882 3894 else:
3883 3895 with repo.lock(), repo.transaction("phase") as tr:
3884 3896 # set phase
3885 3897 if not revs:
3886 3898 raise error.Abort(_('empty revision set'))
3887 3899 nodes = [repo[r].node() for r in revs]
3888 3900 # moving revision from public to draft may hide them
3889 3901 # We have to check result on an unfiltered repository
3890 3902 unfi = repo.unfiltered()
3891 3903 getphase = unfi._phasecache.phase
3892 3904 olddata = [getphase(unfi, r) for r in unfi]
3893 3905 phases.advanceboundary(repo, tr, targetphase, nodes)
3894 3906 if opts['force']:
3895 3907 phases.retractboundary(repo, tr, targetphase, nodes)
3896 3908 getphase = unfi._phasecache.phase
3897 3909 newdata = [getphase(unfi, r) for r in unfi]
3898 3910 changes = sum(newdata[r] != olddata[r] for r in unfi)
3899 3911 cl = unfi.changelog
3900 3912 rejected = [n for n in nodes
3901 3913 if newdata[cl.rev(n)] < targetphase]
3902 3914 if rejected:
3903 3915 ui.warn(_('cannot move %i changesets to a higher '
3904 3916 'phase, use --force\n') % len(rejected))
3905 3917 ret = 1
3906 3918 if changes:
3907 3919 msg = _('phase changed for %i changesets\n') % changes
3908 3920 if ret:
3909 3921 ui.status(msg)
3910 3922 else:
3911 3923 ui.note(msg)
3912 3924 else:
3913 3925 ui.warn(_('no phases changed\n'))
3914 3926 return ret
3915 3927
3916 3928 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3917 3929 """Run after a changegroup has been added via pull/unbundle
3918 3930
3919 3931 This takes arguments below:
3920 3932
3921 3933 :modheads: change of heads by pull/unbundle
3922 3934 :optupdate: updating working directory is needed or not
3923 3935 :checkout: update destination revision (or None to default destination)
3924 3936 :brev: a name, which might be a bookmark to be activated after updating
3925 3937 """
3926 3938 if modheads == 0:
3927 3939 return
3928 3940 if optupdate:
3929 3941 try:
3930 3942 return hg.updatetotally(ui, repo, checkout, brev)
3931 3943 except error.UpdateAbort as inst:
3932 3944 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
3933 3945 hint = inst.hint
3934 3946 raise error.UpdateAbort(msg, hint=hint)
3935 3947 if modheads > 1:
3936 3948 currentbranchheads = len(repo.branchheads())
3937 3949 if currentbranchheads == modheads:
3938 3950 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3939 3951 elif currentbranchheads > 1:
3940 3952 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3941 3953 "merge)\n"))
3942 3954 else:
3943 3955 ui.status(_("(run 'hg heads' to see heads)\n"))
3944 3956 elif not ui.configbool('commands', 'update.requiredest'):
3945 3957 ui.status(_("(run 'hg update' to get a working copy)\n"))
3946 3958
3947 3959 @command('^pull',
3948 3960 [('u', 'update', None,
3949 3961 _('update to new branch head if new descendants were pulled')),
3950 3962 ('f', 'force', None, _('run even when remote repository is unrelated')),
3951 3963 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3952 3964 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3953 3965 ('b', 'branch', [], _('a specific branch you would like to pull'),
3954 3966 _('BRANCH')),
3955 3967 ] + remoteopts,
3956 3968 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3957 3969 def pull(ui, repo, source="default", **opts):
3958 3970 """pull changes from the specified source
3959 3971
3960 3972 Pull changes from a remote repository to a local one.
3961 3973
3962 3974 This finds all changes from the repository at the specified path
3963 3975 or URL and adds them to a local repository (the current one unless
3964 3976 -R is specified). By default, this does not update the copy of the
3965 3977 project in the working directory.
3966 3978
3967 3979 When cloning from servers that support it, Mercurial may fetch
3968 3980 pre-generated data. When this is done, hooks operating on incoming
3969 3981 changesets and changegroups may fire more than once, once for each
3970 3982 pre-generated bundle and as well as for any additional remaining
3971 3983 data. See :hg:`help -e clonebundles` for more.
3972 3984
3973 3985 Use :hg:`incoming` if you want to see what would have been added
3974 3986 by a pull at the time you issued this command. If you then decide
3975 3987 to add those changes to the repository, you should use :hg:`pull
3976 3988 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3977 3989
3978 3990 If SOURCE is omitted, the 'default' path will be used.
3979 3991 See :hg:`help urls` for more information.
3980 3992
3981 3993 Specifying bookmark as ``.`` is equivalent to specifying the active
3982 3994 bookmark's name.
3983 3995
3984 3996 Returns 0 on success, 1 if an update had unresolved files.
3985 3997 """
3986 3998
3987 3999 opts = pycompat.byteskwargs(opts)
3988 4000 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3989 4001 msg = _('update destination required by configuration')
3990 4002 hint = _('use hg pull followed by hg update DEST')
3991 4003 raise error.Abort(msg, hint=hint)
3992 4004
3993 4005 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3994 4006 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3995 4007 other = hg.peer(repo, opts, source)
3996 4008 try:
3997 4009 revs, checkout = hg.addbranchrevs(repo, other, branches,
3998 4010 opts.get('rev'))
3999 4011
4000 4012
4001 4013 pullopargs = {}
4002 4014 if opts.get('bookmark'):
4003 4015 if not revs:
4004 4016 revs = []
4005 4017 # The list of bookmark used here is not the one used to actually
4006 4018 # update the bookmark name. This can result in the revision pulled
4007 4019 # not ending up with the name of the bookmark because of a race
4008 4020 # condition on the server. (See issue 4689 for details)
4009 4021 remotebookmarks = other.listkeys('bookmarks')
4010 4022 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4011 4023 pullopargs['remotebookmarks'] = remotebookmarks
4012 4024 for b in opts['bookmark']:
4013 4025 b = repo._bookmarks.expandname(b)
4014 4026 if b not in remotebookmarks:
4015 4027 raise error.Abort(_('remote bookmark %s not found!') % b)
4016 4028 revs.append(hex(remotebookmarks[b]))
4017 4029
4018 4030 if revs:
4019 4031 try:
4020 4032 # When 'rev' is a bookmark name, we cannot guarantee that it
4021 4033 # will be updated with that name because of a race condition
4022 4034 # server side. (See issue 4689 for details)
4023 4035 oldrevs = revs
4024 4036 revs = [] # actually, nodes
4025 4037 for r in oldrevs:
4026 4038 node = other.lookup(r)
4027 4039 revs.append(node)
4028 4040 if r == checkout:
4029 4041 checkout = node
4030 4042 except error.CapabilityError:
4031 4043 err = _("other repository doesn't support revision lookup, "
4032 4044 "so a rev cannot be specified.")
4033 4045 raise error.Abort(err)
4034 4046
4035 4047 wlock = util.nullcontextmanager()
4036 4048 if opts.get('update'):
4037 4049 wlock = repo.wlock()
4038 4050 with wlock:
4039 4051 pullopargs.update(opts.get('opargs', {}))
4040 4052 modheads = exchange.pull(repo, other, heads=revs,
4041 4053 force=opts.get('force'),
4042 4054 bookmarks=opts.get('bookmark', ()),
4043 4055 opargs=pullopargs).cgresult
4044 4056
4045 4057 # brev is a name, which might be a bookmark to be activated at
4046 4058 # the end of the update. In other words, it is an explicit
4047 4059 # destination of the update
4048 4060 brev = None
4049 4061
4050 4062 if checkout:
4051 4063 checkout = repo.changelog.rev(checkout)
4052 4064
4053 4065 # order below depends on implementation of
4054 4066 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4055 4067 # because 'checkout' is determined without it.
4056 4068 if opts.get('rev'):
4057 4069 brev = opts['rev'][0]
4058 4070 elif opts.get('branch'):
4059 4071 brev = opts['branch'][0]
4060 4072 else:
4061 4073 brev = branches[0]
4062 4074 repo._subtoppath = source
4063 4075 try:
4064 4076 ret = postincoming(ui, repo, modheads, opts.get('update'),
4065 4077 checkout, brev)
4066 4078
4067 4079 finally:
4068 4080 del repo._subtoppath
4069 4081
4070 4082 finally:
4071 4083 other.close()
4072 4084 return ret
4073 4085
4074 4086 @command('^push',
4075 4087 [('f', 'force', None, _('force push')),
4076 4088 ('r', 'rev', [],
4077 4089 _('a changeset intended to be included in the destination'),
4078 4090 _('REV')),
4079 4091 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4080 4092 ('b', 'branch', [],
4081 4093 _('a specific branch you would like to push'), _('BRANCH')),
4082 4094 ('', 'new-branch', False, _('allow pushing a new branch')),
4083 4095 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4084 4096 ] + remoteopts,
4085 4097 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4086 4098 def push(ui, repo, dest=None, **opts):
4087 4099 """push changes to the specified destination
4088 4100
4089 4101 Push changesets from the local repository to the specified
4090 4102 destination.
4091 4103
4092 4104 This operation is symmetrical to pull: it is identical to a pull
4093 4105 in the destination repository from the current one.
4094 4106
4095 4107 By default, push will not allow creation of new heads at the
4096 4108 destination, since multiple heads would make it unclear which head
4097 4109 to use. In this situation, it is recommended to pull and merge
4098 4110 before pushing.
4099 4111
4100 4112 Use --new-branch if you want to allow push to create a new named
4101 4113 branch that is not present at the destination. This allows you to
4102 4114 only create a new branch without forcing other changes.
4103 4115
4104 4116 .. note::
4105 4117
4106 4118 Extra care should be taken with the -f/--force option,
4107 4119 which will push all new heads on all branches, an action which will
4108 4120 almost always cause confusion for collaborators.
4109 4121
4110 4122 If -r/--rev is used, the specified revision and all its ancestors
4111 4123 will be pushed to the remote repository.
4112 4124
4113 4125 If -B/--bookmark is used, the specified bookmarked revision, its
4114 4126 ancestors, and the bookmark will be pushed to the remote
4115 4127 repository. Specifying ``.`` is equivalent to specifying the active
4116 4128 bookmark's name.
4117 4129
4118 4130 Please see :hg:`help urls` for important details about ``ssh://``
4119 4131 URLs. If DESTINATION is omitted, a default path will be used.
4120 4132
4121 4133 .. container:: verbose
4122 4134
4123 4135 The --pushvars option sends strings to the server that become
4124 4136 environment variables prepended with ``HG_USERVAR_``. For example,
4125 4137 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4126 4138 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4127 4139
4128 4140 pushvars can provide for user-overridable hooks as well as set debug
4129 4141 levels. One example is having a hook that blocks commits containing
4130 4142 conflict markers, but enables the user to override the hook if the file
4131 4143 is using conflict markers for testing purposes or the file format has
4132 4144 strings that look like conflict markers.
4133 4145
4134 4146 By default, servers will ignore `--pushvars`. To enable it add the
4135 4147 following to your configuration file::
4136 4148
4137 4149 [push]
4138 4150 pushvars.server = true
4139 4151
4140 4152 Returns 0 if push was successful, 1 if nothing to push.
4141 4153 """
4142 4154
4143 4155 opts = pycompat.byteskwargs(opts)
4144 4156 if opts.get('bookmark'):
4145 4157 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4146 4158 for b in opts['bookmark']:
4147 4159 # translate -B options to -r so changesets get pushed
4148 4160 b = repo._bookmarks.expandname(b)
4149 4161 if b in repo._bookmarks:
4150 4162 opts.setdefault('rev', []).append(b)
4151 4163 else:
4152 4164 # if we try to push a deleted bookmark, translate it to null
4153 4165 # this lets simultaneous -r, -b options continue working
4154 4166 opts.setdefault('rev', []).append("null")
4155 4167
4156 4168 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4157 4169 if not path:
4158 4170 raise error.Abort(_('default repository not configured!'),
4159 4171 hint=_("see 'hg help config.paths'"))
4160 4172 dest = path.pushloc or path.loc
4161 4173 branches = (path.branch, opts.get('branch') or [])
4162 4174 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4163 4175 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4164 4176 other = hg.peer(repo, opts, dest)
4165 4177
4166 4178 if revs:
4167 4179 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4168 4180 if not revs:
4169 4181 raise error.Abort(_("specified revisions evaluate to an empty set"),
4170 4182 hint=_("use different revision arguments"))
4171 4183 elif path.pushrev:
4172 4184 # It doesn't make any sense to specify ancestor revisions. So limit
4173 4185 # to DAG heads to make discovery simpler.
4174 4186 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4175 4187 revs = scmutil.revrange(repo, [expr])
4176 4188 revs = [repo[rev].node() for rev in revs]
4177 4189 if not revs:
4178 4190 raise error.Abort(_('default push revset for path evaluates to an '
4179 4191 'empty set'))
4180 4192
4181 4193 repo._subtoppath = dest
4182 4194 try:
4183 4195 # push subrepos depth-first for coherent ordering
4184 4196 c = repo['.']
4185 4197 subs = c.substate # only repos that are committed
4186 4198 for s in sorted(subs):
4187 4199 result = c.sub(s).push(opts)
4188 4200 if result == 0:
4189 4201 return not result
4190 4202 finally:
4191 4203 del repo._subtoppath
4192 4204
4193 4205 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4194 4206 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4195 4207
4196 4208 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4197 4209 newbranch=opts.get('new_branch'),
4198 4210 bookmarks=opts.get('bookmark', ()),
4199 4211 opargs=opargs)
4200 4212
4201 4213 result = not pushop.cgresult
4202 4214
4203 4215 if pushop.bkresult is not None:
4204 4216 if pushop.bkresult == 2:
4205 4217 result = 2
4206 4218 elif not result and pushop.bkresult:
4207 4219 result = 2
4208 4220
4209 4221 return result
4210 4222
4211 4223 @command('recover', [])
4212 4224 def recover(ui, repo):
4213 4225 """roll back an interrupted transaction
4214 4226
4215 4227 Recover from an interrupted commit or pull.
4216 4228
4217 4229 This command tries to fix the repository status after an
4218 4230 interrupted operation. It should only be necessary when Mercurial
4219 4231 suggests it.
4220 4232
4221 4233 Returns 0 if successful, 1 if nothing to recover or verify fails.
4222 4234 """
4223 4235 if repo.recover():
4224 4236 return hg.verify(repo)
4225 4237 return 1
4226 4238
4227 4239 @command('^remove|rm',
4228 4240 [('A', 'after', None, _('record delete for missing files')),
4229 4241 ('f', 'force', None,
4230 4242 _('forget added files, delete modified files')),
4231 4243 ] + subrepoopts + walkopts + dryrunopts,
4232 4244 _('[OPTION]... FILE...'),
4233 4245 inferrepo=True)
4234 4246 def remove(ui, repo, *pats, **opts):
4235 4247 """remove the specified files on the next commit
4236 4248
4237 4249 Schedule the indicated files for removal from the current branch.
4238 4250
4239 4251 This command schedules the files to be removed at the next commit.
4240 4252 To undo a remove before that, see :hg:`revert`. To undo added
4241 4253 files, see :hg:`forget`.
4242 4254
4243 4255 .. container:: verbose
4244 4256
4245 4257 -A/--after can be used to remove only files that have already
4246 4258 been deleted, -f/--force can be used to force deletion, and -Af
4247 4259 can be used to remove files from the next revision without
4248 4260 deleting them from the working directory.
4249 4261
4250 4262 The following table details the behavior of remove for different
4251 4263 file states (columns) and option combinations (rows). The file
4252 4264 states are Added [A], Clean [C], Modified [M] and Missing [!]
4253 4265 (as reported by :hg:`status`). The actions are Warn, Remove
4254 4266 (from branch) and Delete (from disk):
4255 4267
4256 4268 ========= == == == ==
4257 4269 opt/state A C M !
4258 4270 ========= == == == ==
4259 4271 none W RD W R
4260 4272 -f R RD RD R
4261 4273 -A W W W R
4262 4274 -Af R R R R
4263 4275 ========= == == == ==
4264 4276
4265 4277 .. note::
4266 4278
4267 4279 :hg:`remove` never deletes files in Added [A] state from the
4268 4280 working directory, not even if ``--force`` is specified.
4269 4281
4270 4282 Returns 0 on success, 1 if any warnings encountered.
4271 4283 """
4272 4284
4273 4285 opts = pycompat.byteskwargs(opts)
4274 4286 after, force = opts.get('after'), opts.get('force')
4275 4287 dryrun = opts.get('dry_run')
4276 4288 if not pats and not after:
4277 4289 raise error.Abort(_('no files specified'))
4278 4290
4279 4291 m = scmutil.match(repo[None], pats, opts)
4280 4292 subrepos = opts.get('subrepos')
4281 4293 return cmdutil.remove(ui, repo, m, "", after, force, subrepos,
4282 4294 dryrun=dryrun)
4283 4295
4284 4296 @command('rename|move|mv',
4285 4297 [('A', 'after', None, _('record a rename that has already occurred')),
4286 4298 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4287 4299 ] + walkopts + dryrunopts,
4288 4300 _('[OPTION]... SOURCE... DEST'))
4289 4301 def rename(ui, repo, *pats, **opts):
4290 4302 """rename files; equivalent of copy + remove
4291 4303
4292 4304 Mark dest as copies of sources; mark sources for deletion. If dest
4293 4305 is a directory, copies are put in that directory. If dest is a
4294 4306 file, there can only be one source.
4295 4307
4296 4308 By default, this command copies the contents of files as they
4297 4309 exist in the working directory. If invoked with -A/--after, the
4298 4310 operation is recorded, but no copying is performed.
4299 4311
4300 4312 This command takes effect at the next commit. To undo a rename
4301 4313 before that, see :hg:`revert`.
4302 4314
4303 4315 Returns 0 on success, 1 if errors are encountered.
4304 4316 """
4305 4317 opts = pycompat.byteskwargs(opts)
4306 4318 with repo.wlock(False):
4307 4319 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4308 4320
4309 4321 @command('resolve',
4310 4322 [('a', 'all', None, _('select all unresolved files')),
4311 4323 ('l', 'list', None, _('list state of files needing merge')),
4312 4324 ('m', 'mark', None, _('mark files as resolved')),
4313 4325 ('u', 'unmark', None, _('mark files as unresolved')),
4314 4326 ('n', 'no-status', None, _('hide status prefix'))]
4315 4327 + mergetoolopts + walkopts + formatteropts,
4316 4328 _('[OPTION]... [FILE]...'),
4317 4329 inferrepo=True)
4318 4330 def resolve(ui, repo, *pats, **opts):
4319 4331 """redo merges or set/view the merge status of files
4320 4332
4321 4333 Merges with unresolved conflicts are often the result of
4322 4334 non-interactive merging using the ``internal:merge`` configuration
4323 4335 setting, or a command-line merge tool like ``diff3``. The resolve
4324 4336 command is used to manage the files involved in a merge, after
4325 4337 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4326 4338 working directory must have two parents). See :hg:`help
4327 4339 merge-tools` for information on configuring merge tools.
4328 4340
4329 4341 The resolve command can be used in the following ways:
4330 4342
4331 4343 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4332 4344 files, discarding any previous merge attempts. Re-merging is not
4333 4345 performed for files already marked as resolved. Use ``--all/-a``
4334 4346 to select all unresolved files. ``--tool`` can be used to specify
4335 4347 the merge tool used for the given files. It overrides the HGMERGE
4336 4348 environment variable and your configuration files. Previous file
4337 4349 contents are saved with a ``.orig`` suffix.
4338 4350
4339 4351 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4340 4352 (e.g. after having manually fixed-up the files). The default is
4341 4353 to mark all unresolved files.
4342 4354
4343 4355 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4344 4356 default is to mark all resolved files.
4345 4357
4346 4358 - :hg:`resolve -l`: list files which had or still have conflicts.
4347 4359 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4348 4360 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4349 4361 the list. See :hg:`help filesets` for details.
4350 4362
4351 4363 .. note::
4352 4364
4353 4365 Mercurial will not let you commit files with unresolved merge
4354 4366 conflicts. You must use :hg:`resolve -m ...` before you can
4355 4367 commit after a conflicting merge.
4356 4368
4357 4369 Returns 0 on success, 1 if any files fail a resolve attempt.
4358 4370 """
4359 4371
4360 4372 opts = pycompat.byteskwargs(opts)
4361 4373 flaglist = 'all mark unmark list no_status'.split()
4362 4374 all, mark, unmark, show, nostatus = \
4363 4375 [opts.get(o) for o in flaglist]
4364 4376
4365 4377 if (show and (mark or unmark)) or (mark and unmark):
4366 4378 raise error.Abort(_("too many options specified"))
4367 4379 if pats and all:
4368 4380 raise error.Abort(_("can't specify --all and patterns"))
4369 4381 if not (all or pats or show or mark or unmark):
4370 4382 raise error.Abort(_('no files or directories specified'),
4371 4383 hint=('use --all to re-merge all unresolved files'))
4372 4384
4373 4385 if show:
4374 4386 ui.pager('resolve')
4375 4387 fm = ui.formatter('resolve', opts)
4376 4388 ms = mergemod.mergestate.read(repo)
4377 4389 m = scmutil.match(repo[None], pats, opts)
4378 4390
4379 4391 # Labels and keys based on merge state. Unresolved path conflicts show
4380 4392 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4381 4393 # resolved conflicts.
4382 4394 mergestateinfo = {
4383 4395 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4384 4396 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4385 4397 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4386 4398 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4387 4399 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4388 4400 'D'),
4389 4401 }
4390 4402
4391 4403 for f in ms:
4392 4404 if not m(f):
4393 4405 continue
4394 4406
4395 4407 label, key = mergestateinfo[ms[f]]
4396 4408 fm.startitem()
4397 4409 fm.condwrite(not nostatus, 'status', '%s ', key, label=label)
4398 4410 fm.write('path', '%s\n', f, label=label)
4399 4411 fm.end()
4400 4412 return 0
4401 4413
4402 4414 with repo.wlock():
4403 4415 ms = mergemod.mergestate.read(repo)
4404 4416
4405 4417 if not (ms.active() or repo.dirstate.p2() != nullid):
4406 4418 raise error.Abort(
4407 4419 _('resolve command not applicable when not merging'))
4408 4420
4409 4421 wctx = repo[None]
4410 4422
4411 4423 if (ms.mergedriver
4412 4424 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4413 4425 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4414 4426 ms.commit()
4415 4427 # allow mark and unmark to go through
4416 4428 if not mark and not unmark and not proceed:
4417 4429 return 1
4418 4430
4419 4431 m = scmutil.match(wctx, pats, opts)
4420 4432 ret = 0
4421 4433 didwork = False
4422 4434 runconclude = False
4423 4435
4424 4436 tocomplete = []
4425 4437 for f in ms:
4426 4438 if not m(f):
4427 4439 continue
4428 4440
4429 4441 didwork = True
4430 4442
4431 4443 # don't let driver-resolved files be marked, and run the conclude
4432 4444 # step if asked to resolve
4433 4445 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4434 4446 exact = m.exact(f)
4435 4447 if mark:
4436 4448 if exact:
4437 4449 ui.warn(_('not marking %s as it is driver-resolved\n')
4438 4450 % f)
4439 4451 elif unmark:
4440 4452 if exact:
4441 4453 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4442 4454 % f)
4443 4455 else:
4444 4456 runconclude = True
4445 4457 continue
4446 4458
4447 4459 # path conflicts must be resolved manually
4448 4460 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4449 4461 mergemod.MERGE_RECORD_RESOLVED_PATH):
4450 4462 if mark:
4451 4463 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4452 4464 elif unmark:
4453 4465 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4454 4466 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4455 4467 ui.warn(_('%s: path conflict must be resolved manually\n')
4456 4468 % f)
4457 4469 continue
4458 4470
4459 4471 if mark:
4460 4472 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4461 4473 elif unmark:
4462 4474 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4463 4475 else:
4464 4476 # backup pre-resolve (merge uses .orig for its own purposes)
4465 4477 a = repo.wjoin(f)
4466 4478 try:
4467 4479 util.copyfile(a, a + ".resolve")
4468 4480 except (IOError, OSError) as inst:
4469 4481 if inst.errno != errno.ENOENT:
4470 4482 raise
4471 4483
4472 4484 try:
4473 4485 # preresolve file
4474 4486 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4475 4487 'resolve')
4476 4488 complete, r = ms.preresolve(f, wctx)
4477 4489 if not complete:
4478 4490 tocomplete.append(f)
4479 4491 elif r:
4480 4492 ret = 1
4481 4493 finally:
4482 4494 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4483 4495 ms.commit()
4484 4496
4485 4497 # replace filemerge's .orig file with our resolve file, but only
4486 4498 # for merges that are complete
4487 4499 if complete:
4488 4500 try:
4489 4501 util.rename(a + ".resolve",
4490 4502 scmutil.origpath(ui, repo, a))
4491 4503 except OSError as inst:
4492 4504 if inst.errno != errno.ENOENT:
4493 4505 raise
4494 4506
4495 4507 for f in tocomplete:
4496 4508 try:
4497 4509 # resolve file
4498 4510 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4499 4511 'resolve')
4500 4512 r = ms.resolve(f, wctx)
4501 4513 if r:
4502 4514 ret = 1
4503 4515 finally:
4504 4516 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4505 4517 ms.commit()
4506 4518
4507 4519 # replace filemerge's .orig file with our resolve file
4508 4520 a = repo.wjoin(f)
4509 4521 try:
4510 4522 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4511 4523 except OSError as inst:
4512 4524 if inst.errno != errno.ENOENT:
4513 4525 raise
4514 4526
4515 4527 ms.commit()
4516 4528 ms.recordactions()
4517 4529
4518 4530 if not didwork and pats:
4519 4531 hint = None
4520 4532 if not any([p for p in pats if p.find(':') >= 0]):
4521 4533 pats = ['path:%s' % p for p in pats]
4522 4534 m = scmutil.match(wctx, pats, opts)
4523 4535 for f in ms:
4524 4536 if not m(f):
4525 4537 continue
4526 4538 flags = ''.join(['-%s ' % o[0:1] for o in flaglist
4527 4539 if opts.get(o)])
4528 4540 hint = _("(try: hg resolve %s%s)\n") % (
4529 4541 flags,
4530 4542 ' '.join(pats))
4531 4543 break
4532 4544 ui.warn(_("arguments do not match paths that need resolving\n"))
4533 4545 if hint:
4534 4546 ui.warn(hint)
4535 4547 elif ms.mergedriver and ms.mdstate() != 's':
4536 4548 # run conclude step when either a driver-resolved file is requested
4537 4549 # or there are no driver-resolved files
4538 4550 # we can't use 'ret' to determine whether any files are unresolved
4539 4551 # because we might not have tried to resolve some
4540 4552 if ((runconclude or not list(ms.driverresolved()))
4541 4553 and not list(ms.unresolved())):
4542 4554 proceed = mergemod.driverconclude(repo, ms, wctx)
4543 4555 ms.commit()
4544 4556 if not proceed:
4545 4557 return 1
4546 4558
4547 4559 # Nudge users into finishing an unfinished operation
4548 4560 unresolvedf = list(ms.unresolved())
4549 4561 driverresolvedf = list(ms.driverresolved())
4550 4562 if not unresolvedf and not driverresolvedf:
4551 4563 ui.status(_('(no more unresolved files)\n'))
4552 4564 cmdutil.checkafterresolved(repo)
4553 4565 elif not unresolvedf:
4554 4566 ui.status(_('(no more unresolved files -- '
4555 4567 'run "hg resolve --all" to conclude)\n'))
4556 4568
4557 4569 return ret
4558 4570
4559 4571 @command('revert',
4560 4572 [('a', 'all', None, _('revert all changes when no arguments given')),
4561 4573 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4562 4574 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4563 4575 ('C', 'no-backup', None, _('do not save backup copies of files')),
4564 4576 ('i', 'interactive', None, _('interactively select the changes')),
4565 4577 ] + walkopts + dryrunopts,
4566 4578 _('[OPTION]... [-r REV] [NAME]...'))
4567 4579 def revert(ui, repo, *pats, **opts):
4568 4580 """restore files to their checkout state
4569 4581
4570 4582 .. note::
4571 4583
4572 4584 To check out earlier revisions, you should use :hg:`update REV`.
4573 4585 To cancel an uncommitted merge (and lose your changes),
4574 4586 use :hg:`merge --abort`.
4575 4587
4576 4588 With no revision specified, revert the specified files or directories
4577 4589 to the contents they had in the parent of the working directory.
4578 4590 This restores the contents of files to an unmodified
4579 4591 state and unschedules adds, removes, copies, and renames. If the
4580 4592 working directory has two parents, you must explicitly specify a
4581 4593 revision.
4582 4594
4583 4595 Using the -r/--rev or -d/--date options, revert the given files or
4584 4596 directories to their states as of a specific revision. Because
4585 4597 revert does not change the working directory parents, this will
4586 4598 cause these files to appear modified. This can be helpful to "back
4587 4599 out" some or all of an earlier change. See :hg:`backout` for a
4588 4600 related method.
4589 4601
4590 4602 Modified files are saved with a .orig suffix before reverting.
4591 4603 To disable these backups, use --no-backup. It is possible to store
4592 4604 the backup files in a custom directory relative to the root of the
4593 4605 repository by setting the ``ui.origbackuppath`` configuration
4594 4606 option.
4595 4607
4596 4608 See :hg:`help dates` for a list of formats valid for -d/--date.
4597 4609
4598 4610 See :hg:`help backout` for a way to reverse the effect of an
4599 4611 earlier changeset.
4600 4612
4601 4613 Returns 0 on success.
4602 4614 """
4603 4615
4604 4616 opts = pycompat.byteskwargs(opts)
4605 4617 if opts.get("date"):
4606 4618 if opts.get("rev"):
4607 4619 raise error.Abort(_("you can't specify a revision and a date"))
4608 4620 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4609 4621
4610 4622 parent, p2 = repo.dirstate.parents()
4611 4623 if not opts.get('rev') and p2 != nullid:
4612 4624 # revert after merge is a trap for new users (issue2915)
4613 4625 raise error.Abort(_('uncommitted merge with no revision specified'),
4614 4626 hint=_("use 'hg update' or see 'hg help revert'"))
4615 4627
4616 4628 rev = opts.get('rev')
4617 4629 if rev:
4618 4630 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4619 4631 ctx = scmutil.revsingle(repo, rev)
4620 4632
4621 4633 if (not (pats or opts.get('include') or opts.get('exclude') or
4622 4634 opts.get('all') or opts.get('interactive'))):
4623 4635 msg = _("no files or directories specified")
4624 4636 if p2 != nullid:
4625 4637 hint = _("uncommitted merge, use --all to discard all changes,"
4626 4638 " or 'hg update -C .' to abort the merge")
4627 4639 raise error.Abort(msg, hint=hint)
4628 4640 dirty = any(repo.status())
4629 4641 node = ctx.node()
4630 4642 if node != parent:
4631 4643 if dirty:
4632 4644 hint = _("uncommitted changes, use --all to discard all"
4633 4645 " changes, or 'hg update %s' to update") % ctx.rev()
4634 4646 else:
4635 4647 hint = _("use --all to revert all files,"
4636 4648 " or 'hg update %s' to update") % ctx.rev()
4637 4649 elif dirty:
4638 4650 hint = _("uncommitted changes, use --all to discard all changes")
4639 4651 else:
4640 4652 hint = _("use --all to revert all files")
4641 4653 raise error.Abort(msg, hint=hint)
4642 4654
4643 4655 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
4644 4656 **pycompat.strkwargs(opts))
4645 4657
4646 4658 @command('rollback', dryrunopts +
4647 4659 [('f', 'force', False, _('ignore safety measures'))])
4648 4660 def rollback(ui, repo, **opts):
4649 4661 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4650 4662
4651 4663 Please use :hg:`commit --amend` instead of rollback to correct
4652 4664 mistakes in the last commit.
4653 4665
4654 4666 This command should be used with care. There is only one level of
4655 4667 rollback, and there is no way to undo a rollback. It will also
4656 4668 restore the dirstate at the time of the last transaction, losing
4657 4669 any dirstate changes since that time. This command does not alter
4658 4670 the working directory.
4659 4671
4660 4672 Transactions are used to encapsulate the effects of all commands
4661 4673 that create new changesets or propagate existing changesets into a
4662 4674 repository.
4663 4675
4664 4676 .. container:: verbose
4665 4677
4666 4678 For example, the following commands are transactional, and their
4667 4679 effects can be rolled back:
4668 4680
4669 4681 - commit
4670 4682 - import
4671 4683 - pull
4672 4684 - push (with this repository as the destination)
4673 4685 - unbundle
4674 4686
4675 4687 To avoid permanent data loss, rollback will refuse to rollback a
4676 4688 commit transaction if it isn't checked out. Use --force to
4677 4689 override this protection.
4678 4690
4679 4691 The rollback command can be entirely disabled by setting the
4680 4692 ``ui.rollback`` configuration setting to false. If you're here
4681 4693 because you want to use rollback and it's disabled, you can
4682 4694 re-enable the command by setting ``ui.rollback`` to true.
4683 4695
4684 4696 This command is not intended for use on public repositories. Once
4685 4697 changes are visible for pull by other users, rolling a transaction
4686 4698 back locally is ineffective (someone else may already have pulled
4687 4699 the changes). Furthermore, a race is possible with readers of the
4688 4700 repository; for example an in-progress pull from the repository
4689 4701 may fail if a rollback is performed.
4690 4702
4691 4703 Returns 0 on success, 1 if no rollback data is available.
4692 4704 """
4693 4705 if not ui.configbool('ui', 'rollback'):
4694 4706 raise error.Abort(_('rollback is disabled because it is unsafe'),
4695 4707 hint=('see `hg help -v rollback` for information'))
4696 4708 return repo.rollback(dryrun=opts.get(r'dry_run'),
4697 4709 force=opts.get(r'force'))
4698 4710
4699 @command('root', [], cmdtype=readonly)
4711 @command('root', [], intents={INTENT_READONLY})
4700 4712 def root(ui, repo):
4701 4713 """print the root (top) of the current working directory
4702 4714
4703 4715 Print the root directory of the current repository.
4704 4716
4705 4717 Returns 0 on success.
4706 4718 """
4707 4719 ui.write(repo.root + "\n")
4708 4720
4709 4721 @command('^serve',
4710 4722 [('A', 'accesslog', '', _('name of access log file to write to'),
4711 4723 _('FILE')),
4712 4724 ('d', 'daemon', None, _('run server in background')),
4713 4725 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4714 4726 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4715 4727 # use string type, then we can check if something was passed
4716 4728 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4717 4729 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4718 4730 _('ADDR')),
4719 4731 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4720 4732 _('PREFIX')),
4721 4733 ('n', 'name', '',
4722 4734 _('name to show in web pages (default: working directory)'), _('NAME')),
4723 4735 ('', 'web-conf', '',
4724 4736 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4725 4737 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4726 4738 _('FILE')),
4727 4739 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4728 4740 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4729 4741 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4730 4742 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4731 4743 ('', 'style', '', _('template style to use'), _('STYLE')),
4732 4744 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4733 4745 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4734 4746 + subrepoopts,
4735 4747 _('[OPTION]...'),
4736 4748 optionalrepo=True)
4737 4749 def serve(ui, repo, **opts):
4738 4750 """start stand-alone webserver
4739 4751
4740 4752 Start a local HTTP repository browser and pull server. You can use
4741 4753 this for ad-hoc sharing and browsing of repositories. It is
4742 4754 recommended to use a real web server to serve a repository for
4743 4755 longer periods of time.
4744 4756
4745 4757 Please note that the server does not implement access control.
4746 4758 This means that, by default, anybody can read from the server and
4747 4759 nobody can write to it by default. Set the ``web.allow-push``
4748 4760 option to ``*`` to allow everybody to push to the server. You
4749 4761 should use a real web server if you need to authenticate users.
4750 4762
4751 4763 By default, the server logs accesses to stdout and errors to
4752 4764 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4753 4765 files.
4754 4766
4755 4767 To have the server choose a free port number to listen on, specify
4756 4768 a port number of 0; in this case, the server will print the port
4757 4769 number it uses.
4758 4770
4759 4771 Returns 0 on success.
4760 4772 """
4761 4773
4762 4774 opts = pycompat.byteskwargs(opts)
4763 4775 if opts["stdio"] and opts["cmdserver"]:
4764 4776 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4765 4777
4766 4778 if opts["stdio"]:
4767 4779 if repo is None:
4768 4780 raise error.RepoError(_("there is no Mercurial repository here"
4769 4781 " (.hg not found)"))
4770 4782 s = wireprotoserver.sshserver(ui, repo)
4771 4783 s.serve_forever()
4772 4784
4773 4785 service = server.createservice(ui, repo, opts)
4774 4786 return server.runservice(opts, initfn=service.init, runfn=service.run)
4775 4787
4776 4788 @command('^status|st',
4777 4789 [('A', 'all', None, _('show status of all files')),
4778 4790 ('m', 'modified', None, _('show only modified files')),
4779 4791 ('a', 'added', None, _('show only added files')),
4780 4792 ('r', 'removed', None, _('show only removed files')),
4781 4793 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4782 4794 ('c', 'clean', None, _('show only files without changes')),
4783 4795 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4784 4796 ('i', 'ignored', None, _('show only ignored files')),
4785 4797 ('n', 'no-status', None, _('hide status prefix')),
4786 4798 ('t', 'terse', '', _('show the terse output (EXPERIMENTAL)')),
4787 4799 ('C', 'copies', None, _('show source of copied files')),
4788 4800 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4789 4801 ('', 'rev', [], _('show difference from revision'), _('REV')),
4790 4802 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4791 4803 ] + walkopts + subrepoopts + formatteropts,
4792 4804 _('[OPTION]... [FILE]...'),
4793 inferrepo=True, cmdtype=readonly)
4805 inferrepo=True,
4806 intents={INTENT_READONLY})
4794 4807 def status(ui, repo, *pats, **opts):
4795 4808 """show changed files in the working directory
4796 4809
4797 4810 Show status of files in the repository. If names are given, only
4798 4811 files that match are shown. Files that are clean or ignored or
4799 4812 the source of a copy/move operation, are not listed unless
4800 4813 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4801 4814 Unless options described with "show only ..." are given, the
4802 4815 options -mardu are used.
4803 4816
4804 4817 Option -q/--quiet hides untracked (unknown and ignored) files
4805 4818 unless explicitly requested with -u/--unknown or -i/--ignored.
4806 4819
4807 4820 .. note::
4808 4821
4809 4822 :hg:`status` may appear to disagree with diff if permissions have
4810 4823 changed or a merge has occurred. The standard diff format does
4811 4824 not report permission changes and diff only reports changes
4812 4825 relative to one merge parent.
4813 4826
4814 4827 If one revision is given, it is used as the base revision.
4815 4828 If two revisions are given, the differences between them are
4816 4829 shown. The --change option can also be used as a shortcut to list
4817 4830 the changed files of a revision from its first parent.
4818 4831
4819 4832 The codes used to show the status of files are::
4820 4833
4821 4834 M = modified
4822 4835 A = added
4823 4836 R = removed
4824 4837 C = clean
4825 4838 ! = missing (deleted by non-hg command, but still tracked)
4826 4839 ? = not tracked
4827 4840 I = ignored
4828 4841 = origin of the previous file (with --copies)
4829 4842
4830 4843 .. container:: verbose
4831 4844
4832 4845 The -t/--terse option abbreviates the output by showing only the directory
4833 4846 name if all the files in it share the same status. The option takes an
4834 4847 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
4835 4848 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
4836 4849 for 'ignored' and 'c' for clean.
4837 4850
4838 4851 It abbreviates only those statuses which are passed. Note that clean and
4839 4852 ignored files are not displayed with '--terse ic' unless the -c/--clean
4840 4853 and -i/--ignored options are also used.
4841 4854
4842 4855 The -v/--verbose option shows information when the repository is in an
4843 4856 unfinished merge, shelve, rebase state etc. You can have this behavior
4844 4857 turned on by default by enabling the ``commands.status.verbose`` option.
4845 4858
4846 4859 You can skip displaying some of these states by setting
4847 4860 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
4848 4861 'histedit', 'merge', 'rebase', or 'unshelve'.
4849 4862
4850 4863 Examples:
4851 4864
4852 4865 - show changes in the working directory relative to a
4853 4866 changeset::
4854 4867
4855 4868 hg status --rev 9353
4856 4869
4857 4870 - show changes in the working directory relative to the
4858 4871 current directory (see :hg:`help patterns` for more information)::
4859 4872
4860 4873 hg status re:
4861 4874
4862 4875 - show all changes including copies in an existing changeset::
4863 4876
4864 4877 hg status --copies --change 9353
4865 4878
4866 4879 - get a NUL separated list of added files, suitable for xargs::
4867 4880
4868 4881 hg status -an0
4869 4882
4870 4883 - show more information about the repository status, abbreviating
4871 4884 added, removed, modified, deleted, and untracked paths::
4872 4885
4873 4886 hg status -v -t mardu
4874 4887
4875 4888 Returns 0 on success.
4876 4889
4877 4890 """
4878 4891
4879 4892 opts = pycompat.byteskwargs(opts)
4880 4893 revs = opts.get('rev')
4881 4894 change = opts.get('change')
4882 4895 terse = opts.get('terse')
4883 4896
4884 4897 if revs and change:
4885 4898 msg = _('cannot specify --rev and --change at the same time')
4886 4899 raise error.Abort(msg)
4887 4900 elif revs and terse:
4888 4901 msg = _('cannot use --terse with --rev')
4889 4902 raise error.Abort(msg)
4890 4903 elif change:
4891 4904 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
4892 4905 ctx2 = scmutil.revsingle(repo, change, None)
4893 4906 ctx1 = ctx2.p1()
4894 4907 else:
4895 4908 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
4896 4909 ctx1, ctx2 = scmutil.revpair(repo, revs)
4897 4910
4898 4911 if pats or ui.configbool('commands', 'status.relative'):
4899 4912 cwd = repo.getcwd()
4900 4913 else:
4901 4914 cwd = ''
4902 4915
4903 4916 if opts.get('print0'):
4904 4917 end = '\0'
4905 4918 else:
4906 4919 end = '\n'
4907 4920 copy = {}
4908 4921 states = 'modified added removed deleted unknown ignored clean'.split()
4909 4922 show = [k for k in states if opts.get(k)]
4910 4923 if opts.get('all'):
4911 4924 show += ui.quiet and (states[:4] + ['clean']) or states
4912 4925
4913 4926 if not show:
4914 4927 if ui.quiet:
4915 4928 show = states[:4]
4916 4929 else:
4917 4930 show = states[:5]
4918 4931
4919 4932 m = scmutil.match(ctx2, pats, opts)
4920 4933 if terse:
4921 4934 # we need to compute clean and unknown to terse
4922 4935 stat = repo.status(ctx1.node(), ctx2.node(), m,
4923 4936 'ignored' in show or 'i' in terse,
4924 4937 True, True, opts.get('subrepos'))
4925 4938
4926 4939 stat = cmdutil.tersedir(stat, terse)
4927 4940 else:
4928 4941 stat = repo.status(ctx1.node(), ctx2.node(), m,
4929 4942 'ignored' in show, 'clean' in show,
4930 4943 'unknown' in show, opts.get('subrepos'))
4931 4944
4932 4945 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4933 4946
4934 4947 if (opts.get('all') or opts.get('copies')
4935 4948 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4936 4949 copy = copies.pathcopies(ctx1, ctx2, m)
4937 4950
4938 4951 ui.pager('status')
4939 4952 fm = ui.formatter('status', opts)
4940 4953 fmt = '%s' + end
4941 4954 showchar = not opts.get('no_status')
4942 4955
4943 4956 for state, char, files in changestates:
4944 4957 if state in show:
4945 4958 label = 'status.' + state
4946 4959 for f in files:
4947 4960 fm.startitem()
4948 4961 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4949 4962 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4950 4963 if f in copy:
4951 4964 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4952 4965 label='status.copied')
4953 4966
4954 4967 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
4955 4968 and not ui.plain()):
4956 4969 cmdutil.morestatus(repo, fm)
4957 4970 fm.end()
4958 4971
4959 4972 @command('^summary|sum',
4960 4973 [('', 'remote', None, _('check for push and pull'))],
4961 '[--remote]', cmdtype=readonly)
4974 '[--remote]',
4975 intents={INTENT_READONLY})
4962 4976 def summary(ui, repo, **opts):
4963 4977 """summarize working directory state
4964 4978
4965 4979 This generates a brief summary of the working directory state,
4966 4980 including parents, branch, commit status, phase and available updates.
4967 4981
4968 4982 With the --remote option, this will check the default paths for
4969 4983 incoming and outgoing changes. This can be time-consuming.
4970 4984
4971 4985 Returns 0 on success.
4972 4986 """
4973 4987
4974 4988 opts = pycompat.byteskwargs(opts)
4975 4989 ui.pager('summary')
4976 4990 ctx = repo[None]
4977 4991 parents = ctx.parents()
4978 4992 pnode = parents[0].node()
4979 4993 marks = []
4980 4994
4981 4995 ms = None
4982 4996 try:
4983 4997 ms = mergemod.mergestate.read(repo)
4984 4998 except error.UnsupportedMergeRecords as e:
4985 4999 s = ' '.join(e.recordtypes)
4986 5000 ui.warn(
4987 5001 _('warning: merge state has unsupported record types: %s\n') % s)
4988 5002 unresolved = []
4989 5003 else:
4990 5004 unresolved = list(ms.unresolved())
4991 5005
4992 5006 for p in parents:
4993 5007 # label with log.changeset (instead of log.parent) since this
4994 5008 # shows a working directory parent *changeset*:
4995 5009 # i18n: column positioning for "hg summary"
4996 5010 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4997 5011 label=logcmdutil.changesetlabels(p))
4998 5012 ui.write(' '.join(p.tags()), label='log.tag')
4999 5013 if p.bookmarks():
5000 5014 marks.extend(p.bookmarks())
5001 5015 if p.rev() == -1:
5002 5016 if not len(repo):
5003 5017 ui.write(_(' (empty repository)'))
5004 5018 else:
5005 5019 ui.write(_(' (no revision checked out)'))
5006 5020 if p.obsolete():
5007 5021 ui.write(_(' (obsolete)'))
5008 5022 if p.isunstable():
5009 5023 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5010 5024 for instability in p.instabilities())
5011 5025 ui.write(' ('
5012 5026 + ', '.join(instabilities)
5013 5027 + ')')
5014 5028 ui.write('\n')
5015 5029 if p.description():
5016 5030 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5017 5031 label='log.summary')
5018 5032
5019 5033 branch = ctx.branch()
5020 5034 bheads = repo.branchheads(branch)
5021 5035 # i18n: column positioning for "hg summary"
5022 5036 m = _('branch: %s\n') % branch
5023 5037 if branch != 'default':
5024 5038 ui.write(m, label='log.branch')
5025 5039 else:
5026 5040 ui.status(m, label='log.branch')
5027 5041
5028 5042 if marks:
5029 5043 active = repo._activebookmark
5030 5044 # i18n: column positioning for "hg summary"
5031 5045 ui.write(_('bookmarks:'), label='log.bookmark')
5032 5046 if active is not None:
5033 5047 if active in marks:
5034 5048 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5035 5049 marks.remove(active)
5036 5050 else:
5037 5051 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5038 5052 for m in marks:
5039 5053 ui.write(' ' + m, label='log.bookmark')
5040 5054 ui.write('\n', label='log.bookmark')
5041 5055
5042 5056 status = repo.status(unknown=True)
5043 5057
5044 5058 c = repo.dirstate.copies()
5045 5059 copied, renamed = [], []
5046 5060 for d, s in c.iteritems():
5047 5061 if s in status.removed:
5048 5062 status.removed.remove(s)
5049 5063 renamed.append(d)
5050 5064 else:
5051 5065 copied.append(d)
5052 5066 if d in status.added:
5053 5067 status.added.remove(d)
5054 5068
5055 5069 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5056 5070
5057 5071 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5058 5072 (ui.label(_('%d added'), 'status.added'), status.added),
5059 5073 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5060 5074 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5061 5075 (ui.label(_('%d copied'), 'status.copied'), copied),
5062 5076 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5063 5077 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5064 5078 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5065 5079 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5066 5080 t = []
5067 5081 for l, s in labels:
5068 5082 if s:
5069 5083 t.append(l % len(s))
5070 5084
5071 5085 t = ', '.join(t)
5072 5086 cleanworkdir = False
5073 5087
5074 5088 if repo.vfs.exists('graftstate'):
5075 5089 t += _(' (graft in progress)')
5076 5090 if repo.vfs.exists('updatestate'):
5077 5091 t += _(' (interrupted update)')
5078 5092 elif len(parents) > 1:
5079 5093 t += _(' (merge)')
5080 5094 elif branch != parents[0].branch():
5081 5095 t += _(' (new branch)')
5082 5096 elif (parents[0].closesbranch() and
5083 5097 pnode in repo.branchheads(branch, closed=True)):
5084 5098 t += _(' (head closed)')
5085 5099 elif not (status.modified or status.added or status.removed or renamed or
5086 5100 copied or subs):
5087 5101 t += _(' (clean)')
5088 5102 cleanworkdir = True
5089 5103 elif pnode not in bheads:
5090 5104 t += _(' (new branch head)')
5091 5105
5092 5106 if parents:
5093 5107 pendingphase = max(p.phase() for p in parents)
5094 5108 else:
5095 5109 pendingphase = phases.public
5096 5110
5097 5111 if pendingphase > phases.newcommitphase(ui):
5098 5112 t += ' (%s)' % phases.phasenames[pendingphase]
5099 5113
5100 5114 if cleanworkdir:
5101 5115 # i18n: column positioning for "hg summary"
5102 5116 ui.status(_('commit: %s\n') % t.strip())
5103 5117 else:
5104 5118 # i18n: column positioning for "hg summary"
5105 5119 ui.write(_('commit: %s\n') % t.strip())
5106 5120
5107 5121 # all ancestors of branch heads - all ancestors of parent = new csets
5108 5122 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5109 5123 bheads))
5110 5124
5111 5125 if new == 0:
5112 5126 # i18n: column positioning for "hg summary"
5113 5127 ui.status(_('update: (current)\n'))
5114 5128 elif pnode not in bheads:
5115 5129 # i18n: column positioning for "hg summary"
5116 5130 ui.write(_('update: %d new changesets (update)\n') % new)
5117 5131 else:
5118 5132 # i18n: column positioning for "hg summary"
5119 5133 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5120 5134 (new, len(bheads)))
5121 5135
5122 5136 t = []
5123 5137 draft = len(repo.revs('draft()'))
5124 5138 if draft:
5125 5139 t.append(_('%d draft') % draft)
5126 5140 secret = len(repo.revs('secret()'))
5127 5141 if secret:
5128 5142 t.append(_('%d secret') % secret)
5129 5143
5130 5144 if draft or secret:
5131 5145 ui.status(_('phases: %s\n') % ', '.join(t))
5132 5146
5133 5147 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5134 5148 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5135 5149 numtrouble = len(repo.revs(trouble + "()"))
5136 5150 # We write all the possibilities to ease translation
5137 5151 troublemsg = {
5138 5152 "orphan": _("orphan: %d changesets"),
5139 5153 "contentdivergent": _("content-divergent: %d changesets"),
5140 5154 "phasedivergent": _("phase-divergent: %d changesets"),
5141 5155 }
5142 5156 if numtrouble > 0:
5143 5157 ui.status(troublemsg[trouble] % numtrouble + "\n")
5144 5158
5145 5159 cmdutil.summaryhooks(ui, repo)
5146 5160
5147 5161 if opts.get('remote'):
5148 5162 needsincoming, needsoutgoing = True, True
5149 5163 else:
5150 5164 needsincoming, needsoutgoing = False, False
5151 5165 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5152 5166 if i:
5153 5167 needsincoming = True
5154 5168 if o:
5155 5169 needsoutgoing = True
5156 5170 if not needsincoming and not needsoutgoing:
5157 5171 return
5158 5172
5159 5173 def getincoming():
5160 5174 source, branches = hg.parseurl(ui.expandpath('default'))
5161 5175 sbranch = branches[0]
5162 5176 try:
5163 5177 other = hg.peer(repo, {}, source)
5164 5178 except error.RepoError:
5165 5179 if opts.get('remote'):
5166 5180 raise
5167 5181 return source, sbranch, None, None, None
5168 5182 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5169 5183 if revs:
5170 5184 revs = [other.lookup(rev) for rev in revs]
5171 5185 ui.debug('comparing with %s\n' % util.hidepassword(source))
5172 5186 repo.ui.pushbuffer()
5173 5187 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5174 5188 repo.ui.popbuffer()
5175 5189 return source, sbranch, other, commoninc, commoninc[1]
5176 5190
5177 5191 if needsincoming:
5178 5192 source, sbranch, sother, commoninc, incoming = getincoming()
5179 5193 else:
5180 5194 source = sbranch = sother = commoninc = incoming = None
5181 5195
5182 5196 def getoutgoing():
5183 5197 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5184 5198 dbranch = branches[0]
5185 5199 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5186 5200 if source != dest:
5187 5201 try:
5188 5202 dother = hg.peer(repo, {}, dest)
5189 5203 except error.RepoError:
5190 5204 if opts.get('remote'):
5191 5205 raise
5192 5206 return dest, dbranch, None, None
5193 5207 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5194 5208 elif sother is None:
5195 5209 # there is no explicit destination peer, but source one is invalid
5196 5210 return dest, dbranch, None, None
5197 5211 else:
5198 5212 dother = sother
5199 5213 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5200 5214 common = None
5201 5215 else:
5202 5216 common = commoninc
5203 5217 if revs:
5204 5218 revs = [repo.lookup(rev) for rev in revs]
5205 5219 repo.ui.pushbuffer()
5206 5220 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5207 5221 commoninc=common)
5208 5222 repo.ui.popbuffer()
5209 5223 return dest, dbranch, dother, outgoing
5210 5224
5211 5225 if needsoutgoing:
5212 5226 dest, dbranch, dother, outgoing = getoutgoing()
5213 5227 else:
5214 5228 dest = dbranch = dother = outgoing = None
5215 5229
5216 5230 if opts.get('remote'):
5217 5231 t = []
5218 5232 if incoming:
5219 5233 t.append(_('1 or more incoming'))
5220 5234 o = outgoing.missing
5221 5235 if o:
5222 5236 t.append(_('%d outgoing') % len(o))
5223 5237 other = dother or sother
5224 5238 if 'bookmarks' in other.listkeys('namespaces'):
5225 5239 counts = bookmarks.summary(repo, other)
5226 5240 if counts[0] > 0:
5227 5241 t.append(_('%d incoming bookmarks') % counts[0])
5228 5242 if counts[1] > 0:
5229 5243 t.append(_('%d outgoing bookmarks') % counts[1])
5230 5244
5231 5245 if t:
5232 5246 # i18n: column positioning for "hg summary"
5233 5247 ui.write(_('remote: %s\n') % (', '.join(t)))
5234 5248 else:
5235 5249 # i18n: column positioning for "hg summary"
5236 5250 ui.status(_('remote: (synced)\n'))
5237 5251
5238 5252 cmdutil.summaryremotehooks(ui, repo, opts,
5239 5253 ((source, sbranch, sother, commoninc),
5240 5254 (dest, dbranch, dother, outgoing)))
5241 5255
5242 5256 @command('tag',
5243 5257 [('f', 'force', None, _('force tag')),
5244 5258 ('l', 'local', None, _('make the tag local')),
5245 5259 ('r', 'rev', '', _('revision to tag'), _('REV')),
5246 5260 ('', 'remove', None, _('remove a tag')),
5247 5261 # -l/--local is already there, commitopts cannot be used
5248 5262 ('e', 'edit', None, _('invoke editor on commit messages')),
5249 5263 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5250 5264 ] + commitopts2,
5251 5265 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5252 5266 def tag(ui, repo, name1, *names, **opts):
5253 5267 """add one or more tags for the current or given revision
5254 5268
5255 5269 Name a particular revision using <name>.
5256 5270
5257 5271 Tags are used to name particular revisions of the repository and are
5258 5272 very useful to compare different revisions, to go back to significant
5259 5273 earlier versions or to mark branch points as releases, etc. Changing
5260 5274 an existing tag is normally disallowed; use -f/--force to override.
5261 5275
5262 5276 If no revision is given, the parent of the working directory is
5263 5277 used.
5264 5278
5265 5279 To facilitate version control, distribution, and merging of tags,
5266 5280 they are stored as a file named ".hgtags" which is managed similarly
5267 5281 to other project files and can be hand-edited if necessary. This
5268 5282 also means that tagging creates a new commit. The file
5269 5283 ".hg/localtags" is used for local tags (not shared among
5270 5284 repositories).
5271 5285
5272 5286 Tag commits are usually made at the head of a branch. If the parent
5273 5287 of the working directory is not a branch head, :hg:`tag` aborts; use
5274 5288 -f/--force to force the tag commit to be based on a non-head
5275 5289 changeset.
5276 5290
5277 5291 See :hg:`help dates` for a list of formats valid for -d/--date.
5278 5292
5279 5293 Since tag names have priority over branch names during revision
5280 5294 lookup, using an existing branch name as a tag name is discouraged.
5281 5295
5282 5296 Returns 0 on success.
5283 5297 """
5284 5298 opts = pycompat.byteskwargs(opts)
5285 5299 wlock = lock = None
5286 5300 try:
5287 5301 wlock = repo.wlock()
5288 5302 lock = repo.lock()
5289 5303 rev_ = "."
5290 5304 names = [t.strip() for t in (name1,) + names]
5291 5305 if len(names) != len(set(names)):
5292 5306 raise error.Abort(_('tag names must be unique'))
5293 5307 for n in names:
5294 5308 scmutil.checknewlabel(repo, n, 'tag')
5295 5309 if not n:
5296 5310 raise error.Abort(_('tag names cannot consist entirely of '
5297 5311 'whitespace'))
5298 5312 if opts.get('rev') and opts.get('remove'):
5299 5313 raise error.Abort(_("--rev and --remove are incompatible"))
5300 5314 if opts.get('rev'):
5301 5315 rev_ = opts['rev']
5302 5316 message = opts.get('message')
5303 5317 if opts.get('remove'):
5304 5318 if opts.get('local'):
5305 5319 expectedtype = 'local'
5306 5320 else:
5307 5321 expectedtype = 'global'
5308 5322
5309 5323 for n in names:
5310 5324 if not repo.tagtype(n):
5311 5325 raise error.Abort(_("tag '%s' does not exist") % n)
5312 5326 if repo.tagtype(n) != expectedtype:
5313 5327 if expectedtype == 'global':
5314 5328 raise error.Abort(_("tag '%s' is not a global tag") % n)
5315 5329 else:
5316 5330 raise error.Abort(_("tag '%s' is not a local tag") % n)
5317 5331 rev_ = 'null'
5318 5332 if not message:
5319 5333 # we don't translate commit messages
5320 5334 message = 'Removed tag %s' % ', '.join(names)
5321 5335 elif not opts.get('force'):
5322 5336 for n in names:
5323 5337 if n in repo.tags():
5324 5338 raise error.Abort(_("tag '%s' already exists "
5325 5339 "(use -f to force)") % n)
5326 5340 if not opts.get('local'):
5327 5341 p1, p2 = repo.dirstate.parents()
5328 5342 if p2 != nullid:
5329 5343 raise error.Abort(_('uncommitted merge'))
5330 5344 bheads = repo.branchheads()
5331 5345 if not opts.get('force') and bheads and p1 not in bheads:
5332 5346 raise error.Abort(_('working directory is not at a branch head '
5333 5347 '(use -f to force)'))
5334 5348 node = scmutil.revsingle(repo, rev_).node()
5335 5349
5336 5350 if not message:
5337 5351 # we don't translate commit messages
5338 5352 message = ('Added tag %s for changeset %s' %
5339 5353 (', '.join(names), short(node)))
5340 5354
5341 5355 date = opts.get('date')
5342 5356 if date:
5343 5357 date = dateutil.parsedate(date)
5344 5358
5345 5359 if opts.get('remove'):
5346 5360 editform = 'tag.remove'
5347 5361 else:
5348 5362 editform = 'tag.add'
5349 5363 editor = cmdutil.getcommiteditor(editform=editform,
5350 5364 **pycompat.strkwargs(opts))
5351 5365
5352 5366 # don't allow tagging the null rev
5353 5367 if (not opts.get('remove') and
5354 5368 scmutil.revsingle(repo, rev_).rev() == nullrev):
5355 5369 raise error.Abort(_("cannot tag null revision"))
5356 5370
5357 5371 tagsmod.tag(repo, names, node, message, opts.get('local'),
5358 5372 opts.get('user'), date, editor=editor)
5359 5373 finally:
5360 5374 release(lock, wlock)
5361 5375
5362 @command('tags', formatteropts, '', cmdtype=readonly)
5376 @command('tags', formatteropts, '', intents={INTENT_READONLY})
5363 5377 def tags(ui, repo, **opts):
5364 5378 """list repository tags
5365 5379
5366 5380 This lists both regular and local tags. When the -v/--verbose
5367 5381 switch is used, a third column "local" is printed for local tags.
5368 5382 When the -q/--quiet switch is used, only the tag name is printed.
5369 5383
5370 5384 Returns 0 on success.
5371 5385 """
5372 5386
5373 5387 opts = pycompat.byteskwargs(opts)
5374 5388 ui.pager('tags')
5375 5389 fm = ui.formatter('tags', opts)
5376 5390 hexfunc = fm.hexfunc
5377 5391 tagtype = ""
5378 5392
5379 5393 for t, n in reversed(repo.tagslist()):
5380 5394 hn = hexfunc(n)
5381 5395 label = 'tags.normal'
5382 5396 tagtype = ''
5383 5397 if repo.tagtype(t) == 'local':
5384 5398 label = 'tags.local'
5385 5399 tagtype = 'local'
5386 5400
5387 5401 fm.startitem()
5388 5402 fm.write('tag', '%s', t, label=label)
5389 5403 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5390 5404 fm.condwrite(not ui.quiet, 'rev node', fmt,
5391 5405 repo.changelog.rev(n), hn, label=label)
5392 5406 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5393 5407 tagtype, label=label)
5394 5408 fm.plain('\n')
5395 5409 fm.end()
5396 5410
5397 5411 @command('tip',
5398 5412 [('p', 'patch', None, _('show patch')),
5399 5413 ('g', 'git', None, _('use git extended diff format')),
5400 5414 ] + templateopts,
5401 5415 _('[-p] [-g]'))
5402 5416 def tip(ui, repo, **opts):
5403 5417 """show the tip revision (DEPRECATED)
5404 5418
5405 5419 The tip revision (usually just called the tip) is the changeset
5406 5420 most recently added to the repository (and therefore the most
5407 5421 recently changed head).
5408 5422
5409 5423 If you have just made a commit, that commit will be the tip. If
5410 5424 you have just pulled changes from another repository, the tip of
5411 5425 that repository becomes the current tip. The "tip" tag is special
5412 5426 and cannot be renamed or assigned to a different changeset.
5413 5427
5414 5428 This command is deprecated, please use :hg:`heads` instead.
5415 5429
5416 5430 Returns 0 on success.
5417 5431 """
5418 5432 opts = pycompat.byteskwargs(opts)
5419 5433 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5420 5434 displayer.show(repo['tip'])
5421 5435 displayer.close()
5422 5436
5423 5437 @command('unbundle',
5424 5438 [('u', 'update', None,
5425 5439 _('update to new branch head if changesets were unbundled'))],
5426 5440 _('[-u] FILE...'))
5427 5441 def unbundle(ui, repo, fname1, *fnames, **opts):
5428 5442 """apply one or more bundle files
5429 5443
5430 5444 Apply one or more bundle files generated by :hg:`bundle`.
5431 5445
5432 5446 Returns 0 on success, 1 if an update has unresolved files.
5433 5447 """
5434 5448 fnames = (fname1,) + fnames
5435 5449
5436 5450 with repo.lock():
5437 5451 for fname in fnames:
5438 5452 f = hg.openpath(ui, fname)
5439 5453 gen = exchange.readbundle(ui, f, fname)
5440 5454 if isinstance(gen, streamclone.streamcloneapplier):
5441 5455 raise error.Abort(
5442 5456 _('packed bundles cannot be applied with '
5443 5457 '"hg unbundle"'),
5444 5458 hint=_('use "hg debugapplystreamclonebundle"'))
5445 5459 url = 'bundle:' + fname
5446 5460 try:
5447 5461 txnname = 'unbundle'
5448 5462 if not isinstance(gen, bundle2.unbundle20):
5449 5463 txnname = 'unbundle\n%s' % util.hidepassword(url)
5450 5464 with repo.transaction(txnname) as tr:
5451 5465 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5452 5466 url=url)
5453 5467 except error.BundleUnknownFeatureError as exc:
5454 5468 raise error.Abort(
5455 5469 _('%s: unknown bundle feature, %s') % (fname, exc),
5456 5470 hint=_("see https://mercurial-scm.org/"
5457 5471 "wiki/BundleFeature for more "
5458 5472 "information"))
5459 5473 modheads = bundle2.combinechangegroupresults(op)
5460 5474
5461 5475 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5462 5476
5463 5477 @command('^update|up|checkout|co',
5464 5478 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5465 5479 ('c', 'check', None, _('require clean working directory')),
5466 5480 ('m', 'merge', None, _('merge uncommitted changes')),
5467 5481 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5468 5482 ('r', 'rev', '', _('revision'), _('REV'))
5469 5483 ] + mergetoolopts,
5470 5484 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5471 5485 def update(ui, repo, node=None, **opts):
5472 5486 """update working directory (or switch revisions)
5473 5487
5474 5488 Update the repository's working directory to the specified
5475 5489 changeset. If no changeset is specified, update to the tip of the
5476 5490 current named branch and move the active bookmark (see :hg:`help
5477 5491 bookmarks`).
5478 5492
5479 5493 Update sets the working directory's parent revision to the specified
5480 5494 changeset (see :hg:`help parents`).
5481 5495
5482 5496 If the changeset is not a descendant or ancestor of the working
5483 5497 directory's parent and there are uncommitted changes, the update is
5484 5498 aborted. With the -c/--check option, the working directory is checked
5485 5499 for uncommitted changes; if none are found, the working directory is
5486 5500 updated to the specified changeset.
5487 5501
5488 5502 .. container:: verbose
5489 5503
5490 5504 The -C/--clean, -c/--check, and -m/--merge options control what
5491 5505 happens if the working directory contains uncommitted changes.
5492 5506 At most of one of them can be specified.
5493 5507
5494 5508 1. If no option is specified, and if
5495 5509 the requested changeset is an ancestor or descendant of
5496 5510 the working directory's parent, the uncommitted changes
5497 5511 are merged into the requested changeset and the merged
5498 5512 result is left uncommitted. If the requested changeset is
5499 5513 not an ancestor or descendant (that is, it is on another
5500 5514 branch), the update is aborted and the uncommitted changes
5501 5515 are preserved.
5502 5516
5503 5517 2. With the -m/--merge option, the update is allowed even if the
5504 5518 requested changeset is not an ancestor or descendant of
5505 5519 the working directory's parent.
5506 5520
5507 5521 3. With the -c/--check option, the update is aborted and the
5508 5522 uncommitted changes are preserved.
5509 5523
5510 5524 4. With the -C/--clean option, uncommitted changes are discarded and
5511 5525 the working directory is updated to the requested changeset.
5512 5526
5513 5527 To cancel an uncommitted merge (and lose your changes), use
5514 5528 :hg:`merge --abort`.
5515 5529
5516 5530 Use null as the changeset to remove the working directory (like
5517 5531 :hg:`clone -U`).
5518 5532
5519 5533 If you want to revert just one file to an older revision, use
5520 5534 :hg:`revert [-r REV] NAME`.
5521 5535
5522 5536 See :hg:`help dates` for a list of formats valid for -d/--date.
5523 5537
5524 5538 Returns 0 on success, 1 if there are unresolved files.
5525 5539 """
5526 5540 rev = opts.get(r'rev')
5527 5541 date = opts.get(r'date')
5528 5542 clean = opts.get(r'clean')
5529 5543 check = opts.get(r'check')
5530 5544 merge = opts.get(r'merge')
5531 5545 if rev and node:
5532 5546 raise error.Abort(_("please specify just one revision"))
5533 5547
5534 5548 if ui.configbool('commands', 'update.requiredest'):
5535 5549 if not node and not rev and not date:
5536 5550 raise error.Abort(_('you must specify a destination'),
5537 5551 hint=_('for example: hg update ".::"'))
5538 5552
5539 5553 if rev is None or rev == '':
5540 5554 rev = node
5541 5555
5542 5556 if date and rev is not None:
5543 5557 raise error.Abort(_("you can't specify a revision and a date"))
5544 5558
5545 5559 if len([x for x in (clean, check, merge) if x]) > 1:
5546 5560 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5547 5561 "or -m/--merge"))
5548 5562
5549 5563 updatecheck = None
5550 5564 if check:
5551 5565 updatecheck = 'abort'
5552 5566 elif merge:
5553 5567 updatecheck = 'none'
5554 5568
5555 5569 with repo.wlock():
5556 5570 cmdutil.clearunfinished(repo)
5557 5571
5558 5572 if date:
5559 5573 rev = cmdutil.finddate(ui, repo, date)
5560 5574
5561 5575 # if we defined a bookmark, we have to remember the original name
5562 5576 brev = rev
5563 5577 if rev:
5564 5578 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5565 5579 ctx = scmutil.revsingle(repo, rev, rev)
5566 5580 rev = ctx.rev()
5567 5581 if ctx.hidden():
5568 5582 ctxstr = ctx.hex()[:12]
5569 5583 ui.warn(_("updating to a hidden changeset %s\n") % ctxstr)
5570 5584
5571 5585 if ctx.obsolete():
5572 5586 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
5573 5587 ui.warn("(%s)\n" % obsfatemsg)
5574 5588
5575 5589 repo.ui.setconfig('ui', 'forcemerge', opts.get(r'tool'), 'update')
5576 5590
5577 5591 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5578 5592 updatecheck=updatecheck)
5579 5593
5580 5594 @command('verify', [])
5581 5595 def verify(ui, repo):
5582 5596 """verify the integrity of the repository
5583 5597
5584 5598 Verify the integrity of the current repository.
5585 5599
5586 5600 This will perform an extensive check of the repository's
5587 5601 integrity, validating the hashes and checksums of each entry in
5588 5602 the changelog, manifest, and tracked files, as well as the
5589 5603 integrity of their crosslinks and indices.
5590 5604
5591 5605 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5592 5606 for more information about recovery from corruption of the
5593 5607 repository.
5594 5608
5595 5609 Returns 0 on success, 1 if errors are encountered.
5596 5610 """
5597 5611 return hg.verify(repo)
5598 5612
5599 @command('version', [] + formatteropts, norepo=True, cmdtype=readonly)
5613 @command('version', [] + formatteropts, norepo=True,
5614 intents={INTENT_READONLY})
5600 5615 def version_(ui, **opts):
5601 5616 """output version and copyright information"""
5602 5617 opts = pycompat.byteskwargs(opts)
5603 5618 if ui.verbose:
5604 5619 ui.pager('version')
5605 5620 fm = ui.formatter("version", opts)
5606 5621 fm.startitem()
5607 5622 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5608 5623 util.version())
5609 5624 license = _(
5610 5625 "(see https://mercurial-scm.org for more information)\n"
5611 5626 "\nCopyright (C) 2005-2018 Matt Mackall and others\n"
5612 5627 "This is free software; see the source for copying conditions. "
5613 5628 "There is NO\nwarranty; "
5614 5629 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5615 5630 )
5616 5631 if not ui.quiet:
5617 5632 fm.plain(license)
5618 5633
5619 5634 if ui.verbose:
5620 5635 fm.plain(_("\nEnabled extensions:\n\n"))
5621 5636 # format names and versions into columns
5622 5637 names = []
5623 5638 vers = []
5624 5639 isinternals = []
5625 5640 for name, module in extensions.extensions():
5626 5641 names.append(name)
5627 5642 vers.append(extensions.moduleversion(module) or None)
5628 5643 isinternals.append(extensions.ismoduleinternal(module))
5629 5644 fn = fm.nested("extensions", tmpl='{name}\n')
5630 5645 if names:
5631 5646 namefmt = " %%-%ds " % max(len(n) for n in names)
5632 5647 places = [_("external"), _("internal")]
5633 5648 for n, v, p in zip(names, vers, isinternals):
5634 5649 fn.startitem()
5635 5650 fn.condwrite(ui.verbose, "name", namefmt, n)
5636 5651 if ui.verbose:
5637 5652 fn.plain("%s " % places[p])
5638 5653 fn.data(bundled=p)
5639 5654 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5640 5655 if ui.verbose:
5641 5656 fn.plain("\n")
5642 5657 fn.end()
5643 5658 fm.end()
5644 5659
5645 5660 def loadcmdtable(ui, name, cmdtable):
5646 5661 """Load command functions from specified cmdtable
5647 5662 """
5648 5663 overrides = [cmd for cmd in cmdtable if cmd in table]
5649 5664 if overrides:
5650 5665 ui.warn(_("extension '%s' overrides commands: %s\n")
5651 5666 % (name, " ".join(overrides)))
5652 5667 table.update(cmdtable)
@@ -1,1055 +1,1052 b''
1 1 # dispatch.py - command dispatching 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, print_function
9 9
10 10 import difflib
11 11 import errno
12 12 import getopt
13 13 import os
14 14 import pdb
15 15 import re
16 16 import signal
17 17 import sys
18 18 import time
19 19 import traceback
20 20
21 21
22 22 from .i18n import _
23 23
24 24 from . import (
25 25 cmdutil,
26 26 color,
27 27 commands,
28 28 demandimport,
29 29 encoding,
30 30 error,
31 31 extensions,
32 32 fancyopts,
33 33 help,
34 34 hg,
35 35 hook,
36 36 profiling,
37 37 pycompat,
38 registrar,
39 38 scmutil,
40 39 ui as uimod,
41 40 util,
42 41 )
43 42
44 43 from .utils import (
45 44 procutil,
46 45 stringutil,
47 46 )
48 47
49 unrecoverablewrite = registrar.command.unrecoverablewrite
50
51 48 class request(object):
52 49 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
53 50 ferr=None, prereposetups=None):
54 51 self.args = args
55 52 self.ui = ui
56 53 self.repo = repo
57 54
58 55 # input/output/error streams
59 56 self.fin = fin
60 57 self.fout = fout
61 58 self.ferr = ferr
62 59
63 60 # remember options pre-parsed by _earlyparseopts()
64 61 self.earlyoptions = {}
65 62
66 63 # reposetups which run before extensions, useful for chg to pre-fill
67 64 # low-level repo state (for example, changelog) before extensions.
68 65 self.prereposetups = prereposetups or []
69 66
70 67 def _runexithandlers(self):
71 68 exc = None
72 69 handlers = self.ui._exithandlers
73 70 try:
74 71 while handlers:
75 72 func, args, kwargs = handlers.pop()
76 73 try:
77 74 func(*args, **kwargs)
78 75 except: # re-raises below
79 76 if exc is None:
80 77 exc = sys.exc_info()[1]
81 78 self.ui.warn(('error in exit handlers:\n'))
82 79 self.ui.traceback(force=True)
83 80 finally:
84 81 if exc is not None:
85 82 raise exc
86 83
87 84 def run():
88 85 "run the command in sys.argv"
89 86 _initstdio()
90 87 req = request(pycompat.sysargv[1:])
91 88 err = None
92 89 try:
93 90 status = (dispatch(req) or 0)
94 91 except error.StdioError as e:
95 92 err = e
96 93 status = -1
97 94 if util.safehasattr(req.ui, 'fout'):
98 95 try:
99 96 req.ui.fout.flush()
100 97 except IOError as e:
101 98 err = e
102 99 status = -1
103 100 if util.safehasattr(req.ui, 'ferr'):
104 101 try:
105 102 if err is not None and err.errno != errno.EPIPE:
106 103 req.ui.ferr.write('abort: %s\n' %
107 104 encoding.strtolocal(err.strerror))
108 105 req.ui.ferr.flush()
109 106 # There's not much we can do about an I/O error here. So (possibly)
110 107 # change the status code and move on.
111 108 except IOError:
112 109 status = -1
113 110
114 111 _silencestdio()
115 112 sys.exit(status & 255)
116 113
117 114 if pycompat.ispy3:
118 115 def _initstdio():
119 116 pass
120 117
121 118 def _silencestdio():
122 119 for fp in (sys.stdout, sys.stderr):
123 120 # Check if the file is okay
124 121 try:
125 122 fp.flush()
126 123 continue
127 124 except IOError:
128 125 pass
129 126 # Otherwise mark it as closed to silence "Exception ignored in"
130 127 # message emitted by the interpreter finalizer. Be careful to
131 128 # not close procutil.stdout, which may be a fdopen-ed file object
132 129 # and its close() actually closes the underlying file descriptor.
133 130 try:
134 131 fp.close()
135 132 except IOError:
136 133 pass
137 134 else:
138 135 def _initstdio():
139 136 for fp in (sys.stdin, sys.stdout, sys.stderr):
140 137 procutil.setbinary(fp)
141 138
142 139 def _silencestdio():
143 140 pass
144 141
145 142 def _getsimilar(symbols, value):
146 143 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
147 144 # The cutoff for similarity here is pretty arbitrary. It should
148 145 # probably be investigated and tweaked.
149 146 return [s for s in symbols if sim(s) > 0.6]
150 147
151 148 def _reportsimilar(write, similar):
152 149 if len(similar) == 1:
153 150 write(_("(did you mean %s?)\n") % similar[0])
154 151 elif similar:
155 152 ss = ", ".join(sorted(similar))
156 153 write(_("(did you mean one of %s?)\n") % ss)
157 154
158 155 def _formatparse(write, inst):
159 156 similar = []
160 157 if isinstance(inst, error.UnknownIdentifier):
161 158 # make sure to check fileset first, as revset can invoke fileset
162 159 similar = _getsimilar(inst.symbols, inst.function)
163 160 if len(inst.args) > 1:
164 161 write(_("hg: parse error at %s: %s\n") %
165 162 (pycompat.bytestr(inst.args[1]), inst.args[0]))
166 163 if inst.args[0].startswith(' '):
167 164 write(_("unexpected leading whitespace\n"))
168 165 else:
169 166 write(_("hg: parse error: %s\n") % inst.args[0])
170 167 _reportsimilar(write, similar)
171 168 if inst.hint:
172 169 write(_("(%s)\n") % inst.hint)
173 170
174 171 def _formatargs(args):
175 172 return ' '.join(procutil.shellquote(a) for a in args)
176 173
177 174 def dispatch(req):
178 175 "run the command specified in req.args"
179 176 if req.ferr:
180 177 ferr = req.ferr
181 178 elif req.ui:
182 179 ferr = req.ui.ferr
183 180 else:
184 181 ferr = procutil.stderr
185 182
186 183 try:
187 184 if not req.ui:
188 185 req.ui = uimod.ui.load()
189 186 req.earlyoptions.update(_earlyparseopts(req.ui, req.args))
190 187 if req.earlyoptions['traceback']:
191 188 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
192 189
193 190 # set ui streams from the request
194 191 if req.fin:
195 192 req.ui.fin = req.fin
196 193 if req.fout:
197 194 req.ui.fout = req.fout
198 195 if req.ferr:
199 196 req.ui.ferr = req.ferr
200 197 except error.Abort as inst:
201 198 ferr.write(_("abort: %s\n") % inst)
202 199 if inst.hint:
203 200 ferr.write(_("(%s)\n") % inst.hint)
204 201 return -1
205 202 except error.ParseError as inst:
206 203 _formatparse(ferr.write, inst)
207 204 return -1
208 205
209 206 msg = _formatargs(req.args)
210 207 starttime = util.timer()
211 208 ret = None
212 209 try:
213 210 ret = _runcatch(req)
214 211 except error.ProgrammingError as inst:
215 212 req.ui.warn(_('** ProgrammingError: %s\n') % inst)
216 213 if inst.hint:
217 214 req.ui.warn(_('** (%s)\n') % inst.hint)
218 215 raise
219 216 except KeyboardInterrupt as inst:
220 217 try:
221 218 if isinstance(inst, error.SignalInterrupt):
222 219 msg = _("killed!\n")
223 220 else:
224 221 msg = _("interrupted!\n")
225 222 req.ui.warn(msg)
226 223 except error.SignalInterrupt:
227 224 # maybe pager would quit without consuming all the output, and
228 225 # SIGPIPE was raised. we cannot print anything in this case.
229 226 pass
230 227 except IOError as inst:
231 228 if inst.errno != errno.EPIPE:
232 229 raise
233 230 ret = -1
234 231 finally:
235 232 duration = util.timer() - starttime
236 233 req.ui.flush()
237 234 if req.ui.logblockedtimes:
238 235 req.ui._blockedtimes['command_duration'] = duration * 1000
239 236 req.ui.log('uiblocked', 'ui blocked ms',
240 237 **pycompat.strkwargs(req.ui._blockedtimes))
241 238 req.ui.log("commandfinish", "%s exited %d after %0.2f seconds\n",
242 239 msg, ret or 0, duration)
243 240 try:
244 241 req._runexithandlers()
245 242 except: # exiting, so no re-raises
246 243 ret = ret or -1
247 244 return ret
248 245
249 246 def _runcatch(req):
250 247 def catchterm(*args):
251 248 raise error.SignalInterrupt
252 249
253 250 ui = req.ui
254 251 try:
255 252 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
256 253 num = getattr(signal, name, None)
257 254 if num:
258 255 signal.signal(num, catchterm)
259 256 except ValueError:
260 257 pass # happens if called in a thread
261 258
262 259 def _runcatchfunc():
263 260 realcmd = None
264 261 try:
265 262 cmdargs = fancyopts.fancyopts(req.args[:], commands.globalopts, {})
266 263 cmd = cmdargs[0]
267 264 aliases, entry = cmdutil.findcmd(cmd, commands.table, False)
268 265 realcmd = aliases[0]
269 266 except (error.UnknownCommand, error.AmbiguousCommand,
270 267 IndexError, getopt.GetoptError):
271 268 # Don't handle this here. We know the command is
272 269 # invalid, but all we're worried about for now is that
273 270 # it's not a command that server operators expect to
274 271 # be safe to offer to users in a sandbox.
275 272 pass
276 273 if realcmd == 'serve' and '--stdio' in cmdargs:
277 274 # We want to constrain 'hg serve --stdio' instances pretty
278 275 # closely, as many shared-ssh access tools want to grant
279 276 # access to run *only* 'hg -R $repo serve --stdio'. We
280 277 # restrict to exactly that set of arguments, and prohibit
281 278 # any repo name that starts with '--' to prevent
282 279 # shenanigans wherein a user does something like pass
283 280 # --debugger or --config=ui.debugger=1 as a repo
284 281 # name. This used to actually run the debugger.
285 282 if (len(req.args) != 4 or
286 283 req.args[0] != '-R' or
287 284 req.args[1].startswith('--') or
288 285 req.args[2] != 'serve' or
289 286 req.args[3] != '--stdio'):
290 287 raise error.Abort(
291 288 _('potentially unsafe serve --stdio invocation: %r') %
292 289 (req.args,))
293 290
294 291 try:
295 292 debugger = 'pdb'
296 293 debugtrace = {
297 294 'pdb': pdb.set_trace
298 295 }
299 296 debugmortem = {
300 297 'pdb': pdb.post_mortem
301 298 }
302 299
303 300 # read --config before doing anything else
304 301 # (e.g. to change trust settings for reading .hg/hgrc)
305 302 cfgs = _parseconfig(req.ui, req.earlyoptions['config'])
306 303
307 304 if req.repo:
308 305 # copy configs that were passed on the cmdline (--config) to
309 306 # the repo ui
310 307 for sec, name, val in cfgs:
311 308 req.repo.ui.setconfig(sec, name, val, source='--config')
312 309
313 310 # developer config: ui.debugger
314 311 debugger = ui.config("ui", "debugger")
315 312 debugmod = pdb
316 313 if not debugger or ui.plain():
317 314 # if we are in HGPLAIN mode, then disable custom debugging
318 315 debugger = 'pdb'
319 316 elif req.earlyoptions['debugger']:
320 317 # This import can be slow for fancy debuggers, so only
321 318 # do it when absolutely necessary, i.e. when actual
322 319 # debugging has been requested
323 320 with demandimport.deactivated():
324 321 try:
325 322 debugmod = __import__(debugger)
326 323 except ImportError:
327 324 pass # Leave debugmod = pdb
328 325
329 326 debugtrace[debugger] = debugmod.set_trace
330 327 debugmortem[debugger] = debugmod.post_mortem
331 328
332 329 # enter the debugger before command execution
333 330 if req.earlyoptions['debugger']:
334 331 ui.warn(_("entering debugger - "
335 332 "type c to continue starting hg or h for help\n"))
336 333
337 334 if (debugger != 'pdb' and
338 335 debugtrace[debugger] == debugtrace['pdb']):
339 336 ui.warn(_("%s debugger specified "
340 337 "but its module was not found\n") % debugger)
341 338 with demandimport.deactivated():
342 339 debugtrace[debugger]()
343 340 try:
344 341 return _dispatch(req)
345 342 finally:
346 343 ui.flush()
347 344 except: # re-raises
348 345 # enter the debugger when we hit an exception
349 346 if req.earlyoptions['debugger']:
350 347 traceback.print_exc()
351 348 debugmortem[debugger](sys.exc_info()[2])
352 349 raise
353 350
354 351 return _callcatch(ui, _runcatchfunc)
355 352
356 353 def _callcatch(ui, func):
357 354 """like scmutil.callcatch but handles more high-level exceptions about
358 355 config parsing and commands. besides, use handlecommandexception to handle
359 356 uncaught exceptions.
360 357 """
361 358 try:
362 359 return scmutil.callcatch(ui, func)
363 360 except error.AmbiguousCommand as inst:
364 361 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
365 362 (inst.args[0], " ".join(inst.args[1])))
366 363 except error.CommandError as inst:
367 364 if inst.args[0]:
368 365 ui.pager('help')
369 366 msgbytes = pycompat.bytestr(inst.args[1])
370 367 ui.warn(_("hg %s: %s\n") % (inst.args[0], msgbytes))
371 368 commands.help_(ui, inst.args[0], full=False, command=True)
372 369 else:
373 370 ui.pager('help')
374 371 ui.warn(_("hg: %s\n") % inst.args[1])
375 372 commands.help_(ui, 'shortlist')
376 373 except error.ParseError as inst:
377 374 _formatparse(ui.warn, inst)
378 375 return -1
379 376 except error.UnknownCommand as inst:
380 377 nocmdmsg = _("hg: unknown command '%s'\n") % inst.args[0]
381 378 try:
382 379 # check if the command is in a disabled extension
383 380 # (but don't check for extensions themselves)
384 381 formatted = help.formattedhelp(ui, commands, inst.args[0],
385 382 unknowncmd=True)
386 383 ui.warn(nocmdmsg)
387 384 ui.write(formatted)
388 385 except (error.UnknownCommand, error.Abort):
389 386 suggested = False
390 387 if len(inst.args) == 2:
391 388 sim = _getsimilar(inst.args[1], inst.args[0])
392 389 if sim:
393 390 ui.warn(nocmdmsg)
394 391 _reportsimilar(ui.warn, sim)
395 392 suggested = True
396 393 if not suggested:
397 394 ui.pager('help')
398 395 ui.warn(nocmdmsg)
399 396 commands.help_(ui, 'shortlist')
400 397 except IOError:
401 398 raise
402 399 except KeyboardInterrupt:
403 400 raise
404 401 except: # probably re-raises
405 402 if not handlecommandexception(ui):
406 403 raise
407 404
408 405 return -1
409 406
410 407 def aliasargs(fn, givenargs):
411 408 args = []
412 409 # only care about alias 'args', ignore 'args' set by extensions.wrapfunction
413 410 if not util.safehasattr(fn, '_origfunc'):
414 411 args = getattr(fn, 'args', args)
415 412 if args:
416 413 cmd = ' '.join(map(procutil.shellquote, args))
417 414
418 415 nums = []
419 416 def replacer(m):
420 417 num = int(m.group(1)) - 1
421 418 nums.append(num)
422 419 if num < len(givenargs):
423 420 return givenargs[num]
424 421 raise error.Abort(_('too few arguments for command alias'))
425 422 cmd = re.sub(br'\$(\d+|\$)', replacer, cmd)
426 423 givenargs = [x for i, x in enumerate(givenargs)
427 424 if i not in nums]
428 425 args = pycompat.shlexsplit(cmd)
429 426 return args + givenargs
430 427
431 428 def aliasinterpolate(name, args, cmd):
432 429 '''interpolate args into cmd for shell aliases
433 430
434 431 This also handles $0, $@ and "$@".
435 432 '''
436 433 # util.interpolate can't deal with "$@" (with quotes) because it's only
437 434 # built to match prefix + patterns.
438 435 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
439 436 replacemap['$0'] = name
440 437 replacemap['$$'] = '$'
441 438 replacemap['$@'] = ' '.join(args)
442 439 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
443 440 # parameters, separated out into words. Emulate the same behavior here by
444 441 # quoting the arguments individually. POSIX shells will then typically
445 442 # tokenize each argument into exactly one word.
446 443 replacemap['"$@"'] = ' '.join(procutil.shellquote(arg) for arg in args)
447 444 # escape '\$' for regex
448 445 regex = '|'.join(replacemap.keys()).replace('$', br'\$')
449 446 r = re.compile(regex)
450 447 return r.sub(lambda x: replacemap[x.group()], cmd)
451 448
452 449 class cmdalias(object):
453 450 def __init__(self, ui, name, definition, cmdtable, source):
454 451 self.name = self.cmd = name
455 452 self.cmdname = ''
456 453 self.definition = definition
457 454 self.fn = None
458 455 self.givenargs = []
459 456 self.opts = []
460 457 self.help = ''
461 458 self.badalias = None
462 459 self.unknowncmd = False
463 460 self.source = source
464 461
465 462 try:
466 463 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
467 464 for alias, e in cmdtable.iteritems():
468 465 if e is entry:
469 466 self.cmd = alias
470 467 break
471 468 self.shadows = True
472 469 except error.UnknownCommand:
473 470 self.shadows = False
474 471
475 472 if not self.definition:
476 473 self.badalias = _("no definition for alias '%s'") % self.name
477 474 return
478 475
479 476 if self.definition.startswith('!'):
480 477 shdef = self.definition[1:]
481 478 self.shell = True
482 479 def fn(ui, *args):
483 480 env = {'HG_ARGS': ' '.join((self.name,) + args)}
484 481 def _checkvar(m):
485 482 if m.groups()[0] == '$':
486 483 return m.group()
487 484 elif int(m.groups()[0]) <= len(args):
488 485 return m.group()
489 486 else:
490 487 ui.debug("No argument found for substitution "
491 488 "of %i variable in alias '%s' definition.\n"
492 489 % (int(m.groups()[0]), self.name))
493 490 return ''
494 491 cmd = re.sub(br'\$(\d+|\$)', _checkvar, shdef)
495 492 cmd = aliasinterpolate(self.name, args, cmd)
496 493 return ui.system(cmd, environ=env,
497 494 blockedtag='alias_%s' % self.name)
498 495 self.fn = fn
499 496 self._populatehelp(ui, name, shdef, self.fn)
500 497 return
501 498
502 499 try:
503 500 args = pycompat.shlexsplit(self.definition)
504 501 except ValueError as inst:
505 502 self.badalias = (_("error in definition for alias '%s': %s")
506 503 % (self.name, stringutil.forcebytestr(inst)))
507 504 return
508 505 earlyopts, args = _earlysplitopts(args)
509 506 if earlyopts:
510 507 self.badalias = (_("error in definition for alias '%s': %s may "
511 508 "only be given on the command line")
512 509 % (self.name, '/'.join(pycompat.ziplist(*earlyopts)
513 510 [0])))
514 511 return
515 512 self.cmdname = cmd = args.pop(0)
516 513 self.givenargs = args
517 514
518 515 try:
519 516 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
520 517 if len(tableentry) > 2:
521 518 self.fn, self.opts, cmdhelp = tableentry
522 519 else:
523 520 self.fn, self.opts = tableentry
524 521 cmdhelp = None
525 522
526 523 self._populatehelp(ui, name, cmd, self.fn, cmdhelp)
527 524
528 525 except error.UnknownCommand:
529 526 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
530 527 % (self.name, cmd))
531 528 self.unknowncmd = True
532 529 except error.AmbiguousCommand:
533 530 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
534 531 % (self.name, cmd))
535 532
536 533 def _populatehelp(self, ui, name, cmd, fn, defaulthelp=None):
537 534 # confine strings to be passed to i18n.gettext()
538 535 cfg = {}
539 536 for k in ('doc', 'help'):
540 537 v = ui.config('alias', '%s:%s' % (name, k), None)
541 538 if v is None:
542 539 continue
543 540 if not encoding.isasciistr(v):
544 541 self.badalias = (_("non-ASCII character in alias definition "
545 542 "'%s:%s'") % (name, k))
546 543 return
547 544 cfg[k] = v
548 545
549 546 self.help = cfg.get('help', defaulthelp or '')
550 547 if self.help and self.help.startswith("hg " + cmd):
551 548 # drop prefix in old-style help lines so hg shows the alias
552 549 self.help = self.help[4 + len(cmd):]
553 550
554 551 doc = cfg.get('doc', pycompat.getdoc(fn))
555 552 if doc is not None:
556 553 doc = pycompat.sysstr(doc)
557 554 self.__doc__ = doc
558 555
559 556 @property
560 557 def args(self):
561 558 args = pycompat.maplist(util.expandpath, self.givenargs)
562 559 return aliasargs(self.fn, args)
563 560
564 561 def __getattr__(self, name):
565 adefaults = {r'norepo': True, r'cmdtype': unrecoverablewrite,
562 adefaults = {r'norepo': True, r'intents': set(),
566 563 r'optionalrepo': False, r'inferrepo': False}
567 564 if name not in adefaults:
568 565 raise AttributeError(name)
569 566 if self.badalias or util.safehasattr(self, 'shell'):
570 567 return adefaults[name]
571 568 return getattr(self.fn, name)
572 569
573 570 def __call__(self, ui, *args, **opts):
574 571 if self.badalias:
575 572 hint = None
576 573 if self.unknowncmd:
577 574 try:
578 575 # check if the command is in a disabled extension
579 576 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
580 577 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
581 578 except error.UnknownCommand:
582 579 pass
583 580 raise error.Abort(self.badalias, hint=hint)
584 581 if self.shadows:
585 582 ui.debug("alias '%s' shadows command '%s'\n" %
586 583 (self.name, self.cmdname))
587 584
588 585 ui.log('commandalias', "alias '%s' expands to '%s'\n",
589 586 self.name, self.definition)
590 587 if util.safehasattr(self, 'shell'):
591 588 return self.fn(ui, *args, **opts)
592 589 else:
593 590 try:
594 591 return util.checksignature(self.fn)(ui, *args, **opts)
595 592 except error.SignatureError:
596 593 args = ' '.join([self.cmdname] + self.args)
597 594 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
598 595 raise
599 596
600 597 class lazyaliasentry(object):
601 598 """like a typical command entry (func, opts, help), but is lazy"""
602 599
603 600 def __init__(self, ui, name, definition, cmdtable, source):
604 601 self.ui = ui
605 602 self.name = name
606 603 self.definition = definition
607 604 self.cmdtable = cmdtable.copy()
608 605 self.source = source
609 606
610 607 @util.propertycache
611 608 def _aliasdef(self):
612 609 return cmdalias(self.ui, self.name, self.definition, self.cmdtable,
613 610 self.source)
614 611
615 612 def __getitem__(self, n):
616 613 aliasdef = self._aliasdef
617 614 if n == 0:
618 615 return aliasdef
619 616 elif n == 1:
620 617 return aliasdef.opts
621 618 elif n == 2:
622 619 return aliasdef.help
623 620 else:
624 621 raise IndexError
625 622
626 623 def __iter__(self):
627 624 for i in range(3):
628 625 yield self[i]
629 626
630 627 def __len__(self):
631 628 return 3
632 629
633 630 def addaliases(ui, cmdtable):
634 631 # aliases are processed after extensions have been loaded, so they
635 632 # may use extension commands. Aliases can also use other alias definitions,
636 633 # but only if they have been defined prior to the current definition.
637 634 for alias, definition in ui.configitems('alias', ignoresub=True):
638 635 try:
639 636 if cmdtable[alias].definition == definition:
640 637 continue
641 638 except (KeyError, AttributeError):
642 639 # definition might not exist or it might not be a cmdalias
643 640 pass
644 641
645 642 source = ui.configsource('alias', alias)
646 643 entry = lazyaliasentry(ui, alias, definition, cmdtable, source)
647 644 cmdtable[alias] = entry
648 645
649 646 def _parse(ui, args):
650 647 options = {}
651 648 cmdoptions = {}
652 649
653 650 try:
654 651 args = fancyopts.fancyopts(args, commands.globalopts, options)
655 652 except getopt.GetoptError as inst:
656 653 raise error.CommandError(None, stringutil.forcebytestr(inst))
657 654
658 655 if args:
659 656 cmd, args = args[0], args[1:]
660 657 aliases, entry = cmdutil.findcmd(cmd, commands.table,
661 658 ui.configbool("ui", "strict"))
662 659 cmd = aliases[0]
663 660 args = aliasargs(entry[0], args)
664 661 defaults = ui.config("defaults", cmd)
665 662 if defaults:
666 663 args = pycompat.maplist(
667 664 util.expandpath, pycompat.shlexsplit(defaults)) + args
668 665 c = list(entry[1])
669 666 else:
670 667 cmd = None
671 668 c = []
672 669
673 670 # combine global options into local
674 671 for o in commands.globalopts:
675 672 c.append((o[0], o[1], options[o[1]], o[3]))
676 673
677 674 try:
678 675 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
679 676 except getopt.GetoptError as inst:
680 677 raise error.CommandError(cmd, stringutil.forcebytestr(inst))
681 678
682 679 # separate global options back out
683 680 for o in commands.globalopts:
684 681 n = o[1]
685 682 options[n] = cmdoptions[n]
686 683 del cmdoptions[n]
687 684
688 685 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
689 686
690 687 def _parseconfig(ui, config):
691 688 """parse the --config options from the command line"""
692 689 configs = []
693 690
694 691 for cfg in config:
695 692 try:
696 693 name, value = [cfgelem.strip()
697 694 for cfgelem in cfg.split('=', 1)]
698 695 section, name = name.split('.', 1)
699 696 if not section or not name:
700 697 raise IndexError
701 698 ui.setconfig(section, name, value, '--config')
702 699 configs.append((section, name, value))
703 700 except (IndexError, ValueError):
704 701 raise error.Abort(_('malformed --config option: %r '
705 702 '(use --config section.name=value)')
706 703 % pycompat.bytestr(cfg))
707 704
708 705 return configs
709 706
710 707 def _earlyparseopts(ui, args):
711 708 options = {}
712 709 fancyopts.fancyopts(args, commands.globalopts, options,
713 710 gnu=not ui.plain('strictflags'), early=True,
714 711 optaliases={'repository': ['repo']})
715 712 return options
716 713
717 714 def _earlysplitopts(args):
718 715 """Split args into a list of possible early options and remainder args"""
719 716 shortoptions = 'R:'
720 717 # TODO: perhaps 'debugger' should be included
721 718 longoptions = ['cwd=', 'repository=', 'repo=', 'config=']
722 719 return fancyopts.earlygetopt(args, shortoptions, longoptions,
723 720 gnu=True, keepsep=True)
724 721
725 722 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
726 723 # run pre-hook, and abort if it fails
727 724 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
728 725 pats=cmdpats, opts=cmdoptions)
729 726 try:
730 727 ret = _runcommand(ui, options, cmd, d)
731 728 # run post-hook, passing command result
732 729 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
733 730 result=ret, pats=cmdpats, opts=cmdoptions)
734 731 except Exception:
735 732 # run failure hook and re-raise
736 733 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
737 734 pats=cmdpats, opts=cmdoptions)
738 735 raise
739 736 return ret
740 737
741 738 def _getlocal(ui, rpath, wd=None):
742 739 """Return (path, local ui object) for the given target path.
743 740
744 741 Takes paths in [cwd]/.hg/hgrc into account."
745 742 """
746 743 if wd is None:
747 744 try:
748 745 wd = pycompat.getcwd()
749 746 except OSError as e:
750 747 raise error.Abort(_("error getting current working directory: %s") %
751 748 encoding.strtolocal(e.strerror))
752 749 path = cmdutil.findrepo(wd) or ""
753 750 if not path:
754 751 lui = ui
755 752 else:
756 753 lui = ui.copy()
757 754 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
758 755
759 756 if rpath:
760 757 path = lui.expandpath(rpath)
761 758 lui = ui.copy()
762 759 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
763 760
764 761 return path, lui
765 762
766 763 def _checkshellalias(lui, ui, args):
767 764 """Return the function to run the shell alias, if it is required"""
768 765 options = {}
769 766
770 767 try:
771 768 args = fancyopts.fancyopts(args, commands.globalopts, options)
772 769 except getopt.GetoptError:
773 770 return
774 771
775 772 if not args:
776 773 return
777 774
778 775 cmdtable = commands.table
779 776
780 777 cmd = args[0]
781 778 try:
782 779 strict = ui.configbool("ui", "strict")
783 780 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
784 781 except (error.AmbiguousCommand, error.UnknownCommand):
785 782 return
786 783
787 784 cmd = aliases[0]
788 785 fn = entry[0]
789 786
790 787 if cmd and util.safehasattr(fn, 'shell'):
791 788 # shell alias shouldn't receive early options which are consumed by hg
792 789 _earlyopts, args = _earlysplitopts(args)
793 790 d = lambda: fn(ui, *args[1:])
794 791 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
795 792 [], {})
796 793
797 794 def _dispatch(req):
798 795 args = req.args
799 796 ui = req.ui
800 797
801 798 # check for cwd
802 799 cwd = req.earlyoptions['cwd']
803 800 if cwd:
804 801 os.chdir(cwd)
805 802
806 803 rpath = req.earlyoptions['repository']
807 804 path, lui = _getlocal(ui, rpath)
808 805
809 806 uis = {ui, lui}
810 807
811 808 if req.repo:
812 809 uis.add(req.repo.ui)
813 810
814 811 if req.earlyoptions['profile']:
815 812 for ui_ in uis:
816 813 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
817 814
818 815 profile = lui.configbool('profiling', 'enabled')
819 816 with profiling.profile(lui, enabled=profile) as profiler:
820 817 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
821 818 # reposetup
822 819 extensions.loadall(lui)
823 820 # Propagate any changes to lui.__class__ by extensions
824 821 ui.__class__ = lui.__class__
825 822
826 823 # (uisetup and extsetup are handled in extensions.loadall)
827 824
828 825 # (reposetup is handled in hg.repository)
829 826
830 827 addaliases(lui, commands.table)
831 828
832 829 # All aliases and commands are completely defined, now.
833 830 # Check abbreviation/ambiguity of shell alias.
834 831 shellaliasfn = _checkshellalias(lui, ui, args)
835 832 if shellaliasfn:
836 833 return shellaliasfn()
837 834
838 835 # check for fallback encoding
839 836 fallback = lui.config('ui', 'fallbackencoding')
840 837 if fallback:
841 838 encoding.fallbackencoding = fallback
842 839
843 840 fullargs = args
844 841 cmd, func, args, options, cmdoptions = _parse(lui, args)
845 842
846 843 if options["config"] != req.earlyoptions["config"]:
847 844 raise error.Abort(_("option --config may not be abbreviated!"))
848 845 if options["cwd"] != req.earlyoptions["cwd"]:
849 846 raise error.Abort(_("option --cwd may not be abbreviated!"))
850 847 if options["repository"] != req.earlyoptions["repository"]:
851 848 raise error.Abort(_(
852 849 "option -R has to be separated from other options (e.g. not "
853 850 "-qR) and --repository may only be abbreviated as --repo!"))
854 851 if options["debugger"] != req.earlyoptions["debugger"]:
855 852 raise error.Abort(_("option --debugger may not be abbreviated!"))
856 853 # don't validate --profile/--traceback, which can be enabled from now
857 854
858 855 if options["encoding"]:
859 856 encoding.encoding = options["encoding"]
860 857 if options["encodingmode"]:
861 858 encoding.encodingmode = options["encodingmode"]
862 859 if options["time"]:
863 860 def get_times():
864 861 t = os.times()
865 862 if t[4] == 0.0:
866 863 # Windows leaves this as zero, so use time.clock()
867 864 t = (t[0], t[1], t[2], t[3], time.clock())
868 865 return t
869 866 s = get_times()
870 867 def print_time():
871 868 t = get_times()
872 869 ui.warn(
873 870 _("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
874 871 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
875 872 ui.atexit(print_time)
876 873 if options["profile"]:
877 874 profiler.start()
878 875
879 876 if options['verbose'] or options['debug'] or options['quiet']:
880 877 for opt in ('verbose', 'debug', 'quiet'):
881 878 val = pycompat.bytestr(bool(options[opt]))
882 879 for ui_ in uis:
883 880 ui_.setconfig('ui', opt, val, '--' + opt)
884 881
885 882 if options['traceback']:
886 883 for ui_ in uis:
887 884 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
888 885
889 886 if options['noninteractive']:
890 887 for ui_ in uis:
891 888 ui_.setconfig('ui', 'interactive', 'off', '-y')
892 889
893 890 if cmdoptions.get('insecure', False):
894 891 for ui_ in uis:
895 892 ui_.insecureconnections = True
896 893
897 894 # setup color handling before pager, because setting up pager
898 895 # might cause incorrect console information
899 896 coloropt = options['color']
900 897 for ui_ in uis:
901 898 if coloropt:
902 899 ui_.setconfig('ui', 'color', coloropt, '--color')
903 900 color.setup(ui_)
904 901
905 902 if stringutil.parsebool(options['pager']):
906 903 # ui.pager() expects 'internal-always-' prefix in this case
907 904 ui.pager('internal-always-' + cmd)
908 905 elif options['pager'] != 'auto':
909 906 for ui_ in uis:
910 907 ui_.disablepager()
911 908
912 909 if options['version']:
913 910 return commands.version_(ui)
914 911 if options['help']:
915 912 return commands.help_(ui, cmd, command=cmd is not None)
916 913 elif not cmd:
917 914 return commands.help_(ui, 'shortlist')
918 915
919 916 repo = None
920 917 cmdpats = args[:]
921 918 if not func.norepo:
922 919 # use the repo from the request only if we don't have -R
923 920 if not rpath and not cwd:
924 921 repo = req.repo
925 922
926 923 if repo:
927 924 # set the descriptors of the repo ui to those of ui
928 925 repo.ui.fin = ui.fin
929 926 repo.ui.fout = ui.fout
930 927 repo.ui.ferr = ui.ferr
931 928 else:
932 929 try:
933 930 repo = hg.repository(ui, path=path,
934 931 presetupfuncs=req.prereposetups)
935 932 if not repo.local():
936 933 raise error.Abort(_("repository '%s' is not local")
937 934 % path)
938 935 repo.ui.setconfig("bundle", "mainreporoot", repo.root,
939 936 'repo')
940 937 except error.RequirementError:
941 938 raise
942 939 except error.RepoError:
943 940 if rpath: # invalid -R path
944 941 raise
945 942 if not func.optionalrepo:
946 943 if func.inferrepo and args and not path:
947 944 # try to infer -R from command args
948 945 repos = pycompat.maplist(cmdutil.findrepo, args)
949 946 guess = repos[0]
950 947 if guess and repos.count(guess) == len(repos):
951 948 req.args = ['--repository', guess] + fullargs
952 949 req.earlyoptions['repository'] = guess
953 950 return _dispatch(req)
954 951 if not path:
955 952 raise error.RepoError(_("no repository found in"
956 953 " '%s' (.hg not found)")
957 954 % pycompat.getcwd())
958 955 raise
959 956 if repo:
960 957 ui = repo.ui
961 958 if options['hidden']:
962 959 repo = repo.unfiltered()
963 960 args.insert(0, repo)
964 961 elif rpath:
965 962 ui.warn(_("warning: --repository ignored\n"))
966 963
967 964 msg = _formatargs(fullargs)
968 965 ui.log("command", '%s\n', msg)
969 966 strcmdopt = pycompat.strkwargs(cmdoptions)
970 967 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
971 968 try:
972 969 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
973 970 cmdpats, cmdoptions)
974 971 finally:
975 972 if repo and repo != req.repo:
976 973 repo.close()
977 974
978 975 def _runcommand(ui, options, cmd, cmdfunc):
979 976 """Run a command function, possibly with profiling enabled."""
980 977 try:
981 978 return cmdfunc()
982 979 except error.SignatureError:
983 980 raise error.CommandError(cmd, _('invalid arguments'))
984 981
985 982 def _exceptionwarning(ui):
986 983 """Produce a warning message for the current active exception"""
987 984
988 985 # For compatibility checking, we discard the portion of the hg
989 986 # version after the + on the assumption that if a "normal
990 987 # user" is running a build with a + in it the packager
991 988 # probably built from fairly close to a tag and anyone with a
992 989 # 'make local' copy of hg (where the version number can be out
993 990 # of date) will be clueful enough to notice the implausible
994 991 # version number and try updating.
995 992 ct = util.versiontuple(n=2)
996 993 worst = None, ct, ''
997 994 if ui.config('ui', 'supportcontact') is None:
998 995 for name, mod in extensions.extensions():
999 996 # 'testedwith' should be bytes, but not all extensions are ported
1000 997 # to py3 and we don't want UnicodeException because of that.
1001 998 testedwith = stringutil.forcebytestr(getattr(mod, 'testedwith', ''))
1002 999 report = getattr(mod, 'buglink', _('the extension author.'))
1003 1000 if not testedwith.strip():
1004 1001 # We found an untested extension. It's likely the culprit.
1005 1002 worst = name, 'unknown', report
1006 1003 break
1007 1004
1008 1005 # Never blame on extensions bundled with Mercurial.
1009 1006 if extensions.ismoduleinternal(mod):
1010 1007 continue
1011 1008
1012 1009 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
1013 1010 if ct in tested:
1014 1011 continue
1015 1012
1016 1013 lower = [t for t in tested if t < ct]
1017 1014 nearest = max(lower or tested)
1018 1015 if worst[0] is None or nearest < worst[1]:
1019 1016 worst = name, nearest, report
1020 1017 if worst[0] is not None:
1021 1018 name, testedwith, report = worst
1022 1019 if not isinstance(testedwith, (bytes, str)):
1023 1020 testedwith = '.'.join([stringutil.forcebytestr(c)
1024 1021 for c in testedwith])
1025 1022 warning = (_('** Unknown exception encountered with '
1026 1023 'possibly-broken third-party extension %s\n'
1027 1024 '** which supports versions %s of Mercurial.\n'
1028 1025 '** Please disable %s and try your action again.\n'
1029 1026 '** If that fixes the bug please report it to %s\n')
1030 1027 % (name, testedwith, name, report))
1031 1028 else:
1032 1029 bugtracker = ui.config('ui', 'supportcontact')
1033 1030 if bugtracker is None:
1034 1031 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
1035 1032 warning = (_("** unknown exception encountered, "
1036 1033 "please report by visiting\n** ") + bugtracker + '\n')
1037 1034 sysversion = pycompat.sysbytes(sys.version).replace('\n', '')
1038 1035 warning += ((_("** Python %s\n") % sysversion) +
1039 1036 (_("** Mercurial Distributed SCM (version %s)\n") %
1040 1037 util.version()) +
1041 1038 (_("** Extensions loaded: %s\n") %
1042 1039 ", ".join([x[0] for x in extensions.extensions()])))
1043 1040 return warning
1044 1041
1045 1042 def handlecommandexception(ui):
1046 1043 """Produce a warning message for broken commands
1047 1044
1048 1045 Called when handling an exception; the exception is reraised if
1049 1046 this function returns False, ignored otherwise.
1050 1047 """
1051 1048 warning = _exceptionwarning(ui)
1052 1049 ui.log("commandexception", "%s\n%s\n", warning,
1053 1050 pycompat.sysbytes(traceback.format_exc()))
1054 1051 ui.warn(warning)
1055 1052 return False # re-raise the exception
@@ -1,443 +1,439 b''
1 1 # registrar.py - utilities to register function for specific purpose
2 2 #
3 3 # Copyright FUJIWARA Katsunori <foozy@lares.dti.ne.jp> and others
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 from . import (
11 11 configitems,
12 12 error,
13 13 pycompat,
14 14 util,
15 15 )
16 16
17 17 # unlike the other registered items, config options are neither functions or
18 18 # classes. Registering the option is just small function call.
19 19 #
20 20 # We still add the official API to the registrar module for consistency with
21 21 # the other items extensions want might to register.
22 22 configitem = configitems.getitemregister
23 23
24 24 class _funcregistrarbase(object):
25 25 """Base of decorator to register a function for specific purpose
26 26
27 27 This decorator stores decorated functions into own dict 'table'.
28 28
29 29 The least derived class can be defined by overriding 'formatdoc',
30 30 for example::
31 31
32 32 class keyword(_funcregistrarbase):
33 33 _docformat = ":%s: %s"
34 34
35 35 This should be used as below:
36 36
37 37 keyword = registrar.keyword()
38 38
39 39 @keyword('bar')
40 40 def barfunc(*args, **kwargs):
41 41 '''Explanation of bar keyword ....
42 42 '''
43 43 pass
44 44
45 45 In this case:
46 46
47 47 - 'barfunc' is stored as 'bar' in '_table' of an instance 'keyword' above
48 48 - 'barfunc.__doc__' becomes ":bar: Explanation of bar keyword"
49 49 """
50 50 def __init__(self, table=None):
51 51 if table is None:
52 52 self._table = {}
53 53 else:
54 54 self._table = table
55 55
56 56 def __call__(self, decl, *args, **kwargs):
57 57 return lambda func: self._doregister(func, decl, *args, **kwargs)
58 58
59 59 def _doregister(self, func, decl, *args, **kwargs):
60 60 name = self._getname(decl)
61 61
62 62 if name in self._table:
63 63 msg = 'duplicate registration for name: "%s"' % name
64 64 raise error.ProgrammingError(msg)
65 65
66 66 if func.__doc__ and not util.safehasattr(func, '_origdoc'):
67 67 doc = pycompat.sysbytes(func.__doc__).strip()
68 68 func._origdoc = doc
69 69 func.__doc__ = pycompat.sysstr(self._formatdoc(decl, doc))
70 70
71 71 self._table[name] = func
72 72 self._extrasetup(name, func, *args, **kwargs)
73 73
74 74 return func
75 75
76 76 def _parsefuncdecl(self, decl):
77 77 """Parse function declaration and return the name of function in it
78 78 """
79 79 i = decl.find('(')
80 80 if i >= 0:
81 81 return decl[:i]
82 82 else:
83 83 return decl
84 84
85 85 def _getname(self, decl):
86 86 """Return the name of the registered function from decl
87 87
88 88 Derived class should override this, if it allows more
89 89 descriptive 'decl' string than just a name.
90 90 """
91 91 return decl
92 92
93 93 _docformat = None
94 94
95 95 def _formatdoc(self, decl, doc):
96 96 """Return formatted document of the registered function for help
97 97
98 98 'doc' is '__doc__.strip()' of the registered function.
99 99 """
100 100 return self._docformat % (decl, doc)
101 101
102 102 def _extrasetup(self, name, func):
103 103 """Execute exra setup for registered function, if needed
104 104 """
105 105
106 106 class command(_funcregistrarbase):
107 107 """Decorator to register a command function to table
108 108
109 109 This class receives a command table as its argument. The table should
110 110 be a dict.
111 111
112 112 The created object can be used as a decorator for adding commands to
113 113 that command table. This accepts multiple arguments to define a command.
114 114
115 115 The first argument is the command name (as bytes).
116 116
117 117 The `options` keyword argument is an iterable of tuples defining command
118 118 arguments. See ``mercurial.fancyopts.fancyopts()`` for the format of each
119 119 tuple.
120 120
121 121 The `synopsis` argument defines a short, one line summary of how to use the
122 122 command. This shows up in the help output.
123 123
124 124 There are three arguments that control what repository (if any) is found
125 125 and passed to the decorated function: `norepo`, `optionalrepo`, and
126 126 `inferrepo`.
127 127
128 128 The `norepo` argument defines whether the command does not require a
129 129 local repository. Most commands operate against a repository, thus the
130 130 default is False. When True, no repository will be passed.
131 131
132 132 The `optionalrepo` argument defines whether the command optionally requires
133 133 a local repository. If no repository can be found, None will be passed
134 134 to the decorated function.
135 135
136 136 The `inferrepo` argument defines whether to try to find a repository from
137 137 the command line arguments. If True, arguments will be examined for
138 138 potential repository locations. See ``findrepo()``. If a repository is
139 139 found, it will be used and passed to the decorated function.
140 140
141 There are three constants in the class which tells what type of the command
142 that is. That information will be helpful at various places. It will be also
143 be used to decide what level of access the command has on hidden commits.
144 The constants are:
141 The `intents` argument defines a set of intended actions or capabilities
142 the command is taking. These intents can be used to affect the construction
143 of the repository object passed to the command. For example, commands
144 declaring that they are read-only could receive a repository that doesn't
145 have any methods allowing repository mutation. Other intents could be used
146 to prevent the command from running if the requested intent could not be
147 fulfilled.
145 148
146 `unrecoverablewrite` is for those write commands which can't be recovered
147 like push.
148 `recoverablewrite` is for write commands which can be recovered like commit.
149 `readonly` is for commands which are read only.
149 The following intents are defined:
150
151 readonly
152 The command is read-only
150 153
151 154 The signature of the decorated function looks like this:
152 155 def cmd(ui[, repo] [, <args>] [, <options>])
153 156
154 157 `repo` is required if `norepo` is False.
155 158 `<args>` are positional args (or `*args`) arguments, of non-option
156 159 arguments from the command line.
157 160 `<options>` are keyword arguments (or `**options`) of option arguments
158 161 from the command line.
159 162
160 163 See the WritingExtensions and MercurialApi documentation for more exhaustive
161 164 descriptions and examples.
162 165 """
163 166
164 unrecoverablewrite = "unrecoverable"
165 recoverablewrite = "recoverable"
166 readonly = "readonly"
167
168 possiblecmdtypes = {unrecoverablewrite, recoverablewrite, readonly}
169
170 167 def _doregister(self, func, name, options=(), synopsis=None,
171 168 norepo=False, optionalrepo=False, inferrepo=False,
172 cmdtype=unrecoverablewrite):
169 intents=None):
173 170
174 if cmdtype not in self.possiblecmdtypes:
175 raise error.ProgrammingError("unknown cmdtype value '%s' for "
176 "'%s' command" % (cmdtype, name))
177 171 func.norepo = norepo
178 172 func.optionalrepo = optionalrepo
179 173 func.inferrepo = inferrepo
180 func.cmdtype = cmdtype
174 func.intents = intents or set()
181 175 if synopsis:
182 176 self._table[name] = func, list(options), synopsis
183 177 else:
184 178 self._table[name] = func, list(options)
185 179 return func
186 180
181 INTENT_READONLY = b'readonly'
182
187 183 class revsetpredicate(_funcregistrarbase):
188 184 """Decorator to register revset predicate
189 185
190 186 Usage::
191 187
192 188 revsetpredicate = registrar.revsetpredicate()
193 189
194 190 @revsetpredicate('mypredicate(arg1, arg2[, arg3])')
195 191 def mypredicatefunc(repo, subset, x):
196 192 '''Explanation of this revset predicate ....
197 193 '''
198 194 pass
199 195
200 196 The first string argument is used also in online help.
201 197
202 198 Optional argument 'safe' indicates whether a predicate is safe for
203 199 DoS attack (False by default).
204 200
205 201 Optional argument 'takeorder' indicates whether a predicate function
206 202 takes ordering policy as the last argument.
207 203
208 204 Optional argument 'weight' indicates the estimated run-time cost, useful
209 205 for static optimization, default is 1. Higher weight means more expensive.
210 206 Usually, revsets that are fast and return only one revision has a weight of
211 207 0.5 (ex. a symbol); revsets with O(changelog) complexity and read only the
212 208 changelog have weight 10 (ex. author); revsets reading manifest deltas have
213 209 weight 30 (ex. adds); revset reading manifest contents have weight 100
214 210 (ex. contains). Note: those values are flexible. If the revset has a
215 211 same big-O time complexity as 'contains', but with a smaller constant, it
216 212 might have a weight of 90.
217 213
218 214 'revsetpredicate' instance in example above can be used to
219 215 decorate multiple functions.
220 216
221 217 Decorated functions are registered automatically at loading
222 218 extension, if an instance named as 'revsetpredicate' is used for
223 219 decorating in extension.
224 220
225 221 Otherwise, explicit 'revset.loadpredicate()' is needed.
226 222 """
227 223 _getname = _funcregistrarbase._parsefuncdecl
228 224 _docformat = "``%s``\n %s"
229 225
230 226 def _extrasetup(self, name, func, safe=False, takeorder=False, weight=1):
231 227 func._safe = safe
232 228 func._takeorder = takeorder
233 229 func._weight = weight
234 230
235 231 class filesetpredicate(_funcregistrarbase):
236 232 """Decorator to register fileset predicate
237 233
238 234 Usage::
239 235
240 236 filesetpredicate = registrar.filesetpredicate()
241 237
242 238 @filesetpredicate('mypredicate()')
243 239 def mypredicatefunc(mctx, x):
244 240 '''Explanation of this fileset predicate ....
245 241 '''
246 242 pass
247 243
248 244 The first string argument is used also in online help.
249 245
250 246 Optional argument 'callstatus' indicates whether a predicate
251 247 implies 'matchctx.status()' at runtime or not (False, by
252 248 default).
253 249
254 250 Optional argument 'callexisting' indicates whether a predicate
255 251 implies 'matchctx.existing()' at runtime or not (False, by
256 252 default).
257 253
258 254 'filesetpredicate' instance in example above can be used to
259 255 decorate multiple functions.
260 256
261 257 Decorated functions are registered automatically at loading
262 258 extension, if an instance named as 'filesetpredicate' is used for
263 259 decorating in extension.
264 260
265 261 Otherwise, explicit 'fileset.loadpredicate()' is needed.
266 262 """
267 263 _getname = _funcregistrarbase._parsefuncdecl
268 264 _docformat = "``%s``\n %s"
269 265
270 266 def _extrasetup(self, name, func, callstatus=False, callexisting=False):
271 267 func._callstatus = callstatus
272 268 func._callexisting = callexisting
273 269
274 270 class _templateregistrarbase(_funcregistrarbase):
275 271 """Base of decorator to register functions as template specific one
276 272 """
277 273 _docformat = ":%s: %s"
278 274
279 275 class templatekeyword(_templateregistrarbase):
280 276 """Decorator to register template keyword
281 277
282 278 Usage::
283 279
284 280 templatekeyword = registrar.templatekeyword()
285 281
286 282 # new API (since Mercurial 4.6)
287 283 @templatekeyword('mykeyword', requires={'repo', 'ctx'})
288 284 def mykeywordfunc(context, mapping):
289 285 '''Explanation of this template keyword ....
290 286 '''
291 287 pass
292 288
293 289 # old API
294 290 @templatekeyword('mykeyword')
295 291 def mykeywordfunc(repo, ctx, templ, cache, revcache, **args):
296 292 '''Explanation of this template keyword ....
297 293 '''
298 294 pass
299 295
300 296 The first string argument is used also in online help.
301 297
302 298 Optional argument 'requires' should be a collection of resource names
303 299 which the template keyword depends on. This also serves as a flag to
304 300 switch to the new API. If 'requires' is unspecified, all template
305 301 keywords and resources are expanded to the function arguments.
306 302
307 303 'templatekeyword' instance in example above can be used to
308 304 decorate multiple functions.
309 305
310 306 Decorated functions are registered automatically at loading
311 307 extension, if an instance named as 'templatekeyword' is used for
312 308 decorating in extension.
313 309
314 310 Otherwise, explicit 'templatekw.loadkeyword()' is needed.
315 311 """
316 312
317 313 def _extrasetup(self, name, func, requires=None):
318 314 func._requires = requires
319 315
320 316 class templatefilter(_templateregistrarbase):
321 317 """Decorator to register template filer
322 318
323 319 Usage::
324 320
325 321 templatefilter = registrar.templatefilter()
326 322
327 323 @templatefilter('myfilter', intype=bytes)
328 324 def myfilterfunc(text):
329 325 '''Explanation of this template filter ....
330 326 '''
331 327 pass
332 328
333 329 The first string argument is used also in online help.
334 330
335 331 Optional argument 'intype' defines the type of the input argument,
336 332 which should be (bytes, int, templateutil.date, or None for any.)
337 333
338 334 'templatefilter' instance in example above can be used to
339 335 decorate multiple functions.
340 336
341 337 Decorated functions are registered automatically at loading
342 338 extension, if an instance named as 'templatefilter' is used for
343 339 decorating in extension.
344 340
345 341 Otherwise, explicit 'templatefilters.loadkeyword()' is needed.
346 342 """
347 343
348 344 def _extrasetup(self, name, func, intype=None):
349 345 func._intype = intype
350 346
351 347 class templatefunc(_templateregistrarbase):
352 348 """Decorator to register template function
353 349
354 350 Usage::
355 351
356 352 templatefunc = registrar.templatefunc()
357 353
358 354 @templatefunc('myfunc(arg1, arg2[, arg3])', argspec='arg1 arg2 arg3')
359 355 def myfuncfunc(context, mapping, args):
360 356 '''Explanation of this template function ....
361 357 '''
362 358 pass
363 359
364 360 The first string argument is used also in online help.
365 361
366 362 If optional 'argspec' is defined, the function will receive 'args' as
367 363 a dict of named arguments. Otherwise 'args' is a list of positional
368 364 arguments.
369 365
370 366 'templatefunc' instance in example above can be used to
371 367 decorate multiple functions.
372 368
373 369 Decorated functions are registered automatically at loading
374 370 extension, if an instance named as 'templatefunc' is used for
375 371 decorating in extension.
376 372
377 373 Otherwise, explicit 'templatefuncs.loadfunction()' is needed.
378 374 """
379 375 _getname = _funcregistrarbase._parsefuncdecl
380 376
381 377 def _extrasetup(self, name, func, argspec=None):
382 378 func._argspec = argspec
383 379
384 380 class internalmerge(_funcregistrarbase):
385 381 """Decorator to register in-process merge tool
386 382
387 383 Usage::
388 384
389 385 internalmerge = registrar.internalmerge()
390 386
391 387 @internalmerge('mymerge', internalmerge.mergeonly,
392 388 onfailure=None, precheck=None):
393 389 def mymergefunc(repo, mynode, orig, fcd, fco, fca,
394 390 toolconf, files, labels=None):
395 391 '''Explanation of this internal merge tool ....
396 392 '''
397 393 return 1, False # means "conflicted", "no deletion needed"
398 394
399 395 The first string argument is used to compose actual merge tool name,
400 396 ":name" and "internal:name" (the latter is historical one).
401 397
402 398 The second argument is one of merge types below:
403 399
404 400 ========== ======== ======== =========
405 401 merge type precheck premerge fullmerge
406 402 ========== ======== ======== =========
407 403 nomerge x x x
408 404 mergeonly o x o
409 405 fullmerge o o o
410 406 ========== ======== ======== =========
411 407
412 408 Optional argument 'onfailure' is the format of warning message
413 409 to be used at failure of merging (target filename is specified
414 410 at formatting). Or, None or so, if warning message should be
415 411 suppressed.
416 412
417 413 Optional argument 'precheck' is the function to be used
418 414 before actual invocation of internal merge tool itself.
419 415 It takes as same arguments as internal merge tool does, other than
420 416 'files' and 'labels'. If it returns false value, merging is aborted
421 417 immediately (and file is marked as "unresolved").
422 418
423 419 'internalmerge' instance in example above can be used to
424 420 decorate multiple functions.
425 421
426 422 Decorated functions are registered automatically at loading
427 423 extension, if an instance named as 'internalmerge' is used for
428 424 decorating in extension.
429 425
430 426 Otherwise, explicit 'filemerge.loadinternalmerge()' is needed.
431 427 """
432 428 _docformat = "``:%s``\n %s"
433 429
434 430 # merge type definitions:
435 431 nomerge = None
436 432 mergeonly = 'mergeonly' # just the full merge, no premerge
437 433 fullmerge = 'fullmerge' # both premerge and merge
438 434
439 435 def _extrasetup(self, name, func, mergetype,
440 436 onfailure=None, precheck=None):
441 437 func.mergetype = mergetype
442 438 func.onfailure = onfailure
443 439 func.precheck = precheck
@@ -1,214 +1,214 b''
1 1 Tests for access level on hidden commits by various commands on based of their
2 2 type.
3 3
4 4 Setting the required config to start this
5 5
6 6 $ cat >> $HGRCPATH <<EOF
7 7 > [experimental]
8 8 > evolution=createmarkers, allowunstable
9 9 > directaccess=True
10 10 > directaccess.revnums=True
11 11 > [extensions]
12 12 > amend =
13 13 > EOF
14 14
15 15 $ hg init repo
16 16 $ cd repo
17 17 $ for ch in a b c; do touch $ch; echo "foo" >> $ch; hg ci -Aqm "Added "$ch; done
18 18
19 19 $ hg log -G -T '{rev}:{node} {desc}' --hidden
20 20 @ 2:28ad74487de9599d00d81085be739c61fc340652 Added c
21 21 |
22 22 o 1:29becc82797a4bc11ec8880b58eaecd2ab3e7760 Added b
23 23 |
24 24 o 0:18d04c59bb5d2d4090ad9a5b59bd6274adb63add Added a
25 25
26 26 $ echo "bar" >> c
27 27 $ hg amend
28 28
29 29 $ hg log -G -T '{rev}:{node} {desc}' --hidden
30 30 @ 3:2443a0e664694756d8b435d06b6ad84f941b6fc0 Added c
31 31 |
32 32 | x 2:28ad74487de9599d00d81085be739c61fc340652 Added c
33 33 |/
34 34 o 1:29becc82797a4bc11ec8880b58eaecd2ab3e7760 Added b
35 35 |
36 36 o 0:18d04c59bb5d2d4090ad9a5b59bd6274adb63add Added a
37 37
38 38 Testing read only commands on the hidden revision
39 39
40 40 Testing with rev number
41 41
42 42 $ hg exp 2 --config experimental.directaccess.revnums=False
43 43 abort: hidden revision '2' was rewritten as: 2443a0e66469!
44 44 (use --hidden to access hidden revisions)
45 45 [255]
46 46
47 47 $ hg exp 2
48 48 # HG changeset patch
49 49 # User test
50 50 # Date 0 0
51 51 # Thu Jan 01 00:00:00 1970 +0000
52 52 # Node ID 28ad74487de9599d00d81085be739c61fc340652
53 53 # Parent 29becc82797a4bc11ec8880b58eaecd2ab3e7760
54 54 Added c
55 55
56 56 diff -r 29becc82797a -r 28ad74487de9 c
57 57 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
58 58 +++ b/c Thu Jan 01 00:00:00 1970 +0000
59 59 @@ -0,0 +1,1 @@
60 60 +foo
61 61
62 62 $ hg log -r 2
63 63 changeset: 2:28ad74487de9
64 64 user: test
65 65 date: Thu Jan 01 00:00:00 1970 +0000
66 66 obsolete: rewritten using amend as 3:2443a0e66469
67 67 summary: Added c
68 68
69 69 $ hg identify -r 2
70 70 28ad74487de9
71 71
72 72 $ hg status --change 2
73 73 A c
74 74
75 75 $ hg status --change 2 --config experimental.directaccess.revnums=False
76 76 abort: hidden revision '2' was rewritten as: 2443a0e66469!
77 77 (use --hidden to access hidden revisions)
78 78 [255]
79 79
80 80 $ hg diff -c 2
81 81 diff -r 29becc82797a -r 28ad74487de9 c
82 82 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
83 83 +++ b/c Thu Jan 01 00:00:00 1970 +0000
84 84 @@ -0,0 +1,1 @@
85 85 +foo
86 86
87 87 Testing with hash
88 88
89 89 `hg export`
90 90
91 91 $ hg exp 28ad74
92 92 # HG changeset patch
93 93 # User test
94 94 # Date 0 0
95 95 # Thu Jan 01 00:00:00 1970 +0000
96 96 # Node ID 28ad74487de9599d00d81085be739c61fc340652
97 97 # Parent 29becc82797a4bc11ec8880b58eaecd2ab3e7760
98 98 Added c
99 99
100 100 diff -r 29becc82797a -r 28ad74487de9 c
101 101 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
102 102 +++ b/c Thu Jan 01 00:00:00 1970 +0000
103 103 @@ -0,0 +1,1 @@
104 104 +foo
105 105
106 106 `hg log`
107 107
108 108 $ hg log -r 28ad74
109 109 changeset: 2:28ad74487de9
110 110 user: test
111 111 date: Thu Jan 01 00:00:00 1970 +0000
112 112 obsolete: rewritten using amend as 3:2443a0e66469
113 113 summary: Added c
114 114
115 115 `hg cat`
116 116
117 117 $ hg cat -r 28ad74 c
118 118 foo
119 119
120 120 `hg diff`
121 121
122 122 $ hg diff -c 28ad74
123 123 diff -r 29becc82797a -r 28ad74487de9 c
124 124 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
125 125 +++ b/c Thu Jan 01 00:00:00 1970 +0000
126 126 @@ -0,0 +1,1 @@
127 127 +foo
128 128
129 129 `hg files`
130 130
131 131 $ hg files -r 28ad74
132 132 a
133 133 b
134 134 c
135 135
136 136 `hg identify`
137 137
138 138 $ hg identify -r 28ad74
139 139 28ad74487de9
140 140
141 141 `hg annotate`
142 142
143 143 $ hg annotate -r 28ad74 a
144 144 0: foo
145 145
146 146 `hg status`
147 147
148 148 $ hg status --change 28ad74
149 149 A c
150 150
151 151 `hg archive`
152 152
153 153 This should not throw error
154 154 $ hg archive -r 28ad74 foo
155 155
156 156 `hg update`
157 157
158 158 $ hg up 28ad74
159 159 updating to a hidden changeset 28ad74487de9
160 160 (hidden revision '28ad74487de9' was rewritten as: 2443a0e66469)
161 161 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
162 162
163 163 $ hg up 3
164 164 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
165 165
166 166 $ hg up
167 167 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
168 168
169 169 `hg revert`
170 170
171 171 $ hg revert -r 28ad74 --all
172 172 reverting c
173 173
174 174 $ hg diff
175 175 diff -r 2443a0e66469 c
176 176 --- a/c Thu Jan 01 00:00:00 1970 +0000
177 177 +++ b/c Thu Jan 01 00:00:00 1970 +0000
178 178 @@ -1,2 +1,1 @@
179 179 foo
180 180 -bar
181 181
182 182 Test special hash/rev
183 183
184 184 $ hg log -qr 'null:wdir() & 000000000000'
185 185 -1:000000000000
186 186 $ hg log -qr 'null:wdir() & ffffffffffff'
187 187 2147483647:ffffffffffff
188 188 $ hg log -qr 'null:wdir() & rev(-1)'
189 189 -1:000000000000
190 190 $ hg log -qr 'null:wdir() & rev(2147483647)'
191 191 2147483647:ffffffffffff
192 192 $ hg log -qr 'null:wdir() & 2147483647'
193 193 2147483647:ffffffffffff
194 194
195 Commands with undefined cmdtype should not work right now
195 Commands with undefined intent should not work right now
196 196
197 197 $ hg phase -r 28ad74
198 198 abort: hidden revision '28ad74' was rewritten as: 2443a0e66469!
199 199 (use --hidden to access hidden revisions)
200 200 [255]
201 201
202 202 $ hg phase -r 2
203 203 abort: hidden revision '2' was rewritten as: 2443a0e66469!
204 204 (use --hidden to access hidden revisions)
205 205 [255]
206 206
207 207 Setting a bookmark will make that changeset unhidden, so this should come in end
208 208
209 209 $ hg bookmarks -r 28ad74 book
210 210 bookmarking hidden changeset 28ad74487de9
211 211 (hidden revision '28ad74487de9' was rewritten as: 2443a0e66469)
212 212
213 213 $ hg bookmarks
214 214 book 2:28ad74487de9
General Comments 0
You need to be logged in to leave comments. Login now