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