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