##// END OF EJS Templates
graphlog: document what "_" and "*" mean...
av6 -
r36371:eedc69d2 stable
parent child Browse files
Show More
@@ -1,5637 +1,5638 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 1222 heads = revs and map(repo.lookup, revs) or 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 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
3305 and '+' represents a fork where the changeset from the lines below is a
3306 parent of the 'o' merge on the same line.
3304 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3305 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3306 changeset from the lines below is a parent of the 'o' merge on the same
3307 line.
3307 3308 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3308 3309 of a '|' indicates one or more revisions in a path are omitted.
3309 3310
3310 3311 .. container:: verbose
3311 3312
3312 3313 Use -L/--line-range FILE,M:N options to follow the history of lines
3313 3314 from M to N in FILE. With -p/--patch only diff hunks affecting
3314 3315 specified line range will be shown. This option requires --follow;
3315 3316 it can be specified multiple times. Currently, this option is not
3316 3317 compatible with --graph. This option is experimental.
3317 3318
3318 3319 .. note::
3319 3320
3320 3321 :hg:`log --patch` may generate unexpected diff output for merge
3321 3322 changesets, as it will only compare the merge changeset against
3322 3323 its first parent. Also, only files different from BOTH parents
3323 3324 will appear in files:.
3324 3325
3325 3326 .. note::
3326 3327
3327 3328 For performance reasons, :hg:`log FILE` may omit duplicate changes
3328 3329 made on branches and will not show removals or mode changes. To
3329 3330 see all such changes, use the --removed switch.
3330 3331
3331 3332 .. container:: verbose
3332 3333
3333 3334 .. note::
3334 3335
3335 3336 The history resulting from -L/--line-range options depends on diff
3336 3337 options; for instance if white-spaces are ignored, respective changes
3337 3338 with only white-spaces in specified line range will not be listed.
3338 3339
3339 3340 .. container:: verbose
3340 3341
3341 3342 Some examples:
3342 3343
3343 3344 - changesets with full descriptions and file lists::
3344 3345
3345 3346 hg log -v
3346 3347
3347 3348 - changesets ancestral to the working directory::
3348 3349
3349 3350 hg log -f
3350 3351
3351 3352 - last 10 commits on the current branch::
3352 3353
3353 3354 hg log -l 10 -b .
3354 3355
3355 3356 - changesets showing all modifications of a file, including removals::
3356 3357
3357 3358 hg log --removed file.c
3358 3359
3359 3360 - all changesets that touch a directory, with diffs, excluding merges::
3360 3361
3361 3362 hg log -Mp lib/
3362 3363
3363 3364 - all revision numbers that match a keyword::
3364 3365
3365 3366 hg log -k bug --template "{rev}\\n"
3366 3367
3367 3368 - the full hash identifier of the working directory parent::
3368 3369
3369 3370 hg log -r . --template "{node}\\n"
3370 3371
3371 3372 - list available log templates::
3372 3373
3373 3374 hg log -T list
3374 3375
3375 3376 - check if a given changeset is included in a tagged release::
3376 3377
3377 3378 hg log -r "a21ccf and ancestor(1.9)"
3378 3379
3379 3380 - find all changesets by some user in a date range::
3380 3381
3381 3382 hg log -k alice -d "may 2008 to jul 2008"
3382 3383
3383 3384 - summary of all changesets after the last tag::
3384 3385
3385 3386 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3386 3387
3387 3388 - changesets touching lines 13 to 23 for file.c::
3388 3389
3389 3390 hg log -L file.c,13:23
3390 3391
3391 3392 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3392 3393 main.c with patch::
3393 3394
3394 3395 hg log -L file.c,13:23 -L main.c,2:6 -p
3395 3396
3396 3397 See :hg:`help dates` for a list of formats valid for -d/--date.
3397 3398
3398 3399 See :hg:`help revisions` for more about specifying and ordering
3399 3400 revisions.
3400 3401
3401 3402 See :hg:`help templates` for more about pre-packaged styles and
3402 3403 specifying custom templates. The default template used by the log
3403 3404 command can be customized via the ``ui.logtemplate`` configuration
3404 3405 setting.
3405 3406
3406 3407 Returns 0 on success.
3407 3408
3408 3409 """
3409 3410 opts = pycompat.byteskwargs(opts)
3410 3411 linerange = opts.get('line_range')
3411 3412
3412 3413 if linerange and not opts.get('follow'):
3413 3414 raise error.Abort(_('--line-range requires --follow'))
3414 3415
3415 3416 if linerange and pats:
3416 3417 raise error.Abort(
3417 3418 _('FILE arguments are not compatible with --line-range option')
3418 3419 )
3419 3420
3420 3421 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3421 3422 revs, filematcher = cmdutil.getlogrevs(repo, pats, opts)
3422 3423 hunksfilter = None
3423 3424
3424 3425 if opts.get('graph'):
3425 3426 if linerange:
3426 3427 raise error.Abort(_('graph not supported with line range patterns'))
3427 3428 return cmdutil.graphlog(ui, repo, revs, filematcher, opts)
3428 3429
3429 3430 if linerange:
3430 3431 revs, lrfilematcher, hunksfilter = cmdutil.getloglinerangerevs(
3431 3432 repo, revs, opts)
3432 3433
3433 3434 if filematcher is not None and lrfilematcher is not None:
3434 3435 basefilematcher = filematcher
3435 3436
3436 3437 def filematcher(rev):
3437 3438 files = (basefilematcher(rev).files()
3438 3439 + lrfilematcher(rev).files())
3439 3440 return scmutil.matchfiles(repo, files)
3440 3441
3441 3442 elif filematcher is None:
3442 3443 filematcher = lrfilematcher
3443 3444
3444 3445 getrenamed = None
3445 3446 if opts.get('copies'):
3446 3447 endrev = None
3447 3448 if opts.get('rev'):
3448 3449 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3449 3450 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3450 3451
3451 3452 ui.pager('log')
3452 3453 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3453 3454 for rev in revs:
3454 3455 ctx = repo[rev]
3455 3456 copies = None
3456 3457 if getrenamed is not None and rev:
3457 3458 copies = []
3458 3459 for fn in ctx.files():
3459 3460 rename = getrenamed(fn, rev)
3460 3461 if rename:
3461 3462 copies.append((fn, rename[0]))
3462 3463 if filematcher:
3463 3464 revmatchfn = filematcher(ctx.rev())
3464 3465 else:
3465 3466 revmatchfn = None
3466 3467 if hunksfilter:
3467 3468 revhunksfilter = hunksfilter(rev)
3468 3469 else:
3469 3470 revhunksfilter = None
3470 3471 displayer.show(ctx, copies=copies, matchfn=revmatchfn,
3471 3472 hunksfilterfn=revhunksfilter)
3472 3473 displayer.flush(ctx)
3473 3474
3474 3475 displayer.close()
3475 3476
3476 3477 @command('manifest',
3477 3478 [('r', 'rev', '', _('revision to display'), _('REV')),
3478 3479 ('', 'all', False, _("list files from all revisions"))]
3479 3480 + formatteropts,
3480 3481 _('[-r REV]'), cmdtype=readonly)
3481 3482 def manifest(ui, repo, node=None, rev=None, **opts):
3482 3483 """output the current or given revision of the project manifest
3483 3484
3484 3485 Print a list of version controlled files for the given revision.
3485 3486 If no revision is given, the first parent of the working directory
3486 3487 is used, or the null revision if no revision is checked out.
3487 3488
3488 3489 With -v, print file permissions, symlink and executable bits.
3489 3490 With --debug, print file revision hashes.
3490 3491
3491 3492 If option --all is specified, the list of all files from all revisions
3492 3493 is printed. This includes deleted and renamed files.
3493 3494
3494 3495 Returns 0 on success.
3495 3496 """
3496 3497 opts = pycompat.byteskwargs(opts)
3497 3498 fm = ui.formatter('manifest', opts)
3498 3499
3499 3500 if opts.get('all'):
3500 3501 if rev or node:
3501 3502 raise error.Abort(_("can't specify a revision with --all"))
3502 3503
3503 3504 res = []
3504 3505 prefix = "data/"
3505 3506 suffix = ".i"
3506 3507 plen = len(prefix)
3507 3508 slen = len(suffix)
3508 3509 with repo.lock():
3509 3510 for fn, b, size in repo.store.datafiles():
3510 3511 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3511 3512 res.append(fn[plen:-slen])
3512 3513 ui.pager('manifest')
3513 3514 for f in res:
3514 3515 fm.startitem()
3515 3516 fm.write("path", '%s\n', f)
3516 3517 fm.end()
3517 3518 return
3518 3519
3519 3520 if rev and node:
3520 3521 raise error.Abort(_("please specify just one revision"))
3521 3522
3522 3523 if not node:
3523 3524 node = rev
3524 3525
3525 3526 char = {'l': '@', 'x': '*', '': ''}
3526 3527 mode = {'l': '644', 'x': '755', '': '644'}
3527 3528 if node:
3528 3529 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3529 3530 ctx = scmutil.revsingle(repo, node)
3530 3531 mf = ctx.manifest()
3531 3532 ui.pager('manifest')
3532 3533 for f in ctx:
3533 3534 fm.startitem()
3534 3535 fl = ctx[f].flags()
3535 3536 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3536 3537 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3537 3538 fm.write('path', '%s\n', f)
3538 3539 fm.end()
3539 3540
3540 3541 @command('^merge',
3541 3542 [('f', 'force', None,
3542 3543 _('force a merge including outstanding changes (DEPRECATED)')),
3543 3544 ('r', 'rev', '', _('revision to merge'), _('REV')),
3544 3545 ('P', 'preview', None,
3545 3546 _('review revisions to merge (no merge is performed)')),
3546 3547 ('', 'abort', None, _('abort the ongoing merge')),
3547 3548 ] + mergetoolopts,
3548 3549 _('[-P] [[-r] REV]'))
3549 3550 def merge(ui, repo, node=None, **opts):
3550 3551 """merge another revision into working directory
3551 3552
3552 3553 The current working directory is updated with all changes made in
3553 3554 the requested revision since the last common predecessor revision.
3554 3555
3555 3556 Files that changed between either parent are marked as changed for
3556 3557 the next commit and a commit must be performed before any further
3557 3558 updates to the repository are allowed. The next commit will have
3558 3559 two parents.
3559 3560
3560 3561 ``--tool`` can be used to specify the merge tool used for file
3561 3562 merges. It overrides the HGMERGE environment variable and your
3562 3563 configuration files. See :hg:`help merge-tools` for options.
3563 3564
3564 3565 If no revision is specified, the working directory's parent is a
3565 3566 head revision, and the current branch contains exactly one other
3566 3567 head, the other head is merged with by default. Otherwise, an
3567 3568 explicit revision with which to merge with must be provided.
3568 3569
3569 3570 See :hg:`help resolve` for information on handling file conflicts.
3570 3571
3571 3572 To undo an uncommitted merge, use :hg:`merge --abort` which
3572 3573 will check out a clean copy of the original merge parent, losing
3573 3574 all changes.
3574 3575
3575 3576 Returns 0 on success, 1 if there are unresolved files.
3576 3577 """
3577 3578
3578 3579 opts = pycompat.byteskwargs(opts)
3579 3580 abort = opts.get('abort')
3580 3581 if abort and repo.dirstate.p2() == nullid:
3581 3582 cmdutil.wrongtooltocontinue(repo, _('merge'))
3582 3583 if abort:
3583 3584 if node:
3584 3585 raise error.Abort(_("cannot specify a node with --abort"))
3585 3586 if opts.get('rev'):
3586 3587 raise error.Abort(_("cannot specify both --rev and --abort"))
3587 3588 if opts.get('preview'):
3588 3589 raise error.Abort(_("cannot specify --preview with --abort"))
3589 3590 if opts.get('rev') and node:
3590 3591 raise error.Abort(_("please specify just one revision"))
3591 3592 if not node:
3592 3593 node = opts.get('rev')
3593 3594
3594 3595 if node:
3595 3596 node = scmutil.revsingle(repo, node).node()
3596 3597
3597 3598 if not node and not abort:
3598 3599 node = repo[destutil.destmerge(repo)].node()
3599 3600
3600 3601 if opts.get('preview'):
3601 3602 # find nodes that are ancestors of p2 but not of p1
3602 3603 p1 = repo.lookup('.')
3603 3604 p2 = repo.lookup(node)
3604 3605 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3605 3606
3606 3607 displayer = cmdutil.show_changeset(ui, repo, opts)
3607 3608 for node in nodes:
3608 3609 displayer.show(repo[node])
3609 3610 displayer.close()
3610 3611 return 0
3611 3612
3612 3613 try:
3613 3614 # ui.forcemerge is an internal variable, do not document
3614 3615 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3615 3616 force = opts.get('force')
3616 3617 labels = ['working copy', 'merge rev']
3617 3618 return hg.merge(repo, node, force=force, mergeforce=force,
3618 3619 labels=labels, abort=abort)
3619 3620 finally:
3620 3621 ui.setconfig('ui', 'forcemerge', '', 'merge')
3621 3622
3622 3623 @command('outgoing|out',
3623 3624 [('f', 'force', None, _('run even when the destination is unrelated')),
3624 3625 ('r', 'rev', [],
3625 3626 _('a changeset intended to be included in the destination'), _('REV')),
3626 3627 ('n', 'newest-first', None, _('show newest record first')),
3627 3628 ('B', 'bookmarks', False, _('compare bookmarks')),
3628 3629 ('b', 'branch', [], _('a specific branch you would like to push'),
3629 3630 _('BRANCH')),
3630 3631 ] + logopts + remoteopts + subrepoopts,
3631 3632 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3632 3633 def outgoing(ui, repo, dest=None, **opts):
3633 3634 """show changesets not found in the destination
3634 3635
3635 3636 Show changesets not found in the specified destination repository
3636 3637 or the default push location. These are the changesets that would
3637 3638 be pushed if a push was requested.
3638 3639
3639 3640 See pull for details of valid destination formats.
3640 3641
3641 3642 .. container:: verbose
3642 3643
3643 3644 With -B/--bookmarks, the result of bookmark comparison between
3644 3645 local and remote repositories is displayed. With -v/--verbose,
3645 3646 status is also displayed for each bookmark like below::
3646 3647
3647 3648 BM1 01234567890a added
3648 3649 BM2 deleted
3649 3650 BM3 234567890abc advanced
3650 3651 BM4 34567890abcd diverged
3651 3652 BM5 4567890abcde changed
3652 3653
3653 3654 The action taken when pushing depends on the
3654 3655 status of each bookmark:
3655 3656
3656 3657 :``added``: push with ``-B`` will create it
3657 3658 :``deleted``: push with ``-B`` will delete it
3658 3659 :``advanced``: push will update it
3659 3660 :``diverged``: push with ``-B`` will update it
3660 3661 :``changed``: push with ``-B`` will update it
3661 3662
3662 3663 From the point of view of pushing behavior, bookmarks
3663 3664 existing only in the remote repository are treated as
3664 3665 ``deleted``, even if it is in fact added remotely.
3665 3666
3666 3667 Returns 0 if there are outgoing changes, 1 otherwise.
3667 3668 """
3668 3669 opts = pycompat.byteskwargs(opts)
3669 3670 if opts.get('graph'):
3670 3671 cmdutil.checkunsupportedgraphflags([], opts)
3671 3672 o, other = hg._outgoing(ui, repo, dest, opts)
3672 3673 if not o:
3673 3674 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3674 3675 return
3675 3676
3676 3677 revdag = cmdutil.graphrevs(repo, o, opts)
3677 3678 ui.pager('outgoing')
3678 3679 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3679 3680 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
3680 3681 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3681 3682 return 0
3682 3683
3683 3684 if opts.get('bookmarks'):
3684 3685 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3685 3686 dest, branches = hg.parseurl(dest, opts.get('branch'))
3686 3687 other = hg.peer(repo, opts, dest)
3687 3688 if 'bookmarks' not in other.listkeys('namespaces'):
3688 3689 ui.warn(_("remote doesn't support bookmarks\n"))
3689 3690 return 0
3690 3691 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3691 3692 ui.pager('outgoing')
3692 3693 return bookmarks.outgoing(ui, repo, other)
3693 3694
3694 3695 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3695 3696 try:
3696 3697 return hg.outgoing(ui, repo, dest, opts)
3697 3698 finally:
3698 3699 del repo._subtoppath
3699 3700
3700 3701 @command('parents',
3701 3702 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3702 3703 ] + templateopts,
3703 3704 _('[-r REV] [FILE]'),
3704 3705 inferrepo=True)
3705 3706 def parents(ui, repo, file_=None, **opts):
3706 3707 """show the parents of the working directory or revision (DEPRECATED)
3707 3708
3708 3709 Print the working directory's parent revisions. If a revision is
3709 3710 given via -r/--rev, the parent of that revision will be printed.
3710 3711 If a file argument is given, the revision in which the file was
3711 3712 last changed (before the working directory revision or the
3712 3713 argument to --rev if given) is printed.
3713 3714
3714 3715 This command is equivalent to::
3715 3716
3716 3717 hg log -r "p1()+p2()" or
3717 3718 hg log -r "p1(REV)+p2(REV)" or
3718 3719 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3719 3720 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3720 3721
3721 3722 See :hg:`summary` and :hg:`help revsets` for related information.
3722 3723
3723 3724 Returns 0 on success.
3724 3725 """
3725 3726
3726 3727 opts = pycompat.byteskwargs(opts)
3727 3728 rev = opts.get('rev')
3728 3729 if rev:
3729 3730 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3730 3731 ctx = scmutil.revsingle(repo, rev, None)
3731 3732
3732 3733 if file_:
3733 3734 m = scmutil.match(ctx, (file_,), opts)
3734 3735 if m.anypats() or len(m.files()) != 1:
3735 3736 raise error.Abort(_('can only specify an explicit filename'))
3736 3737 file_ = m.files()[0]
3737 3738 filenodes = []
3738 3739 for cp in ctx.parents():
3739 3740 if not cp:
3740 3741 continue
3741 3742 try:
3742 3743 filenodes.append(cp.filenode(file_))
3743 3744 except error.LookupError:
3744 3745 pass
3745 3746 if not filenodes:
3746 3747 raise error.Abort(_("'%s' not found in manifest!") % file_)
3747 3748 p = []
3748 3749 for fn in filenodes:
3749 3750 fctx = repo.filectx(file_, fileid=fn)
3750 3751 p.append(fctx.node())
3751 3752 else:
3752 3753 p = [cp.node() for cp in ctx.parents()]
3753 3754
3754 3755 displayer = cmdutil.show_changeset(ui, repo, opts)
3755 3756 for n in p:
3756 3757 if n != nullid:
3757 3758 displayer.show(repo[n])
3758 3759 displayer.close()
3759 3760
3760 3761 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True,
3761 3762 cmdtype=readonly)
3762 3763 def paths(ui, repo, search=None, **opts):
3763 3764 """show aliases for remote repositories
3764 3765
3765 3766 Show definition of symbolic path name NAME. If no name is given,
3766 3767 show definition of all available names.
3767 3768
3768 3769 Option -q/--quiet suppresses all output when searching for NAME
3769 3770 and shows only the path names when listing all definitions.
3770 3771
3771 3772 Path names are defined in the [paths] section of your
3772 3773 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3773 3774 repository, ``.hg/hgrc`` is used, too.
3774 3775
3775 3776 The path names ``default`` and ``default-push`` have a special
3776 3777 meaning. When performing a push or pull operation, they are used
3777 3778 as fallbacks if no location is specified on the command-line.
3778 3779 When ``default-push`` is set, it will be used for push and
3779 3780 ``default`` will be used for pull; otherwise ``default`` is used
3780 3781 as the fallback for both. When cloning a repository, the clone
3781 3782 source is written as ``default`` in ``.hg/hgrc``.
3782 3783
3783 3784 .. note::
3784 3785
3785 3786 ``default`` and ``default-push`` apply to all inbound (e.g.
3786 3787 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3787 3788 and :hg:`bundle`) operations.
3788 3789
3789 3790 See :hg:`help urls` for more information.
3790 3791
3791 3792 Returns 0 on success.
3792 3793 """
3793 3794
3794 3795 opts = pycompat.byteskwargs(opts)
3795 3796 ui.pager('paths')
3796 3797 if search:
3797 3798 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3798 3799 if name == search]
3799 3800 else:
3800 3801 pathitems = sorted(ui.paths.iteritems())
3801 3802
3802 3803 fm = ui.formatter('paths', opts)
3803 3804 if fm.isplain():
3804 3805 hidepassword = util.hidepassword
3805 3806 else:
3806 3807 hidepassword = str
3807 3808 if ui.quiet:
3808 3809 namefmt = '%s\n'
3809 3810 else:
3810 3811 namefmt = '%s = '
3811 3812 showsubopts = not search and not ui.quiet
3812 3813
3813 3814 for name, path in pathitems:
3814 3815 fm.startitem()
3815 3816 fm.condwrite(not search, 'name', namefmt, name)
3816 3817 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3817 3818 for subopt, value in sorted(path.suboptions.items()):
3818 3819 assert subopt not in ('name', 'url')
3819 3820 if showsubopts:
3820 3821 fm.plain('%s:%s = ' % (name, subopt))
3821 3822 fm.condwrite(showsubopts, subopt, '%s\n', value)
3822 3823
3823 3824 fm.end()
3824 3825
3825 3826 if search and not pathitems:
3826 3827 if not ui.quiet:
3827 3828 ui.warn(_("not found!\n"))
3828 3829 return 1
3829 3830 else:
3830 3831 return 0
3831 3832
3832 3833 @command('phase',
3833 3834 [('p', 'public', False, _('set changeset phase to public')),
3834 3835 ('d', 'draft', False, _('set changeset phase to draft')),
3835 3836 ('s', 'secret', False, _('set changeset phase to secret')),
3836 3837 ('f', 'force', False, _('allow to move boundary backward')),
3837 3838 ('r', 'rev', [], _('target revision'), _('REV')),
3838 3839 ],
3839 3840 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3840 3841 def phase(ui, repo, *revs, **opts):
3841 3842 """set or show the current phase name
3842 3843
3843 3844 With no argument, show the phase name of the current revision(s).
3844 3845
3845 3846 With one of -p/--public, -d/--draft or -s/--secret, change the
3846 3847 phase value of the specified revisions.
3847 3848
3848 3849 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
3849 3850 lower phase to a higher phase. Phases are ordered as follows::
3850 3851
3851 3852 public < draft < secret
3852 3853
3853 3854 Returns 0 on success, 1 if some phases could not be changed.
3854 3855
3855 3856 (For more information about the phases concept, see :hg:`help phases`.)
3856 3857 """
3857 3858 opts = pycompat.byteskwargs(opts)
3858 3859 # search for a unique phase argument
3859 3860 targetphase = None
3860 3861 for idx, name in enumerate(phases.phasenames):
3861 3862 if opts[name]:
3862 3863 if targetphase is not None:
3863 3864 raise error.Abort(_('only one phase can be specified'))
3864 3865 targetphase = idx
3865 3866
3866 3867 # look for specified revision
3867 3868 revs = list(revs)
3868 3869 revs.extend(opts['rev'])
3869 3870 if not revs:
3870 3871 # display both parents as the second parent phase can influence
3871 3872 # the phase of a merge commit
3872 3873 revs = [c.rev() for c in repo[None].parents()]
3873 3874
3874 3875 revs = scmutil.revrange(repo, revs)
3875 3876
3876 3877 ret = 0
3877 3878 if targetphase is None:
3878 3879 # display
3879 3880 for r in revs:
3880 3881 ctx = repo[r]
3881 3882 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3882 3883 else:
3883 3884 with repo.lock(), repo.transaction("phase") as tr:
3884 3885 # set phase
3885 3886 if not revs:
3886 3887 raise error.Abort(_('empty revision set'))
3887 3888 nodes = [repo[r].node() for r in revs]
3888 3889 # moving revision from public to draft may hide them
3889 3890 # We have to check result on an unfiltered repository
3890 3891 unfi = repo.unfiltered()
3891 3892 getphase = unfi._phasecache.phase
3892 3893 olddata = [getphase(unfi, r) for r in unfi]
3893 3894 phases.advanceboundary(repo, tr, targetphase, nodes)
3894 3895 if opts['force']:
3895 3896 phases.retractboundary(repo, tr, targetphase, nodes)
3896 3897 getphase = unfi._phasecache.phase
3897 3898 newdata = [getphase(unfi, r) for r in unfi]
3898 3899 changes = sum(newdata[r] != olddata[r] for r in unfi)
3899 3900 cl = unfi.changelog
3900 3901 rejected = [n for n in nodes
3901 3902 if newdata[cl.rev(n)] < targetphase]
3902 3903 if rejected:
3903 3904 ui.warn(_('cannot move %i changesets to a higher '
3904 3905 'phase, use --force\n') % len(rejected))
3905 3906 ret = 1
3906 3907 if changes:
3907 3908 msg = _('phase changed for %i changesets\n') % changes
3908 3909 if ret:
3909 3910 ui.status(msg)
3910 3911 else:
3911 3912 ui.note(msg)
3912 3913 else:
3913 3914 ui.warn(_('no phases changed\n'))
3914 3915 return ret
3915 3916
3916 3917 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3917 3918 """Run after a changegroup has been added via pull/unbundle
3918 3919
3919 3920 This takes arguments below:
3920 3921
3921 3922 :modheads: change of heads by pull/unbundle
3922 3923 :optupdate: updating working directory is needed or not
3923 3924 :checkout: update destination revision (or None to default destination)
3924 3925 :brev: a name, which might be a bookmark to be activated after updating
3925 3926 """
3926 3927 if modheads == 0:
3927 3928 return
3928 3929 if optupdate:
3929 3930 try:
3930 3931 return hg.updatetotally(ui, repo, checkout, brev)
3931 3932 except error.UpdateAbort as inst:
3932 3933 msg = _("not updating: %s") % str(inst)
3933 3934 hint = inst.hint
3934 3935 raise error.UpdateAbort(msg, hint=hint)
3935 3936 if modheads > 1:
3936 3937 currentbranchheads = len(repo.branchheads())
3937 3938 if currentbranchheads == modheads:
3938 3939 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3939 3940 elif currentbranchheads > 1:
3940 3941 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3941 3942 "merge)\n"))
3942 3943 else:
3943 3944 ui.status(_("(run 'hg heads' to see heads)\n"))
3944 3945 elif not ui.configbool('commands', 'update.requiredest'):
3945 3946 ui.status(_("(run 'hg update' to get a working copy)\n"))
3946 3947
3947 3948 @command('^pull',
3948 3949 [('u', 'update', None,
3949 3950 _('update to new branch head if new descendants were pulled')),
3950 3951 ('f', 'force', None, _('run even when remote repository is unrelated')),
3951 3952 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3952 3953 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3953 3954 ('b', 'branch', [], _('a specific branch you would like to pull'),
3954 3955 _('BRANCH')),
3955 3956 ] + remoteopts,
3956 3957 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3957 3958 def pull(ui, repo, source="default", **opts):
3958 3959 """pull changes from the specified source
3959 3960
3960 3961 Pull changes from a remote repository to a local one.
3961 3962
3962 3963 This finds all changes from the repository at the specified path
3963 3964 or URL and adds them to a local repository (the current one unless
3964 3965 -R is specified). By default, this does not update the copy of the
3965 3966 project in the working directory.
3966 3967
3967 3968 Use :hg:`incoming` if you want to see what would have been added
3968 3969 by a pull at the time you issued this command. If you then decide
3969 3970 to add those changes to the repository, you should use :hg:`pull
3970 3971 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3971 3972
3972 3973 If SOURCE is omitted, the 'default' path will be used.
3973 3974 See :hg:`help urls` for more information.
3974 3975
3975 3976 Specifying bookmark as ``.`` is equivalent to specifying the active
3976 3977 bookmark's name.
3977 3978
3978 3979 Returns 0 on success, 1 if an update had unresolved files.
3979 3980 """
3980 3981
3981 3982 opts = pycompat.byteskwargs(opts)
3982 3983 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3983 3984 msg = _('update destination required by configuration')
3984 3985 hint = _('use hg pull followed by hg update DEST')
3985 3986 raise error.Abort(msg, hint=hint)
3986 3987
3987 3988 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3988 3989 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3989 3990 other = hg.peer(repo, opts, source)
3990 3991 try:
3991 3992 revs, checkout = hg.addbranchrevs(repo, other, branches,
3992 3993 opts.get('rev'))
3993 3994
3994 3995
3995 3996 pullopargs = {}
3996 3997 if opts.get('bookmark'):
3997 3998 if not revs:
3998 3999 revs = []
3999 4000 # The list of bookmark used here is not the one used to actually
4000 4001 # update the bookmark name. This can result in the revision pulled
4001 4002 # not ending up with the name of the bookmark because of a race
4002 4003 # condition on the server. (See issue 4689 for details)
4003 4004 remotebookmarks = other.listkeys('bookmarks')
4004 4005 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4005 4006 pullopargs['remotebookmarks'] = remotebookmarks
4006 4007 for b in opts['bookmark']:
4007 4008 b = repo._bookmarks.expandname(b)
4008 4009 if b not in remotebookmarks:
4009 4010 raise error.Abort(_('remote bookmark %s not found!') % b)
4010 4011 revs.append(hex(remotebookmarks[b]))
4011 4012
4012 4013 if revs:
4013 4014 try:
4014 4015 # When 'rev' is a bookmark name, we cannot guarantee that it
4015 4016 # will be updated with that name because of a race condition
4016 4017 # server side. (See issue 4689 for details)
4017 4018 oldrevs = revs
4018 4019 revs = [] # actually, nodes
4019 4020 for r in oldrevs:
4020 4021 node = other.lookup(r)
4021 4022 revs.append(node)
4022 4023 if r == checkout:
4023 4024 checkout = node
4024 4025 except error.CapabilityError:
4025 4026 err = _("other repository doesn't support revision lookup, "
4026 4027 "so a rev cannot be specified.")
4027 4028 raise error.Abort(err)
4028 4029
4029 4030 wlock = util.nullcontextmanager()
4030 4031 if opts.get('update'):
4031 4032 wlock = repo.wlock()
4032 4033 with wlock:
4033 4034 pullopargs.update(opts.get('opargs', {}))
4034 4035 modheads = exchange.pull(repo, other, heads=revs,
4035 4036 force=opts.get('force'),
4036 4037 bookmarks=opts.get('bookmark', ()),
4037 4038 opargs=pullopargs).cgresult
4038 4039
4039 4040 # brev is a name, which might be a bookmark to be activated at
4040 4041 # the end of the update. In other words, it is an explicit
4041 4042 # destination of the update
4042 4043 brev = None
4043 4044
4044 4045 if checkout:
4045 4046 checkout = str(repo.changelog.rev(checkout))
4046 4047
4047 4048 # order below depends on implementation of
4048 4049 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4049 4050 # because 'checkout' is determined without it.
4050 4051 if opts.get('rev'):
4051 4052 brev = opts['rev'][0]
4052 4053 elif opts.get('branch'):
4053 4054 brev = opts['branch'][0]
4054 4055 else:
4055 4056 brev = branches[0]
4056 4057 repo._subtoppath = source
4057 4058 try:
4058 4059 ret = postincoming(ui, repo, modheads, opts.get('update'),
4059 4060 checkout, brev)
4060 4061
4061 4062 finally:
4062 4063 del repo._subtoppath
4063 4064
4064 4065 finally:
4065 4066 other.close()
4066 4067 return ret
4067 4068
4068 4069 @command('^push',
4069 4070 [('f', 'force', None, _('force push')),
4070 4071 ('r', 'rev', [],
4071 4072 _('a changeset intended to be included in the destination'),
4072 4073 _('REV')),
4073 4074 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4074 4075 ('b', 'branch', [],
4075 4076 _('a specific branch you would like to push'), _('BRANCH')),
4076 4077 ('', 'new-branch', False, _('allow pushing a new branch')),
4077 4078 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4078 4079 ] + remoteopts,
4079 4080 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4080 4081 def push(ui, repo, dest=None, **opts):
4081 4082 """push changes to the specified destination
4082 4083
4083 4084 Push changesets from the local repository to the specified
4084 4085 destination.
4085 4086
4086 4087 This operation is symmetrical to pull: it is identical to a pull
4087 4088 in the destination repository from the current one.
4088 4089
4089 4090 By default, push will not allow creation of new heads at the
4090 4091 destination, since multiple heads would make it unclear which head
4091 4092 to use. In this situation, it is recommended to pull and merge
4092 4093 before pushing.
4093 4094
4094 4095 Use --new-branch if you want to allow push to create a new named
4095 4096 branch that is not present at the destination. This allows you to
4096 4097 only create a new branch without forcing other changes.
4097 4098
4098 4099 .. note::
4099 4100
4100 4101 Extra care should be taken with the -f/--force option,
4101 4102 which will push all new heads on all branches, an action which will
4102 4103 almost always cause confusion for collaborators.
4103 4104
4104 4105 If -r/--rev is used, the specified revision and all its ancestors
4105 4106 will be pushed to the remote repository.
4106 4107
4107 4108 If -B/--bookmark is used, the specified bookmarked revision, its
4108 4109 ancestors, and the bookmark will be pushed to the remote
4109 4110 repository. Specifying ``.`` is equivalent to specifying the active
4110 4111 bookmark's name.
4111 4112
4112 4113 Please see :hg:`help urls` for important details about ``ssh://``
4113 4114 URLs. If DESTINATION is omitted, a default path will be used.
4114 4115
4115 4116 .. container:: verbose
4116 4117
4117 4118 The --pushvars option sends strings to the server that become
4118 4119 environment variables prepended with ``HG_USERVAR_``. For example,
4119 4120 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4120 4121 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4121 4122
4122 4123 pushvars can provide for user-overridable hooks as well as set debug
4123 4124 levels. One example is having a hook that blocks commits containing
4124 4125 conflict markers, but enables the user to override the hook if the file
4125 4126 is using conflict markers for testing purposes or the file format has
4126 4127 strings that look like conflict markers.
4127 4128
4128 4129 By default, servers will ignore `--pushvars`. To enable it add the
4129 4130 following to your configuration file::
4130 4131
4131 4132 [push]
4132 4133 pushvars.server = true
4133 4134
4134 4135 Returns 0 if push was successful, 1 if nothing to push.
4135 4136 """
4136 4137
4137 4138 opts = pycompat.byteskwargs(opts)
4138 4139 if opts.get('bookmark'):
4139 4140 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4140 4141 for b in opts['bookmark']:
4141 4142 # translate -B options to -r so changesets get pushed
4142 4143 b = repo._bookmarks.expandname(b)
4143 4144 if b in repo._bookmarks:
4144 4145 opts.setdefault('rev', []).append(b)
4145 4146 else:
4146 4147 # if we try to push a deleted bookmark, translate it to null
4147 4148 # this lets simultaneous -r, -b options continue working
4148 4149 opts.setdefault('rev', []).append("null")
4149 4150
4150 4151 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4151 4152 if not path:
4152 4153 raise error.Abort(_('default repository not configured!'),
4153 4154 hint=_("see 'hg help config.paths'"))
4154 4155 dest = path.pushloc or path.loc
4155 4156 branches = (path.branch, opts.get('branch') or [])
4156 4157 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4157 4158 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4158 4159 other = hg.peer(repo, opts, dest)
4159 4160
4160 4161 if revs:
4161 4162 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4162 4163 if not revs:
4163 4164 raise error.Abort(_("specified revisions evaluate to an empty set"),
4164 4165 hint=_("use different revision arguments"))
4165 4166 elif path.pushrev:
4166 4167 # It doesn't make any sense to specify ancestor revisions. So limit
4167 4168 # to DAG heads to make discovery simpler.
4168 4169 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4169 4170 revs = scmutil.revrange(repo, [expr])
4170 4171 revs = [repo[rev].node() for rev in revs]
4171 4172 if not revs:
4172 4173 raise error.Abort(_('default push revset for path evaluates to an '
4173 4174 'empty set'))
4174 4175
4175 4176 repo._subtoppath = dest
4176 4177 try:
4177 4178 # push subrepos depth-first for coherent ordering
4178 4179 c = repo['']
4179 4180 subs = c.substate # only repos that are committed
4180 4181 for s in sorted(subs):
4181 4182 result = c.sub(s).push(opts)
4182 4183 if result == 0:
4183 4184 return not result
4184 4185 finally:
4185 4186 del repo._subtoppath
4186 4187
4187 4188 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4188 4189 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4189 4190
4190 4191 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4191 4192 newbranch=opts.get('new_branch'),
4192 4193 bookmarks=opts.get('bookmark', ()),
4193 4194 opargs=opargs)
4194 4195
4195 4196 result = not pushop.cgresult
4196 4197
4197 4198 if pushop.bkresult is not None:
4198 4199 if pushop.bkresult == 2:
4199 4200 result = 2
4200 4201 elif not result and pushop.bkresult:
4201 4202 result = 2
4202 4203
4203 4204 return result
4204 4205
4205 4206 @command('recover', [])
4206 4207 def recover(ui, repo):
4207 4208 """roll back an interrupted transaction
4208 4209
4209 4210 Recover from an interrupted commit or pull.
4210 4211
4211 4212 This command tries to fix the repository status after an
4212 4213 interrupted operation. It should only be necessary when Mercurial
4213 4214 suggests it.
4214 4215
4215 4216 Returns 0 if successful, 1 if nothing to recover or verify fails.
4216 4217 """
4217 4218 if repo.recover():
4218 4219 return hg.verify(repo)
4219 4220 return 1
4220 4221
4221 4222 @command('^remove|rm',
4222 4223 [('A', 'after', None, _('record delete for missing files')),
4223 4224 ('f', 'force', None,
4224 4225 _('forget added files, delete modified files')),
4225 4226 ] + subrepoopts + walkopts,
4226 4227 _('[OPTION]... FILE...'),
4227 4228 inferrepo=True)
4228 4229 def remove(ui, repo, *pats, **opts):
4229 4230 """remove the specified files on the next commit
4230 4231
4231 4232 Schedule the indicated files for removal from the current branch.
4232 4233
4233 4234 This command schedules the files to be removed at the next commit.
4234 4235 To undo a remove before that, see :hg:`revert`. To undo added
4235 4236 files, see :hg:`forget`.
4236 4237
4237 4238 .. container:: verbose
4238 4239
4239 4240 -A/--after can be used to remove only files that have already
4240 4241 been deleted, -f/--force can be used to force deletion, and -Af
4241 4242 can be used to remove files from the next revision without
4242 4243 deleting them from the working directory.
4243 4244
4244 4245 The following table details the behavior of remove for different
4245 4246 file states (columns) and option combinations (rows). The file
4246 4247 states are Added [A], Clean [C], Modified [M] and Missing [!]
4247 4248 (as reported by :hg:`status`). The actions are Warn, Remove
4248 4249 (from branch) and Delete (from disk):
4249 4250
4250 4251 ========= == == == ==
4251 4252 opt/state A C M !
4252 4253 ========= == == == ==
4253 4254 none W RD W R
4254 4255 -f R RD RD R
4255 4256 -A W W W R
4256 4257 -Af R R R R
4257 4258 ========= == == == ==
4258 4259
4259 4260 .. note::
4260 4261
4261 4262 :hg:`remove` never deletes files in Added [A] state from the
4262 4263 working directory, not even if ``--force`` is specified.
4263 4264
4264 4265 Returns 0 on success, 1 if any warnings encountered.
4265 4266 """
4266 4267
4267 4268 opts = pycompat.byteskwargs(opts)
4268 4269 after, force = opts.get('after'), opts.get('force')
4269 4270 if not pats and not after:
4270 4271 raise error.Abort(_('no files specified'))
4271 4272
4272 4273 m = scmutil.match(repo[None], pats, opts)
4273 4274 subrepos = opts.get('subrepos')
4274 4275 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
4275 4276
4276 4277 @command('rename|move|mv',
4277 4278 [('A', 'after', None, _('record a rename that has already occurred')),
4278 4279 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4279 4280 ] + walkopts + dryrunopts,
4280 4281 _('[OPTION]... SOURCE... DEST'))
4281 4282 def rename(ui, repo, *pats, **opts):
4282 4283 """rename files; equivalent of copy + remove
4283 4284
4284 4285 Mark dest as copies of sources; mark sources for deletion. If dest
4285 4286 is a directory, copies are put in that directory. If dest is a
4286 4287 file, there can only be one source.
4287 4288
4288 4289 By default, this command copies the contents of files as they
4289 4290 exist in the working directory. If invoked with -A/--after, the
4290 4291 operation is recorded, but no copying is performed.
4291 4292
4292 4293 This command takes effect at the next commit. To undo a rename
4293 4294 before that, see :hg:`revert`.
4294 4295
4295 4296 Returns 0 on success, 1 if errors are encountered.
4296 4297 """
4297 4298 opts = pycompat.byteskwargs(opts)
4298 4299 with repo.wlock(False):
4299 4300 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4300 4301
4301 4302 @command('resolve',
4302 4303 [('a', 'all', None, _('select all unresolved files')),
4303 4304 ('l', 'list', None, _('list state of files needing merge')),
4304 4305 ('m', 'mark', None, _('mark files as resolved')),
4305 4306 ('u', 'unmark', None, _('mark files as unresolved')),
4306 4307 ('n', 'no-status', None, _('hide status prefix'))]
4307 4308 + mergetoolopts + walkopts + formatteropts,
4308 4309 _('[OPTION]... [FILE]...'),
4309 4310 inferrepo=True)
4310 4311 def resolve(ui, repo, *pats, **opts):
4311 4312 """redo merges or set/view the merge status of files
4312 4313
4313 4314 Merges with unresolved conflicts are often the result of
4314 4315 non-interactive merging using the ``internal:merge`` configuration
4315 4316 setting, or a command-line merge tool like ``diff3``. The resolve
4316 4317 command is used to manage the files involved in a merge, after
4317 4318 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4318 4319 working directory must have two parents). See :hg:`help
4319 4320 merge-tools` for information on configuring merge tools.
4320 4321
4321 4322 The resolve command can be used in the following ways:
4322 4323
4323 4324 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4324 4325 files, discarding any previous merge attempts. Re-merging is not
4325 4326 performed for files already marked as resolved. Use ``--all/-a``
4326 4327 to select all unresolved files. ``--tool`` can be used to specify
4327 4328 the merge tool used for the given files. It overrides the HGMERGE
4328 4329 environment variable and your configuration files. Previous file
4329 4330 contents are saved with a ``.orig`` suffix.
4330 4331
4331 4332 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4332 4333 (e.g. after having manually fixed-up the files). The default is
4333 4334 to mark all unresolved files.
4334 4335
4335 4336 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4336 4337 default is to mark all resolved files.
4337 4338
4338 4339 - :hg:`resolve -l`: list files which had or still have conflicts.
4339 4340 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4340 4341 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4341 4342 the list. See :hg:`help filesets` for details.
4342 4343
4343 4344 .. note::
4344 4345
4345 4346 Mercurial will not let you commit files with unresolved merge
4346 4347 conflicts. You must use :hg:`resolve -m ...` before you can
4347 4348 commit after a conflicting merge.
4348 4349
4349 4350 Returns 0 on success, 1 if any files fail a resolve attempt.
4350 4351 """
4351 4352
4352 4353 opts = pycompat.byteskwargs(opts)
4353 4354 flaglist = 'all mark unmark list no_status'.split()
4354 4355 all, mark, unmark, show, nostatus = \
4355 4356 [opts.get(o) for o in flaglist]
4356 4357
4357 4358 if (show and (mark or unmark)) or (mark and unmark):
4358 4359 raise error.Abort(_("too many options specified"))
4359 4360 if pats and all:
4360 4361 raise error.Abort(_("can't specify --all and patterns"))
4361 4362 if not (all or pats or show or mark or unmark):
4362 4363 raise error.Abort(_('no files or directories specified'),
4363 4364 hint=('use --all to re-merge all unresolved files'))
4364 4365
4365 4366 if show:
4366 4367 ui.pager('resolve')
4367 4368 fm = ui.formatter('resolve', opts)
4368 4369 ms = mergemod.mergestate.read(repo)
4369 4370 m = scmutil.match(repo[None], pats, opts)
4370 4371
4371 4372 # Labels and keys based on merge state. Unresolved path conflicts show
4372 4373 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4373 4374 # resolved conflicts.
4374 4375 mergestateinfo = {
4375 4376 'u': ('resolve.unresolved', 'U'),
4376 4377 'r': ('resolve.resolved', 'R'),
4377 4378 'pu': ('resolve.unresolved', 'P'),
4378 4379 'pr': ('resolve.resolved', 'R'),
4379 4380 'd': ('resolve.driverresolved', 'D'),
4380 4381 }
4381 4382
4382 4383 for f in ms:
4383 4384 if not m(f):
4384 4385 continue
4385 4386
4386 4387 label, key = mergestateinfo[ms[f]]
4387 4388 fm.startitem()
4388 4389 fm.condwrite(not nostatus, 'status', '%s ', key, label=label)
4389 4390 fm.write('path', '%s\n', f, label=label)
4390 4391 fm.end()
4391 4392 return 0
4392 4393
4393 4394 with repo.wlock():
4394 4395 ms = mergemod.mergestate.read(repo)
4395 4396
4396 4397 if not (ms.active() or repo.dirstate.p2() != nullid):
4397 4398 raise error.Abort(
4398 4399 _('resolve command not applicable when not merging'))
4399 4400
4400 4401 wctx = repo[None]
4401 4402
4402 4403 if ms.mergedriver and ms.mdstate() == 'u':
4403 4404 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4404 4405 ms.commit()
4405 4406 # allow mark and unmark to go through
4406 4407 if not mark and not unmark and not proceed:
4407 4408 return 1
4408 4409
4409 4410 m = scmutil.match(wctx, pats, opts)
4410 4411 ret = 0
4411 4412 didwork = False
4412 4413 runconclude = False
4413 4414
4414 4415 tocomplete = []
4415 4416 for f in ms:
4416 4417 if not m(f):
4417 4418 continue
4418 4419
4419 4420 didwork = True
4420 4421
4421 4422 # don't let driver-resolved files be marked, and run the conclude
4422 4423 # step if asked to resolve
4423 4424 if ms[f] == "d":
4424 4425 exact = m.exact(f)
4425 4426 if mark:
4426 4427 if exact:
4427 4428 ui.warn(_('not marking %s as it is driver-resolved\n')
4428 4429 % f)
4429 4430 elif unmark:
4430 4431 if exact:
4431 4432 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4432 4433 % f)
4433 4434 else:
4434 4435 runconclude = True
4435 4436 continue
4436 4437
4437 4438 # path conflicts must be resolved manually
4438 4439 if ms[f] in ("pu", "pr"):
4439 4440 if mark:
4440 4441 ms.mark(f, "pr")
4441 4442 elif unmark:
4442 4443 ms.mark(f, "pu")
4443 4444 elif ms[f] == "pu":
4444 4445 ui.warn(_('%s: path conflict must be resolved manually\n')
4445 4446 % f)
4446 4447 continue
4447 4448
4448 4449 if mark:
4449 4450 ms.mark(f, "r")
4450 4451 elif unmark:
4451 4452 ms.mark(f, "u")
4452 4453 else:
4453 4454 # backup pre-resolve (merge uses .orig for its own purposes)
4454 4455 a = repo.wjoin(f)
4455 4456 try:
4456 4457 util.copyfile(a, a + ".resolve")
4457 4458 except (IOError, OSError) as inst:
4458 4459 if inst.errno != errno.ENOENT:
4459 4460 raise
4460 4461
4461 4462 try:
4462 4463 # preresolve file
4463 4464 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4464 4465 'resolve')
4465 4466 complete, r = ms.preresolve(f, wctx)
4466 4467 if not complete:
4467 4468 tocomplete.append(f)
4468 4469 elif r:
4469 4470 ret = 1
4470 4471 finally:
4471 4472 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4472 4473 ms.commit()
4473 4474
4474 4475 # replace filemerge's .orig file with our resolve file, but only
4475 4476 # for merges that are complete
4476 4477 if complete:
4477 4478 try:
4478 4479 util.rename(a + ".resolve",
4479 4480 scmutil.origpath(ui, repo, a))
4480 4481 except OSError as inst:
4481 4482 if inst.errno != errno.ENOENT:
4482 4483 raise
4483 4484
4484 4485 for f in tocomplete:
4485 4486 try:
4486 4487 # resolve file
4487 4488 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4488 4489 'resolve')
4489 4490 r = ms.resolve(f, wctx)
4490 4491 if r:
4491 4492 ret = 1
4492 4493 finally:
4493 4494 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4494 4495 ms.commit()
4495 4496
4496 4497 # replace filemerge's .orig file with our resolve file
4497 4498 a = repo.wjoin(f)
4498 4499 try:
4499 4500 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4500 4501 except OSError as inst:
4501 4502 if inst.errno != errno.ENOENT:
4502 4503 raise
4503 4504
4504 4505 ms.commit()
4505 4506 ms.recordactions()
4506 4507
4507 4508 if not didwork and pats:
4508 4509 hint = None
4509 4510 if not any([p for p in pats if p.find(':') >= 0]):
4510 4511 pats = ['path:%s' % p for p in pats]
4511 4512 m = scmutil.match(wctx, pats, opts)
4512 4513 for f in ms:
4513 4514 if not m(f):
4514 4515 continue
4515 4516 flags = ''.join(['-%s ' % o[0] for o in flaglist
4516 4517 if opts.get(o)])
4517 4518 hint = _("(try: hg resolve %s%s)\n") % (
4518 4519 flags,
4519 4520 ' '.join(pats))
4520 4521 break
4521 4522 ui.warn(_("arguments do not match paths that need resolving\n"))
4522 4523 if hint:
4523 4524 ui.warn(hint)
4524 4525 elif ms.mergedriver and ms.mdstate() != 's':
4525 4526 # run conclude step when either a driver-resolved file is requested
4526 4527 # or there are no driver-resolved files
4527 4528 # we can't use 'ret' to determine whether any files are unresolved
4528 4529 # because we might not have tried to resolve some
4529 4530 if ((runconclude or not list(ms.driverresolved()))
4530 4531 and not list(ms.unresolved())):
4531 4532 proceed = mergemod.driverconclude(repo, ms, wctx)
4532 4533 ms.commit()
4533 4534 if not proceed:
4534 4535 return 1
4535 4536
4536 4537 # Nudge users into finishing an unfinished operation
4537 4538 unresolvedf = list(ms.unresolved())
4538 4539 driverresolvedf = list(ms.driverresolved())
4539 4540 if not unresolvedf and not driverresolvedf:
4540 4541 ui.status(_('(no more unresolved files)\n'))
4541 4542 cmdutil.checkafterresolved(repo)
4542 4543 elif not unresolvedf:
4543 4544 ui.status(_('(no more unresolved files -- '
4544 4545 'run "hg resolve --all" to conclude)\n'))
4545 4546
4546 4547 return ret
4547 4548
4548 4549 @command('revert',
4549 4550 [('a', 'all', None, _('revert all changes when no arguments given')),
4550 4551 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4551 4552 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4552 4553 ('C', 'no-backup', None, _('do not save backup copies of files')),
4553 4554 ('i', 'interactive', None, _('interactively select the changes')),
4554 4555 ] + walkopts + dryrunopts,
4555 4556 _('[OPTION]... [-r REV] [NAME]...'))
4556 4557 def revert(ui, repo, *pats, **opts):
4557 4558 """restore files to their checkout state
4558 4559
4559 4560 .. note::
4560 4561
4561 4562 To check out earlier revisions, you should use :hg:`update REV`.
4562 4563 To cancel an uncommitted merge (and lose your changes),
4563 4564 use :hg:`merge --abort`.
4564 4565
4565 4566 With no revision specified, revert the specified files or directories
4566 4567 to the contents they had in the parent of the working directory.
4567 4568 This restores the contents of files to an unmodified
4568 4569 state and unschedules adds, removes, copies, and renames. If the
4569 4570 working directory has two parents, you must explicitly specify a
4570 4571 revision.
4571 4572
4572 4573 Using the -r/--rev or -d/--date options, revert the given files or
4573 4574 directories to their states as of a specific revision. Because
4574 4575 revert does not change the working directory parents, this will
4575 4576 cause these files to appear modified. This can be helpful to "back
4576 4577 out" some or all of an earlier change. See :hg:`backout` for a
4577 4578 related method.
4578 4579
4579 4580 Modified files are saved with a .orig suffix before reverting.
4580 4581 To disable these backups, use --no-backup. It is possible to store
4581 4582 the backup files in a custom directory relative to the root of the
4582 4583 repository by setting the ``ui.origbackuppath`` configuration
4583 4584 option.
4584 4585
4585 4586 See :hg:`help dates` for a list of formats valid for -d/--date.
4586 4587
4587 4588 See :hg:`help backout` for a way to reverse the effect of an
4588 4589 earlier changeset.
4589 4590
4590 4591 Returns 0 on success.
4591 4592 """
4592 4593
4593 4594 opts = pycompat.byteskwargs(opts)
4594 4595 if opts.get("date"):
4595 4596 if opts.get("rev"):
4596 4597 raise error.Abort(_("you can't specify a revision and a date"))
4597 4598 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4598 4599
4599 4600 parent, p2 = repo.dirstate.parents()
4600 4601 if not opts.get('rev') and p2 != nullid:
4601 4602 # revert after merge is a trap for new users (issue2915)
4602 4603 raise error.Abort(_('uncommitted merge with no revision specified'),
4603 4604 hint=_("use 'hg update' or see 'hg help revert'"))
4604 4605
4605 4606 rev = opts.get('rev')
4606 4607 if rev:
4607 4608 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4608 4609 ctx = scmutil.revsingle(repo, rev)
4609 4610
4610 4611 if (not (pats or opts.get('include') or opts.get('exclude') or
4611 4612 opts.get('all') or opts.get('interactive'))):
4612 4613 msg = _("no files or directories specified")
4613 4614 if p2 != nullid:
4614 4615 hint = _("uncommitted merge, use --all to discard all changes,"
4615 4616 " or 'hg update -C .' to abort the merge")
4616 4617 raise error.Abort(msg, hint=hint)
4617 4618 dirty = any(repo.status())
4618 4619 node = ctx.node()
4619 4620 if node != parent:
4620 4621 if dirty:
4621 4622 hint = _("uncommitted changes, use --all to discard all"
4622 4623 " changes, or 'hg update %s' to update") % ctx.rev()
4623 4624 else:
4624 4625 hint = _("use --all to revert all files,"
4625 4626 " or 'hg update %s' to update") % ctx.rev()
4626 4627 elif dirty:
4627 4628 hint = _("uncommitted changes, use --all to discard all changes")
4628 4629 else:
4629 4630 hint = _("use --all to revert all files")
4630 4631 raise error.Abort(msg, hint=hint)
4631 4632
4632 4633 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
4633 4634 **pycompat.strkwargs(opts))
4634 4635
4635 4636 @command('rollback', dryrunopts +
4636 4637 [('f', 'force', False, _('ignore safety measures'))])
4637 4638 def rollback(ui, repo, **opts):
4638 4639 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4639 4640
4640 4641 Please use :hg:`commit --amend` instead of rollback to correct
4641 4642 mistakes in the last commit.
4642 4643
4643 4644 This command should be used with care. There is only one level of
4644 4645 rollback, and there is no way to undo a rollback. It will also
4645 4646 restore the dirstate at the time of the last transaction, losing
4646 4647 any dirstate changes since that time. This command does not alter
4647 4648 the working directory.
4648 4649
4649 4650 Transactions are used to encapsulate the effects of all commands
4650 4651 that create new changesets or propagate existing changesets into a
4651 4652 repository.
4652 4653
4653 4654 .. container:: verbose
4654 4655
4655 4656 For example, the following commands are transactional, and their
4656 4657 effects can be rolled back:
4657 4658
4658 4659 - commit
4659 4660 - import
4660 4661 - pull
4661 4662 - push (with this repository as the destination)
4662 4663 - unbundle
4663 4664
4664 4665 To avoid permanent data loss, rollback will refuse to rollback a
4665 4666 commit transaction if it isn't checked out. Use --force to
4666 4667 override this protection.
4667 4668
4668 4669 The rollback command can be entirely disabled by setting the
4669 4670 ``ui.rollback`` configuration setting to false. If you're here
4670 4671 because you want to use rollback and it's disabled, you can
4671 4672 re-enable the command by setting ``ui.rollback`` to true.
4672 4673
4673 4674 This command is not intended for use on public repositories. Once
4674 4675 changes are visible for pull by other users, rolling a transaction
4675 4676 back locally is ineffective (someone else may already have pulled
4676 4677 the changes). Furthermore, a race is possible with readers of the
4677 4678 repository; for example an in-progress pull from the repository
4678 4679 may fail if a rollback is performed.
4679 4680
4680 4681 Returns 0 on success, 1 if no rollback data is available.
4681 4682 """
4682 4683 if not ui.configbool('ui', 'rollback'):
4683 4684 raise error.Abort(_('rollback is disabled because it is unsafe'),
4684 4685 hint=('see `hg help -v rollback` for information'))
4685 4686 return repo.rollback(dryrun=opts.get(r'dry_run'),
4686 4687 force=opts.get(r'force'))
4687 4688
4688 4689 @command('root', [], cmdtype=readonly)
4689 4690 def root(ui, repo):
4690 4691 """print the root (top) of the current working directory
4691 4692
4692 4693 Print the root directory of the current repository.
4693 4694
4694 4695 Returns 0 on success.
4695 4696 """
4696 4697 ui.write(repo.root + "\n")
4697 4698
4698 4699 @command('^serve',
4699 4700 [('A', 'accesslog', '', _('name of access log file to write to'),
4700 4701 _('FILE')),
4701 4702 ('d', 'daemon', None, _('run server in background')),
4702 4703 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4703 4704 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4704 4705 # use string type, then we can check if something was passed
4705 4706 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4706 4707 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4707 4708 _('ADDR')),
4708 4709 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4709 4710 _('PREFIX')),
4710 4711 ('n', 'name', '',
4711 4712 _('name to show in web pages (default: working directory)'), _('NAME')),
4712 4713 ('', 'web-conf', '',
4713 4714 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4714 4715 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4715 4716 _('FILE')),
4716 4717 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4717 4718 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4718 4719 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4719 4720 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4720 4721 ('', 'style', '', _('template style to use'), _('STYLE')),
4721 4722 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4722 4723 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4723 4724 + subrepoopts,
4724 4725 _('[OPTION]...'),
4725 4726 optionalrepo=True)
4726 4727 def serve(ui, repo, **opts):
4727 4728 """start stand-alone webserver
4728 4729
4729 4730 Start a local HTTP repository browser and pull server. You can use
4730 4731 this for ad-hoc sharing and browsing of repositories. It is
4731 4732 recommended to use a real web server to serve a repository for
4732 4733 longer periods of time.
4733 4734
4734 4735 Please note that the server does not implement access control.
4735 4736 This means that, by default, anybody can read from the server and
4736 4737 nobody can write to it by default. Set the ``web.allow-push``
4737 4738 option to ``*`` to allow everybody to push to the server. You
4738 4739 should use a real web server if you need to authenticate users.
4739 4740
4740 4741 By default, the server logs accesses to stdout and errors to
4741 4742 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4742 4743 files.
4743 4744
4744 4745 To have the server choose a free port number to listen on, specify
4745 4746 a port number of 0; in this case, the server will print the port
4746 4747 number it uses.
4747 4748
4748 4749 Returns 0 on success.
4749 4750 """
4750 4751
4751 4752 opts = pycompat.byteskwargs(opts)
4752 4753 if opts["stdio"] and opts["cmdserver"]:
4753 4754 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4754 4755
4755 4756 if opts["stdio"]:
4756 4757 if repo is None:
4757 4758 raise error.RepoError(_("there is no Mercurial repository here"
4758 4759 " (.hg not found)"))
4759 4760 s = sshserver.sshserver(ui, repo)
4760 4761 s.serve_forever()
4761 4762
4762 4763 service = server.createservice(ui, repo, opts)
4763 4764 return server.runservice(opts, initfn=service.init, runfn=service.run)
4764 4765
4765 4766 @command('^status|st',
4766 4767 [('A', 'all', None, _('show status of all files')),
4767 4768 ('m', 'modified', None, _('show only modified files')),
4768 4769 ('a', 'added', None, _('show only added files')),
4769 4770 ('r', 'removed', None, _('show only removed files')),
4770 4771 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4771 4772 ('c', 'clean', None, _('show only files without changes')),
4772 4773 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4773 4774 ('i', 'ignored', None, _('show only ignored files')),
4774 4775 ('n', 'no-status', None, _('hide status prefix')),
4775 4776 ('t', 'terse', '', _('show the terse output (EXPERIMENTAL)')),
4776 4777 ('C', 'copies', None, _('show source of copied files')),
4777 4778 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4778 4779 ('', 'rev', [], _('show difference from revision'), _('REV')),
4779 4780 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4780 4781 ] + walkopts + subrepoopts + formatteropts,
4781 4782 _('[OPTION]... [FILE]...'),
4782 4783 inferrepo=True, cmdtype=readonly)
4783 4784 def status(ui, repo, *pats, **opts):
4784 4785 """show changed files in the working directory
4785 4786
4786 4787 Show status of files in the repository. If names are given, only
4787 4788 files that match are shown. Files that are clean or ignored or
4788 4789 the source of a copy/move operation, are not listed unless
4789 4790 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4790 4791 Unless options described with "show only ..." are given, the
4791 4792 options -mardu are used.
4792 4793
4793 4794 Option -q/--quiet hides untracked (unknown and ignored) files
4794 4795 unless explicitly requested with -u/--unknown or -i/--ignored.
4795 4796
4796 4797 .. note::
4797 4798
4798 4799 :hg:`status` may appear to disagree with diff if permissions have
4799 4800 changed or a merge has occurred. The standard diff format does
4800 4801 not report permission changes and diff only reports changes
4801 4802 relative to one merge parent.
4802 4803
4803 4804 If one revision is given, it is used as the base revision.
4804 4805 If two revisions are given, the differences between them are
4805 4806 shown. The --change option can also be used as a shortcut to list
4806 4807 the changed files of a revision from its first parent.
4807 4808
4808 4809 The codes used to show the status of files are::
4809 4810
4810 4811 M = modified
4811 4812 A = added
4812 4813 R = removed
4813 4814 C = clean
4814 4815 ! = missing (deleted by non-hg command, but still tracked)
4815 4816 ? = not tracked
4816 4817 I = ignored
4817 4818 = origin of the previous file (with --copies)
4818 4819
4819 4820 .. container:: verbose
4820 4821
4821 4822 The -t/--terse option abbreviates the output by showing only the directory
4822 4823 name if all the files in it share the same status. The option takes an
4823 4824 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
4824 4825 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
4825 4826 for 'ignored' and 'c' for clean.
4826 4827
4827 4828 It abbreviates only those statuses which are passed. Note that clean and
4828 4829 ignored files are not displayed with '--terse ic' unless the -c/--clean
4829 4830 and -i/--ignored options are also used.
4830 4831
4831 4832 The -v/--verbose option shows information when the repository is in an
4832 4833 unfinished merge, shelve, rebase state etc. You can have this behavior
4833 4834 turned on by default by enabling the ``commands.status.verbose`` option.
4834 4835
4835 4836 You can skip displaying some of these states by setting
4836 4837 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
4837 4838 'histedit', 'merge', 'rebase', or 'unshelve'.
4838 4839
4839 4840 Examples:
4840 4841
4841 4842 - show changes in the working directory relative to a
4842 4843 changeset::
4843 4844
4844 4845 hg status --rev 9353
4845 4846
4846 4847 - show changes in the working directory relative to the
4847 4848 current directory (see :hg:`help patterns` for more information)::
4848 4849
4849 4850 hg status re:
4850 4851
4851 4852 - show all changes including copies in an existing changeset::
4852 4853
4853 4854 hg status --copies --change 9353
4854 4855
4855 4856 - get a NUL separated list of added files, suitable for xargs::
4856 4857
4857 4858 hg status -an0
4858 4859
4859 4860 - show more information about the repository status, abbreviating
4860 4861 added, removed, modified, deleted, and untracked paths::
4861 4862
4862 4863 hg status -v -t mardu
4863 4864
4864 4865 Returns 0 on success.
4865 4866
4866 4867 """
4867 4868
4868 4869 opts = pycompat.byteskwargs(opts)
4869 4870 revs = opts.get('rev')
4870 4871 change = opts.get('change')
4871 4872 terse = opts.get('terse')
4872 4873
4873 4874 if revs and change:
4874 4875 msg = _('cannot specify --rev and --change at the same time')
4875 4876 raise error.Abort(msg)
4876 4877 elif revs and terse:
4877 4878 msg = _('cannot use --terse with --rev')
4878 4879 raise error.Abort(msg)
4879 4880 elif change:
4880 4881 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
4881 4882 node2 = scmutil.revsingle(repo, change, None).node()
4882 4883 node1 = repo[node2].p1().node()
4883 4884 else:
4884 4885 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
4885 4886 node1, node2 = scmutil.revpair(repo, revs)
4886 4887
4887 4888 if pats or ui.configbool('commands', 'status.relative'):
4888 4889 cwd = repo.getcwd()
4889 4890 else:
4890 4891 cwd = ''
4891 4892
4892 4893 if opts.get('print0'):
4893 4894 end = '\0'
4894 4895 else:
4895 4896 end = '\n'
4896 4897 copy = {}
4897 4898 states = 'modified added removed deleted unknown ignored clean'.split()
4898 4899 show = [k for k in states if opts.get(k)]
4899 4900 if opts.get('all'):
4900 4901 show += ui.quiet and (states[:4] + ['clean']) or states
4901 4902
4902 4903 if not show:
4903 4904 if ui.quiet:
4904 4905 show = states[:4]
4905 4906 else:
4906 4907 show = states[:5]
4907 4908
4908 4909 m = scmutil.match(repo[node2], pats, opts)
4909 4910 if terse:
4910 4911 # we need to compute clean and unknown to terse
4911 4912 stat = repo.status(node1, node2, m,
4912 4913 'ignored' in show or 'i' in terse,
4913 4914 True, True, opts.get('subrepos'))
4914 4915
4915 4916 stat = cmdutil.tersedir(stat, terse)
4916 4917 else:
4917 4918 stat = repo.status(node1, node2, m,
4918 4919 'ignored' in show, 'clean' in show,
4919 4920 'unknown' in show, opts.get('subrepos'))
4920 4921
4921 4922 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4922 4923
4923 4924 if (opts.get('all') or opts.get('copies')
4924 4925 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4925 4926 copy = copies.pathcopies(repo[node1], repo[node2], m)
4926 4927
4927 4928 ui.pager('status')
4928 4929 fm = ui.formatter('status', opts)
4929 4930 fmt = '%s' + end
4930 4931 showchar = not opts.get('no_status')
4931 4932
4932 4933 for state, char, files in changestates:
4933 4934 if state in show:
4934 4935 label = 'status.' + state
4935 4936 for f in files:
4936 4937 fm.startitem()
4937 4938 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4938 4939 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4939 4940 if f in copy:
4940 4941 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4941 4942 label='status.copied')
4942 4943
4943 4944 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
4944 4945 and not ui.plain()):
4945 4946 cmdutil.morestatus(repo, fm)
4946 4947 fm.end()
4947 4948
4948 4949 @command('^summary|sum',
4949 4950 [('', 'remote', None, _('check for push and pull'))],
4950 4951 '[--remote]', cmdtype=readonly)
4951 4952 def summary(ui, repo, **opts):
4952 4953 """summarize working directory state
4953 4954
4954 4955 This generates a brief summary of the working directory state,
4955 4956 including parents, branch, commit status, phase and available updates.
4956 4957
4957 4958 With the --remote option, this will check the default paths for
4958 4959 incoming and outgoing changes. This can be time-consuming.
4959 4960
4960 4961 Returns 0 on success.
4961 4962 """
4962 4963
4963 4964 opts = pycompat.byteskwargs(opts)
4964 4965 ui.pager('summary')
4965 4966 ctx = repo[None]
4966 4967 parents = ctx.parents()
4967 4968 pnode = parents[0].node()
4968 4969 marks = []
4969 4970
4970 4971 ms = None
4971 4972 try:
4972 4973 ms = mergemod.mergestate.read(repo)
4973 4974 except error.UnsupportedMergeRecords as e:
4974 4975 s = ' '.join(e.recordtypes)
4975 4976 ui.warn(
4976 4977 _('warning: merge state has unsupported record types: %s\n') % s)
4977 4978 unresolved = []
4978 4979 else:
4979 4980 unresolved = list(ms.unresolved())
4980 4981
4981 4982 for p in parents:
4982 4983 # label with log.changeset (instead of log.parent) since this
4983 4984 # shows a working directory parent *changeset*:
4984 4985 # i18n: column positioning for "hg summary"
4985 4986 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4986 4987 label=cmdutil._changesetlabels(p))
4987 4988 ui.write(' '.join(p.tags()), label='log.tag')
4988 4989 if p.bookmarks():
4989 4990 marks.extend(p.bookmarks())
4990 4991 if p.rev() == -1:
4991 4992 if not len(repo):
4992 4993 ui.write(_(' (empty repository)'))
4993 4994 else:
4994 4995 ui.write(_(' (no revision checked out)'))
4995 4996 if p.obsolete():
4996 4997 ui.write(_(' (obsolete)'))
4997 4998 if p.isunstable():
4998 4999 instabilities = (ui.label(instability, 'trouble.%s' % instability)
4999 5000 for instability in p.instabilities())
5000 5001 ui.write(' ('
5001 5002 + ', '.join(instabilities)
5002 5003 + ')')
5003 5004 ui.write('\n')
5004 5005 if p.description():
5005 5006 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5006 5007 label='log.summary')
5007 5008
5008 5009 branch = ctx.branch()
5009 5010 bheads = repo.branchheads(branch)
5010 5011 # i18n: column positioning for "hg summary"
5011 5012 m = _('branch: %s\n') % branch
5012 5013 if branch != 'default':
5013 5014 ui.write(m, label='log.branch')
5014 5015 else:
5015 5016 ui.status(m, label='log.branch')
5016 5017
5017 5018 if marks:
5018 5019 active = repo._activebookmark
5019 5020 # i18n: column positioning for "hg summary"
5020 5021 ui.write(_('bookmarks:'), label='log.bookmark')
5021 5022 if active is not None:
5022 5023 if active in marks:
5023 5024 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5024 5025 marks.remove(active)
5025 5026 else:
5026 5027 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5027 5028 for m in marks:
5028 5029 ui.write(' ' + m, label='log.bookmark')
5029 5030 ui.write('\n', label='log.bookmark')
5030 5031
5031 5032 status = repo.status(unknown=True)
5032 5033
5033 5034 c = repo.dirstate.copies()
5034 5035 copied, renamed = [], []
5035 5036 for d, s in c.iteritems():
5036 5037 if s in status.removed:
5037 5038 status.removed.remove(s)
5038 5039 renamed.append(d)
5039 5040 else:
5040 5041 copied.append(d)
5041 5042 if d in status.added:
5042 5043 status.added.remove(d)
5043 5044
5044 5045 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5045 5046
5046 5047 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5047 5048 (ui.label(_('%d added'), 'status.added'), status.added),
5048 5049 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5049 5050 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5050 5051 (ui.label(_('%d copied'), 'status.copied'), copied),
5051 5052 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5052 5053 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5053 5054 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5054 5055 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5055 5056 t = []
5056 5057 for l, s in labels:
5057 5058 if s:
5058 5059 t.append(l % len(s))
5059 5060
5060 5061 t = ', '.join(t)
5061 5062 cleanworkdir = False
5062 5063
5063 5064 if repo.vfs.exists('graftstate'):
5064 5065 t += _(' (graft in progress)')
5065 5066 if repo.vfs.exists('updatestate'):
5066 5067 t += _(' (interrupted update)')
5067 5068 elif len(parents) > 1:
5068 5069 t += _(' (merge)')
5069 5070 elif branch != parents[0].branch():
5070 5071 t += _(' (new branch)')
5071 5072 elif (parents[0].closesbranch() and
5072 5073 pnode in repo.branchheads(branch, closed=True)):
5073 5074 t += _(' (head closed)')
5074 5075 elif not (status.modified or status.added or status.removed or renamed or
5075 5076 copied or subs):
5076 5077 t += _(' (clean)')
5077 5078 cleanworkdir = True
5078 5079 elif pnode not in bheads:
5079 5080 t += _(' (new branch head)')
5080 5081
5081 5082 if parents:
5082 5083 pendingphase = max(p.phase() for p in parents)
5083 5084 else:
5084 5085 pendingphase = phases.public
5085 5086
5086 5087 if pendingphase > phases.newcommitphase(ui):
5087 5088 t += ' (%s)' % phases.phasenames[pendingphase]
5088 5089
5089 5090 if cleanworkdir:
5090 5091 # i18n: column positioning for "hg summary"
5091 5092 ui.status(_('commit: %s\n') % t.strip())
5092 5093 else:
5093 5094 # i18n: column positioning for "hg summary"
5094 5095 ui.write(_('commit: %s\n') % t.strip())
5095 5096
5096 5097 # all ancestors of branch heads - all ancestors of parent = new csets
5097 5098 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5098 5099 bheads))
5099 5100
5100 5101 if new == 0:
5101 5102 # i18n: column positioning for "hg summary"
5102 5103 ui.status(_('update: (current)\n'))
5103 5104 elif pnode not in bheads:
5104 5105 # i18n: column positioning for "hg summary"
5105 5106 ui.write(_('update: %d new changesets (update)\n') % new)
5106 5107 else:
5107 5108 # i18n: column positioning for "hg summary"
5108 5109 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5109 5110 (new, len(bheads)))
5110 5111
5111 5112 t = []
5112 5113 draft = len(repo.revs('draft()'))
5113 5114 if draft:
5114 5115 t.append(_('%d draft') % draft)
5115 5116 secret = len(repo.revs('secret()'))
5116 5117 if secret:
5117 5118 t.append(_('%d secret') % secret)
5118 5119
5119 5120 if draft or secret:
5120 5121 ui.status(_('phases: %s\n') % ', '.join(t))
5121 5122
5122 5123 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5123 5124 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5124 5125 numtrouble = len(repo.revs(trouble + "()"))
5125 5126 # We write all the possibilities to ease translation
5126 5127 troublemsg = {
5127 5128 "orphan": _("orphan: %d changesets"),
5128 5129 "contentdivergent": _("content-divergent: %d changesets"),
5129 5130 "phasedivergent": _("phase-divergent: %d changesets"),
5130 5131 }
5131 5132 if numtrouble > 0:
5132 5133 ui.status(troublemsg[trouble] % numtrouble + "\n")
5133 5134
5134 5135 cmdutil.summaryhooks(ui, repo)
5135 5136
5136 5137 if opts.get('remote'):
5137 5138 needsincoming, needsoutgoing = True, True
5138 5139 else:
5139 5140 needsincoming, needsoutgoing = False, False
5140 5141 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5141 5142 if i:
5142 5143 needsincoming = True
5143 5144 if o:
5144 5145 needsoutgoing = True
5145 5146 if not needsincoming and not needsoutgoing:
5146 5147 return
5147 5148
5148 5149 def getincoming():
5149 5150 source, branches = hg.parseurl(ui.expandpath('default'))
5150 5151 sbranch = branches[0]
5151 5152 try:
5152 5153 other = hg.peer(repo, {}, source)
5153 5154 except error.RepoError:
5154 5155 if opts.get('remote'):
5155 5156 raise
5156 5157 return source, sbranch, None, None, None
5157 5158 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5158 5159 if revs:
5159 5160 revs = [other.lookup(rev) for rev in revs]
5160 5161 ui.debug('comparing with %s\n' % util.hidepassword(source))
5161 5162 repo.ui.pushbuffer()
5162 5163 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5163 5164 repo.ui.popbuffer()
5164 5165 return source, sbranch, other, commoninc, commoninc[1]
5165 5166
5166 5167 if needsincoming:
5167 5168 source, sbranch, sother, commoninc, incoming = getincoming()
5168 5169 else:
5169 5170 source = sbranch = sother = commoninc = incoming = None
5170 5171
5171 5172 def getoutgoing():
5172 5173 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5173 5174 dbranch = branches[0]
5174 5175 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5175 5176 if source != dest:
5176 5177 try:
5177 5178 dother = hg.peer(repo, {}, dest)
5178 5179 except error.RepoError:
5179 5180 if opts.get('remote'):
5180 5181 raise
5181 5182 return dest, dbranch, None, None
5182 5183 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5183 5184 elif sother is None:
5184 5185 # there is no explicit destination peer, but source one is invalid
5185 5186 return dest, dbranch, None, None
5186 5187 else:
5187 5188 dother = sother
5188 5189 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5189 5190 common = None
5190 5191 else:
5191 5192 common = commoninc
5192 5193 if revs:
5193 5194 revs = [repo.lookup(rev) for rev in revs]
5194 5195 repo.ui.pushbuffer()
5195 5196 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5196 5197 commoninc=common)
5197 5198 repo.ui.popbuffer()
5198 5199 return dest, dbranch, dother, outgoing
5199 5200
5200 5201 if needsoutgoing:
5201 5202 dest, dbranch, dother, outgoing = getoutgoing()
5202 5203 else:
5203 5204 dest = dbranch = dother = outgoing = None
5204 5205
5205 5206 if opts.get('remote'):
5206 5207 t = []
5207 5208 if incoming:
5208 5209 t.append(_('1 or more incoming'))
5209 5210 o = outgoing.missing
5210 5211 if o:
5211 5212 t.append(_('%d outgoing') % len(o))
5212 5213 other = dother or sother
5213 5214 if 'bookmarks' in other.listkeys('namespaces'):
5214 5215 counts = bookmarks.summary(repo, other)
5215 5216 if counts[0] > 0:
5216 5217 t.append(_('%d incoming bookmarks') % counts[0])
5217 5218 if counts[1] > 0:
5218 5219 t.append(_('%d outgoing bookmarks') % counts[1])
5219 5220
5220 5221 if t:
5221 5222 # i18n: column positioning for "hg summary"
5222 5223 ui.write(_('remote: %s\n') % (', '.join(t)))
5223 5224 else:
5224 5225 # i18n: column positioning for "hg summary"
5225 5226 ui.status(_('remote: (synced)\n'))
5226 5227
5227 5228 cmdutil.summaryremotehooks(ui, repo, opts,
5228 5229 ((source, sbranch, sother, commoninc),
5229 5230 (dest, dbranch, dother, outgoing)))
5230 5231
5231 5232 @command('tag',
5232 5233 [('f', 'force', None, _('force tag')),
5233 5234 ('l', 'local', None, _('make the tag local')),
5234 5235 ('r', 'rev', '', _('revision to tag'), _('REV')),
5235 5236 ('', 'remove', None, _('remove a tag')),
5236 5237 # -l/--local is already there, commitopts cannot be used
5237 5238 ('e', 'edit', None, _('invoke editor on commit messages')),
5238 5239 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5239 5240 ] + commitopts2,
5240 5241 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5241 5242 def tag(ui, repo, name1, *names, **opts):
5242 5243 """add one or more tags for the current or given revision
5243 5244
5244 5245 Name a particular revision using <name>.
5245 5246
5246 5247 Tags are used to name particular revisions of the repository and are
5247 5248 very useful to compare different revisions, to go back to significant
5248 5249 earlier versions or to mark branch points as releases, etc. Changing
5249 5250 an existing tag is normally disallowed; use -f/--force to override.
5250 5251
5251 5252 If no revision is given, the parent of the working directory is
5252 5253 used.
5253 5254
5254 5255 To facilitate version control, distribution, and merging of tags,
5255 5256 they are stored as a file named ".hgtags" which is managed similarly
5256 5257 to other project files and can be hand-edited if necessary. This
5257 5258 also means that tagging creates a new commit. The file
5258 5259 ".hg/localtags" is used for local tags (not shared among
5259 5260 repositories).
5260 5261
5261 5262 Tag commits are usually made at the head of a branch. If the parent
5262 5263 of the working directory is not a branch head, :hg:`tag` aborts; use
5263 5264 -f/--force to force the tag commit to be based on a non-head
5264 5265 changeset.
5265 5266
5266 5267 See :hg:`help dates` for a list of formats valid for -d/--date.
5267 5268
5268 5269 Since tag names have priority over branch names during revision
5269 5270 lookup, using an existing branch name as a tag name is discouraged.
5270 5271
5271 5272 Returns 0 on success.
5272 5273 """
5273 5274 opts = pycompat.byteskwargs(opts)
5274 5275 wlock = lock = None
5275 5276 try:
5276 5277 wlock = repo.wlock()
5277 5278 lock = repo.lock()
5278 5279 rev_ = "."
5279 5280 names = [t.strip() for t in (name1,) + names]
5280 5281 if len(names) != len(set(names)):
5281 5282 raise error.Abort(_('tag names must be unique'))
5282 5283 for n in names:
5283 5284 scmutil.checknewlabel(repo, n, 'tag')
5284 5285 if not n:
5285 5286 raise error.Abort(_('tag names cannot consist entirely of '
5286 5287 'whitespace'))
5287 5288 if opts.get('rev') and opts.get('remove'):
5288 5289 raise error.Abort(_("--rev and --remove are incompatible"))
5289 5290 if opts.get('rev'):
5290 5291 rev_ = opts['rev']
5291 5292 message = opts.get('message')
5292 5293 if opts.get('remove'):
5293 5294 if opts.get('local'):
5294 5295 expectedtype = 'local'
5295 5296 else:
5296 5297 expectedtype = 'global'
5297 5298
5298 5299 for n in names:
5299 5300 if not repo.tagtype(n):
5300 5301 raise error.Abort(_("tag '%s' does not exist") % n)
5301 5302 if repo.tagtype(n) != expectedtype:
5302 5303 if expectedtype == 'global':
5303 5304 raise error.Abort(_("tag '%s' is not a global tag") % n)
5304 5305 else:
5305 5306 raise error.Abort(_("tag '%s' is not a local tag") % n)
5306 5307 rev_ = 'null'
5307 5308 if not message:
5308 5309 # we don't translate commit messages
5309 5310 message = 'Removed tag %s' % ', '.join(names)
5310 5311 elif not opts.get('force'):
5311 5312 for n in names:
5312 5313 if n in repo.tags():
5313 5314 raise error.Abort(_("tag '%s' already exists "
5314 5315 "(use -f to force)") % n)
5315 5316 if not opts.get('local'):
5316 5317 p1, p2 = repo.dirstate.parents()
5317 5318 if p2 != nullid:
5318 5319 raise error.Abort(_('uncommitted merge'))
5319 5320 bheads = repo.branchheads()
5320 5321 if not opts.get('force') and bheads and p1 not in bheads:
5321 5322 raise error.Abort(_('working directory is not at a branch head '
5322 5323 '(use -f to force)'))
5323 5324 r = scmutil.revsingle(repo, rev_).node()
5324 5325
5325 5326 if not message:
5326 5327 # we don't translate commit messages
5327 5328 message = ('Added tag %s for changeset %s' %
5328 5329 (', '.join(names), short(r)))
5329 5330
5330 5331 date = opts.get('date')
5331 5332 if date:
5332 5333 date = util.parsedate(date)
5333 5334
5334 5335 if opts.get('remove'):
5335 5336 editform = 'tag.remove'
5336 5337 else:
5337 5338 editform = 'tag.add'
5338 5339 editor = cmdutil.getcommiteditor(editform=editform,
5339 5340 **pycompat.strkwargs(opts))
5340 5341
5341 5342 # don't allow tagging the null rev
5342 5343 if (not opts.get('remove') and
5343 5344 scmutil.revsingle(repo, rev_).rev() == nullrev):
5344 5345 raise error.Abort(_("cannot tag null revision"))
5345 5346
5346 5347 tagsmod.tag(repo, names, r, message, opts.get('local'),
5347 5348 opts.get('user'), date, editor=editor)
5348 5349 finally:
5349 5350 release(lock, wlock)
5350 5351
5351 5352 @command('tags', formatteropts, '', cmdtype=readonly)
5352 5353 def tags(ui, repo, **opts):
5353 5354 """list repository tags
5354 5355
5355 5356 This lists both regular and local tags. When the -v/--verbose
5356 5357 switch is used, a third column "local" is printed for local tags.
5357 5358 When the -q/--quiet switch is used, only the tag name is printed.
5358 5359
5359 5360 Returns 0 on success.
5360 5361 """
5361 5362
5362 5363 opts = pycompat.byteskwargs(opts)
5363 5364 ui.pager('tags')
5364 5365 fm = ui.formatter('tags', opts)
5365 5366 hexfunc = fm.hexfunc
5366 5367 tagtype = ""
5367 5368
5368 5369 for t, n in reversed(repo.tagslist()):
5369 5370 hn = hexfunc(n)
5370 5371 label = 'tags.normal'
5371 5372 tagtype = ''
5372 5373 if repo.tagtype(t) == 'local':
5373 5374 label = 'tags.local'
5374 5375 tagtype = 'local'
5375 5376
5376 5377 fm.startitem()
5377 5378 fm.write('tag', '%s', t, label=label)
5378 5379 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5379 5380 fm.condwrite(not ui.quiet, 'rev node', fmt,
5380 5381 repo.changelog.rev(n), hn, label=label)
5381 5382 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5382 5383 tagtype, label=label)
5383 5384 fm.plain('\n')
5384 5385 fm.end()
5385 5386
5386 5387 @command('tip',
5387 5388 [('p', 'patch', None, _('show patch')),
5388 5389 ('g', 'git', None, _('use git extended diff format')),
5389 5390 ] + templateopts,
5390 5391 _('[-p] [-g]'))
5391 5392 def tip(ui, repo, **opts):
5392 5393 """show the tip revision (DEPRECATED)
5393 5394
5394 5395 The tip revision (usually just called the tip) is the changeset
5395 5396 most recently added to the repository (and therefore the most
5396 5397 recently changed head).
5397 5398
5398 5399 If you have just made a commit, that commit will be the tip. If
5399 5400 you have just pulled changes from another repository, the tip of
5400 5401 that repository becomes the current tip. The "tip" tag is special
5401 5402 and cannot be renamed or assigned to a different changeset.
5402 5403
5403 5404 This command is deprecated, please use :hg:`heads` instead.
5404 5405
5405 5406 Returns 0 on success.
5406 5407 """
5407 5408 opts = pycompat.byteskwargs(opts)
5408 5409 displayer = cmdutil.show_changeset(ui, repo, opts)
5409 5410 displayer.show(repo['tip'])
5410 5411 displayer.close()
5411 5412
5412 5413 @command('unbundle',
5413 5414 [('u', 'update', None,
5414 5415 _('update to new branch head if changesets were unbundled'))],
5415 5416 _('[-u] FILE...'))
5416 5417 def unbundle(ui, repo, fname1, *fnames, **opts):
5417 5418 """apply one or more bundle files
5418 5419
5419 5420 Apply one or more bundle files generated by :hg:`bundle`.
5420 5421
5421 5422 Returns 0 on success, 1 if an update has unresolved files.
5422 5423 """
5423 5424 fnames = (fname1,) + fnames
5424 5425
5425 5426 with repo.lock():
5426 5427 for fname in fnames:
5427 5428 f = hg.openpath(ui, fname)
5428 5429 gen = exchange.readbundle(ui, f, fname)
5429 5430 if isinstance(gen, streamclone.streamcloneapplier):
5430 5431 raise error.Abort(
5431 5432 _('packed bundles cannot be applied with '
5432 5433 '"hg unbundle"'),
5433 5434 hint=_('use "hg debugapplystreamclonebundle"'))
5434 5435 url = 'bundle:' + fname
5435 5436 try:
5436 5437 txnname = 'unbundle'
5437 5438 if not isinstance(gen, bundle2.unbundle20):
5438 5439 txnname = 'unbundle\n%s' % util.hidepassword(url)
5439 5440 with repo.transaction(txnname) as tr:
5440 5441 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5441 5442 url=url)
5442 5443 except error.BundleUnknownFeatureError as exc:
5443 5444 raise error.Abort(
5444 5445 _('%s: unknown bundle feature, %s') % (fname, exc),
5445 5446 hint=_("see https://mercurial-scm.org/"
5446 5447 "wiki/BundleFeature for more "
5447 5448 "information"))
5448 5449 modheads = bundle2.combinechangegroupresults(op)
5449 5450
5450 5451 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5451 5452
5452 5453 @command('^update|up|checkout|co',
5453 5454 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5454 5455 ('c', 'check', None, _('require clean working directory')),
5455 5456 ('m', 'merge', None, _('merge uncommitted changes')),
5456 5457 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5457 5458 ('r', 'rev', '', _('revision'), _('REV'))
5458 5459 ] + mergetoolopts,
5459 5460 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5460 5461 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5461 5462 merge=None, tool=None):
5462 5463 """update working directory (or switch revisions)
5463 5464
5464 5465 Update the repository's working directory to the specified
5465 5466 changeset. If no changeset is specified, update to the tip of the
5466 5467 current named branch and move the active bookmark (see :hg:`help
5467 5468 bookmarks`).
5468 5469
5469 5470 Update sets the working directory's parent revision to the specified
5470 5471 changeset (see :hg:`help parents`).
5471 5472
5472 5473 If the changeset is not a descendant or ancestor of the working
5473 5474 directory's parent and there are uncommitted changes, the update is
5474 5475 aborted. With the -c/--check option, the working directory is checked
5475 5476 for uncommitted changes; if none are found, the working directory is
5476 5477 updated to the specified changeset.
5477 5478
5478 5479 .. container:: verbose
5479 5480
5480 5481 The -C/--clean, -c/--check, and -m/--merge options control what
5481 5482 happens if the working directory contains uncommitted changes.
5482 5483 At most of one of them can be specified.
5483 5484
5484 5485 1. If no option is specified, and if
5485 5486 the requested changeset is an ancestor or descendant of
5486 5487 the working directory's parent, the uncommitted changes
5487 5488 are merged into the requested changeset and the merged
5488 5489 result is left uncommitted. If the requested changeset is
5489 5490 not an ancestor or descendant (that is, it is on another
5490 5491 branch), the update is aborted and the uncommitted changes
5491 5492 are preserved.
5492 5493
5493 5494 2. With the -m/--merge option, the update is allowed even if the
5494 5495 requested changeset is not an ancestor or descendant of
5495 5496 the working directory's parent.
5496 5497
5497 5498 3. With the -c/--check option, the update is aborted and the
5498 5499 uncommitted changes are preserved.
5499 5500
5500 5501 4. With the -C/--clean option, uncommitted changes are discarded and
5501 5502 the working directory is updated to the requested changeset.
5502 5503
5503 5504 To cancel an uncommitted merge (and lose your changes), use
5504 5505 :hg:`merge --abort`.
5505 5506
5506 5507 Use null as the changeset to remove the working directory (like
5507 5508 :hg:`clone -U`).
5508 5509
5509 5510 If you want to revert just one file to an older revision, use
5510 5511 :hg:`revert [-r REV] NAME`.
5511 5512
5512 5513 See :hg:`help dates` for a list of formats valid for -d/--date.
5513 5514
5514 5515 Returns 0 on success, 1 if there are unresolved files.
5515 5516 """
5516 5517 if rev and node:
5517 5518 raise error.Abort(_("please specify just one revision"))
5518 5519
5519 5520 if ui.configbool('commands', 'update.requiredest'):
5520 5521 if not node and not rev and not date:
5521 5522 raise error.Abort(_('you must specify a destination'),
5522 5523 hint=_('for example: hg update ".::"'))
5523 5524
5524 5525 if rev is None or rev == '':
5525 5526 rev = node
5526 5527
5527 5528 if date and rev is not None:
5528 5529 raise error.Abort(_("you can't specify a revision and a date"))
5529 5530
5530 5531 if len([x for x in (clean, check, merge) if x]) > 1:
5531 5532 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5532 5533 "or -m/--merge"))
5533 5534
5534 5535 updatecheck = None
5535 5536 if check:
5536 5537 updatecheck = 'abort'
5537 5538 elif merge:
5538 5539 updatecheck = 'none'
5539 5540
5540 5541 with repo.wlock():
5541 5542 cmdutil.clearunfinished(repo)
5542 5543
5543 5544 if date:
5544 5545 rev = cmdutil.finddate(ui, repo, date)
5545 5546
5546 5547 # if we defined a bookmark, we have to remember the original name
5547 5548 brev = rev
5548 5549 if rev:
5549 5550 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5550 5551 ctx = scmutil.revsingle(repo, rev, rev)
5551 5552 rev = ctx.rev()
5552 5553 if ctx.hidden():
5553 5554 ctxstr = ctx.hex()[:12]
5554 5555 ui.warn(_("updating to a hidden changeset %s\n") % ctxstr)
5555 5556
5556 5557 if ctx.obsolete():
5557 5558 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
5558 5559 ui.warn("(%s)\n" % obsfatemsg)
5559 5560
5560 5561 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
5561 5562
5562 5563 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5563 5564 updatecheck=updatecheck)
5564 5565
5565 5566 @command('verify', [])
5566 5567 def verify(ui, repo):
5567 5568 """verify the integrity of the repository
5568 5569
5569 5570 Verify the integrity of the current repository.
5570 5571
5571 5572 This will perform an extensive check of the repository's
5572 5573 integrity, validating the hashes and checksums of each entry in
5573 5574 the changelog, manifest, and tracked files, as well as the
5574 5575 integrity of their crosslinks and indices.
5575 5576
5576 5577 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5577 5578 for more information about recovery from corruption of the
5578 5579 repository.
5579 5580
5580 5581 Returns 0 on success, 1 if errors are encountered.
5581 5582 """
5582 5583 return hg.verify(repo)
5583 5584
5584 5585 @command('version', [] + formatteropts, norepo=True, cmdtype=readonly)
5585 5586 def version_(ui, **opts):
5586 5587 """output version and copyright information"""
5587 5588 opts = pycompat.byteskwargs(opts)
5588 5589 if ui.verbose:
5589 5590 ui.pager('version')
5590 5591 fm = ui.formatter("version", opts)
5591 5592 fm.startitem()
5592 5593 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5593 5594 util.version())
5594 5595 license = _(
5595 5596 "(see https://mercurial-scm.org for more information)\n"
5596 5597 "\nCopyright (C) 2005-2018 Matt Mackall and others\n"
5597 5598 "This is free software; see the source for copying conditions. "
5598 5599 "There is NO\nwarranty; "
5599 5600 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5600 5601 )
5601 5602 if not ui.quiet:
5602 5603 fm.plain(license)
5603 5604
5604 5605 if ui.verbose:
5605 5606 fm.plain(_("\nEnabled extensions:\n\n"))
5606 5607 # format names and versions into columns
5607 5608 names = []
5608 5609 vers = []
5609 5610 isinternals = []
5610 5611 for name, module in extensions.extensions():
5611 5612 names.append(name)
5612 5613 vers.append(extensions.moduleversion(module) or None)
5613 5614 isinternals.append(extensions.ismoduleinternal(module))
5614 5615 fn = fm.nested("extensions")
5615 5616 if names:
5616 5617 namefmt = " %%-%ds " % max(len(n) for n in names)
5617 5618 places = [_("external"), _("internal")]
5618 5619 for n, v, p in zip(names, vers, isinternals):
5619 5620 fn.startitem()
5620 5621 fn.condwrite(ui.verbose, "name", namefmt, n)
5621 5622 if ui.verbose:
5622 5623 fn.plain("%s " % places[p])
5623 5624 fn.data(bundled=p)
5624 5625 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5625 5626 if ui.verbose:
5626 5627 fn.plain("\n")
5627 5628 fn.end()
5628 5629 fm.end()
5629 5630
5630 5631 def loadcmdtable(ui, name, cmdtable):
5631 5632 """Load command functions from specified cmdtable
5632 5633 """
5633 5634 overrides = [cmd for cmd in cmdtable if cmd in table]
5634 5635 if overrides:
5635 5636 ui.warn(_("extension '%s' overrides commands: %s\n")
5636 5637 % (name, " ".join(overrides)))
5637 5638 table.update(cmdtable)
General Comments 0
You need to be logged in to leave comments. Login now