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