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