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