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