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