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