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