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