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