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