##// END OF EJS Templates
debugcommands: move 'debugknown' in the new module
Pierre-Yves David -
r30919:e1fa5fe9 default
parent child Browse files
Show More
@@ -1,6457 +1,6444
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('debugknown', [], _('REPO ID...'), norepo=True)
1867 def debugknown(ui, repopath, *ids, **opts):
1868 """test whether node ids are known to a repo
1869
1870 Every ID must be a full-length hex node id string. Returns a list of 0s
1871 and 1s indicating unknown/known.
1872 """
1873 repo = hg.peer(ui, opts, repopath)
1874 if not repo.capable('known'):
1875 raise error.Abort("known() not supported by target repository")
1876 flags = repo.known([bin(s) for s in ids])
1877 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1878
1879 1866 @command('debuglabelcomplete', [], _('LABEL...'))
1880 1867 def debuglabelcomplete(ui, repo, *args):
1881 1868 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
1882 1869 debugnamecomplete(ui, repo, *args)
1883 1870
1884 1871 @command('debugmergestate', [], '')
1885 1872 def debugmergestate(ui, repo, *args):
1886 1873 """print merge state
1887 1874
1888 1875 Use --verbose to print out information about whether v1 or v2 merge state
1889 1876 was chosen."""
1890 1877 def _hashornull(h):
1891 1878 if h == nullhex:
1892 1879 return 'null'
1893 1880 else:
1894 1881 return h
1895 1882
1896 1883 def printrecords(version):
1897 1884 ui.write(('* version %s records\n') % version)
1898 1885 if version == 1:
1899 1886 records = v1records
1900 1887 else:
1901 1888 records = v2records
1902 1889
1903 1890 for rtype, record in records:
1904 1891 # pretty print some record types
1905 1892 if rtype == 'L':
1906 1893 ui.write(('local: %s\n') % record)
1907 1894 elif rtype == 'O':
1908 1895 ui.write(('other: %s\n') % record)
1909 1896 elif rtype == 'm':
1910 1897 driver, mdstate = record.split('\0', 1)
1911 1898 ui.write(('merge driver: %s (state "%s")\n')
1912 1899 % (driver, mdstate))
1913 1900 elif rtype in 'FDC':
1914 1901 r = record.split('\0')
1915 1902 f, state, hash, lfile, afile, anode, ofile = r[0:7]
1916 1903 if version == 1:
1917 1904 onode = 'not stored in v1 format'
1918 1905 flags = r[7]
1919 1906 else:
1920 1907 onode, flags = r[7:9]
1921 1908 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
1922 1909 % (f, rtype, state, _hashornull(hash)))
1923 1910 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
1924 1911 ui.write((' ancestor path: %s (node %s)\n')
1925 1912 % (afile, _hashornull(anode)))
1926 1913 ui.write((' other path: %s (node %s)\n')
1927 1914 % (ofile, _hashornull(onode)))
1928 1915 elif rtype == 'f':
1929 1916 filename, rawextras = record.split('\0', 1)
1930 1917 extras = rawextras.split('\0')
1931 1918 i = 0
1932 1919 extrastrings = []
1933 1920 while i < len(extras):
1934 1921 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
1935 1922 i += 2
1936 1923
1937 1924 ui.write(('file extras: %s (%s)\n')
1938 1925 % (filename, ', '.join(extrastrings)))
1939 1926 elif rtype == 'l':
1940 1927 labels = record.split('\0', 2)
1941 1928 labels = [l for l in labels if len(l) > 0]
1942 1929 ui.write(('labels:\n'))
1943 1930 ui.write((' local: %s\n' % labels[0]))
1944 1931 ui.write((' other: %s\n' % labels[1]))
1945 1932 if len(labels) > 2:
1946 1933 ui.write((' base: %s\n' % labels[2]))
1947 1934 else:
1948 1935 ui.write(('unrecognized entry: %s\t%s\n')
1949 1936 % (rtype, record.replace('\0', '\t')))
1950 1937
1951 1938 # Avoid mergestate.read() since it may raise an exception for unsupported
1952 1939 # merge state records. We shouldn't be doing this, but this is OK since this
1953 1940 # command is pretty low-level.
1954 1941 ms = mergemod.mergestate(repo)
1955 1942
1956 1943 # sort so that reasonable information is on top
1957 1944 v1records = ms._readrecordsv1()
1958 1945 v2records = ms._readrecordsv2()
1959 1946 order = 'LOml'
1960 1947 def key(r):
1961 1948 idx = order.find(r[0])
1962 1949 if idx == -1:
1963 1950 return (1, r[1])
1964 1951 else:
1965 1952 return (0, idx)
1966 1953 v1records.sort(key=key)
1967 1954 v2records.sort(key=key)
1968 1955
1969 1956 if not v1records and not v2records:
1970 1957 ui.write(('no merge state found\n'))
1971 1958 elif not v2records:
1972 1959 ui.note(('no version 2 merge state\n'))
1973 1960 printrecords(1)
1974 1961 elif ms._v1v2match(v1records, v2records):
1975 1962 ui.note(('v1 and v2 states match: using v2\n'))
1976 1963 printrecords(2)
1977 1964 else:
1978 1965 ui.note(('v1 and v2 states mismatch: using v1\n'))
1979 1966 printrecords(1)
1980 1967 if ui.verbose:
1981 1968 printrecords(2)
1982 1969
1983 1970 @command('debugnamecomplete', [], _('NAME...'))
1984 1971 def debugnamecomplete(ui, repo, *args):
1985 1972 '''complete "names" - tags, open branch names, bookmark names'''
1986 1973
1987 1974 names = set()
1988 1975 # since we previously only listed open branches, we will handle that
1989 1976 # specially (after this for loop)
1990 1977 for name, ns in repo.names.iteritems():
1991 1978 if name != 'branches':
1992 1979 names.update(ns.listnames(repo))
1993 1980 names.update(tag for (tag, heads, tip, closed)
1994 1981 in repo.branchmap().iterbranches() if not closed)
1995 1982 completions = set()
1996 1983 if not args:
1997 1984 args = ['']
1998 1985 for a in args:
1999 1986 completions.update(n for n in names if n.startswith(a))
2000 1987 ui.write('\n'.join(sorted(completions)))
2001 1988 ui.write('\n')
2002 1989
2003 1990 @command('debuglocks',
2004 1991 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2005 1992 ('W', 'force-wlock', None,
2006 1993 _('free the working state lock (DANGEROUS)'))],
2007 1994 _('[OPTION]...'))
2008 1995 def debuglocks(ui, repo, **opts):
2009 1996 """show or modify state of locks
2010 1997
2011 1998 By default, this command will show which locks are held. This
2012 1999 includes the user and process holding the lock, the amount of time
2013 2000 the lock has been held, and the machine name where the process is
2014 2001 running if it's not local.
2015 2002
2016 2003 Locks protect the integrity of Mercurial's data, so should be
2017 2004 treated with care. System crashes or other interruptions may cause
2018 2005 locks to not be properly released, though Mercurial will usually
2019 2006 detect and remove such stale locks automatically.
2020 2007
2021 2008 However, detecting stale locks may not always be possible (for
2022 2009 instance, on a shared filesystem). Removing locks may also be
2023 2010 blocked by filesystem permissions.
2024 2011
2025 2012 Returns 0 if no locks are held.
2026 2013
2027 2014 """
2028 2015
2029 2016 if opts.get('force_lock'):
2030 2017 repo.svfs.unlink('lock')
2031 2018 if opts.get('force_wlock'):
2032 2019 repo.vfs.unlink('wlock')
2033 2020 if opts.get('force_lock') or opts.get('force_lock'):
2034 2021 return 0
2035 2022
2036 2023 now = time.time()
2037 2024 held = 0
2038 2025
2039 2026 def report(vfs, name, method):
2040 2027 # this causes stale locks to get reaped for more accurate reporting
2041 2028 try:
2042 2029 l = method(False)
2043 2030 except error.LockHeld:
2044 2031 l = None
2045 2032
2046 2033 if l:
2047 2034 l.release()
2048 2035 else:
2049 2036 try:
2050 2037 stat = vfs.lstat(name)
2051 2038 age = now - stat.st_mtime
2052 2039 user = util.username(stat.st_uid)
2053 2040 locker = vfs.readlock(name)
2054 2041 if ":" in locker:
2055 2042 host, pid = locker.split(':')
2056 2043 if host == socket.gethostname():
2057 2044 locker = 'user %s, process %s' % (user, pid)
2058 2045 else:
2059 2046 locker = 'user %s, process %s, host %s' \
2060 2047 % (user, pid, host)
2061 2048 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
2062 2049 return 1
2063 2050 except OSError as e:
2064 2051 if e.errno != errno.ENOENT:
2065 2052 raise
2066 2053
2067 2054 ui.write(("%-6s free\n") % (name + ":"))
2068 2055 return 0
2069 2056
2070 2057 held += report(repo.svfs, "lock", repo.lock)
2071 2058 held += report(repo.vfs, "wlock", repo.wlock)
2072 2059
2073 2060 return held
2074 2061
2075 2062 @command('debugobsolete',
2076 2063 [('', 'flags', 0, _('markers flag')),
2077 2064 ('', 'record-parents', False,
2078 2065 _('record parent information for the precursor')),
2079 2066 ('r', 'rev', [], _('display markers relevant to REV')),
2080 2067 ('', 'index', False, _('display index of the marker')),
2081 2068 ('', 'delete', [], _('delete markers specified by indices')),
2082 2069 ] + commitopts2 + formatteropts,
2083 2070 _('[OBSOLETED [REPLACEMENT ...]]'))
2084 2071 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2085 2072 """create arbitrary obsolete marker
2086 2073
2087 2074 With no arguments, displays the list of obsolescence markers."""
2088 2075
2089 2076 def parsenodeid(s):
2090 2077 try:
2091 2078 # We do not use revsingle/revrange functions here to accept
2092 2079 # arbitrary node identifiers, possibly not present in the
2093 2080 # local repository.
2094 2081 n = bin(s)
2095 2082 if len(n) != len(nullid):
2096 2083 raise TypeError()
2097 2084 return n
2098 2085 except TypeError:
2099 2086 raise error.Abort('changeset references must be full hexadecimal '
2100 2087 'node identifiers')
2101 2088
2102 2089 if opts.get('delete'):
2103 2090 indices = []
2104 2091 for v in opts.get('delete'):
2105 2092 try:
2106 2093 indices.append(int(v))
2107 2094 except ValueError:
2108 2095 raise error.Abort(_('invalid index value: %r') % v,
2109 2096 hint=_('use integers for indices'))
2110 2097
2111 2098 if repo.currenttransaction():
2112 2099 raise error.Abort(_('cannot delete obsmarkers in the middle '
2113 2100 'of transaction.'))
2114 2101
2115 2102 with repo.lock():
2116 2103 n = repair.deleteobsmarkers(repo.obsstore, indices)
2117 2104 ui.write(_('deleted %i obsolescence markers\n') % n)
2118 2105
2119 2106 return
2120 2107
2121 2108 if precursor is not None:
2122 2109 if opts['rev']:
2123 2110 raise error.Abort('cannot select revision when creating marker')
2124 2111 metadata = {}
2125 2112 metadata['user'] = opts['user'] or ui.username()
2126 2113 succs = tuple(parsenodeid(succ) for succ in successors)
2127 2114 l = repo.lock()
2128 2115 try:
2129 2116 tr = repo.transaction('debugobsolete')
2130 2117 try:
2131 2118 date = opts.get('date')
2132 2119 if date:
2133 2120 date = util.parsedate(date)
2134 2121 else:
2135 2122 date = None
2136 2123 prec = parsenodeid(precursor)
2137 2124 parents = None
2138 2125 if opts['record_parents']:
2139 2126 if prec not in repo.unfiltered():
2140 2127 raise error.Abort('cannot used --record-parents on '
2141 2128 'unknown changesets')
2142 2129 parents = repo.unfiltered()[prec].parents()
2143 2130 parents = tuple(p.node() for p in parents)
2144 2131 repo.obsstore.create(tr, prec, succs, opts['flags'],
2145 2132 parents=parents, date=date,
2146 2133 metadata=metadata)
2147 2134 tr.close()
2148 2135 except ValueError as exc:
2149 2136 raise error.Abort(_('bad obsmarker input: %s') % exc)
2150 2137 finally:
2151 2138 tr.release()
2152 2139 finally:
2153 2140 l.release()
2154 2141 else:
2155 2142 if opts['rev']:
2156 2143 revs = scmutil.revrange(repo, opts['rev'])
2157 2144 nodes = [repo[r].node() for r in revs]
2158 2145 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2159 2146 markers.sort(key=lambda x: x._data)
2160 2147 else:
2161 2148 markers = obsolete.getmarkers(repo)
2162 2149
2163 2150 markerstoiter = markers
2164 2151 isrelevant = lambda m: True
2165 2152 if opts.get('rev') and opts.get('index'):
2166 2153 markerstoiter = obsolete.getmarkers(repo)
2167 2154 markerset = set(markers)
2168 2155 isrelevant = lambda m: m in markerset
2169 2156
2170 2157 fm = ui.formatter('debugobsolete', opts)
2171 2158 for i, m in enumerate(markerstoiter):
2172 2159 if not isrelevant(m):
2173 2160 # marker can be irrelevant when we're iterating over a set
2174 2161 # of markers (markerstoiter) which is bigger than the set
2175 2162 # of markers we want to display (markers)
2176 2163 # this can happen if both --index and --rev options are
2177 2164 # provided and thus we need to iterate over all of the markers
2178 2165 # to get the correct indices, but only display the ones that
2179 2166 # are relevant to --rev value
2180 2167 continue
2181 2168 fm.startitem()
2182 2169 ind = i if opts.get('index') else None
2183 2170 cmdutil.showmarker(fm, m, index=ind)
2184 2171 fm.end()
2185 2172
2186 2173 @command('debugpathcomplete',
2187 2174 [('f', 'full', None, _('complete an entire path')),
2188 2175 ('n', 'normal', None, _('show only normal files')),
2189 2176 ('a', 'added', None, _('show only added files')),
2190 2177 ('r', 'removed', None, _('show only removed files'))],
2191 2178 _('FILESPEC...'))
2192 2179 def debugpathcomplete(ui, repo, *specs, **opts):
2193 2180 '''complete part or all of a tracked path
2194 2181
2195 2182 This command supports shells that offer path name completion. It
2196 2183 currently completes only files already known to the dirstate.
2197 2184
2198 2185 Completion extends only to the next path segment unless
2199 2186 --full is specified, in which case entire paths are used.'''
2200 2187
2201 2188 def complete(path, acceptable):
2202 2189 dirstate = repo.dirstate
2203 2190 spec = os.path.normpath(os.path.join(pycompat.getcwd(), path))
2204 2191 rootdir = repo.root + pycompat.ossep
2205 2192 if spec != repo.root and not spec.startswith(rootdir):
2206 2193 return [], []
2207 2194 if os.path.isdir(spec):
2208 2195 spec += '/'
2209 2196 spec = spec[len(rootdir):]
2210 2197 fixpaths = pycompat.ossep != '/'
2211 2198 if fixpaths:
2212 2199 spec = spec.replace(pycompat.ossep, '/')
2213 2200 speclen = len(spec)
2214 2201 fullpaths = opts['full']
2215 2202 files, dirs = set(), set()
2216 2203 adddir, addfile = dirs.add, files.add
2217 2204 for f, st in dirstate.iteritems():
2218 2205 if f.startswith(spec) and st[0] in acceptable:
2219 2206 if fixpaths:
2220 2207 f = f.replace('/', pycompat.ossep)
2221 2208 if fullpaths:
2222 2209 addfile(f)
2223 2210 continue
2224 2211 s = f.find(pycompat.ossep, speclen)
2225 2212 if s >= 0:
2226 2213 adddir(f[:s])
2227 2214 else:
2228 2215 addfile(f)
2229 2216 return files, dirs
2230 2217
2231 2218 acceptable = ''
2232 2219 if opts['normal']:
2233 2220 acceptable += 'nm'
2234 2221 if opts['added']:
2235 2222 acceptable += 'a'
2236 2223 if opts['removed']:
2237 2224 acceptable += 'r'
2238 2225 cwd = repo.getcwd()
2239 2226 if not specs:
2240 2227 specs = ['.']
2241 2228
2242 2229 files, dirs = set(), set()
2243 2230 for spec in specs:
2244 2231 f, d = complete(spec, acceptable or 'nmar')
2245 2232 files.update(f)
2246 2233 dirs.update(d)
2247 2234 files.update(dirs)
2248 2235 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2249 2236 ui.write('\n')
2250 2237
2251 2238 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2252 2239 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2253 2240 '''access the pushkey key/value protocol
2254 2241
2255 2242 With two args, list the keys in the given namespace.
2256 2243
2257 2244 With five args, set a key to new if it currently is set to old.
2258 2245 Reports success or failure.
2259 2246 '''
2260 2247
2261 2248 target = hg.peer(ui, {}, repopath)
2262 2249 if keyinfo:
2263 2250 key, old, new = keyinfo
2264 2251 r = target.pushkey(namespace, key, old, new)
2265 2252 ui.status(str(r) + '\n')
2266 2253 return not r
2267 2254 else:
2268 2255 for k, v in sorted(target.listkeys(namespace).iteritems()):
2269 2256 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2270 2257 v.encode('string-escape')))
2271 2258
2272 2259 @command('debugpvec', [], _('A B'))
2273 2260 def debugpvec(ui, repo, a, b=None):
2274 2261 ca = scmutil.revsingle(repo, a)
2275 2262 cb = scmutil.revsingle(repo, b)
2276 2263 pa = pvec.ctxpvec(ca)
2277 2264 pb = pvec.ctxpvec(cb)
2278 2265 if pa == pb:
2279 2266 rel = "="
2280 2267 elif pa > pb:
2281 2268 rel = ">"
2282 2269 elif pa < pb:
2283 2270 rel = "<"
2284 2271 elif pa | pb:
2285 2272 rel = "|"
2286 2273 ui.write(_("a: %s\n") % pa)
2287 2274 ui.write(_("b: %s\n") % pb)
2288 2275 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2289 2276 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2290 2277 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2291 2278 pa.distance(pb), rel))
2292 2279
2293 2280 @command('debugrebuilddirstate|debugrebuildstate',
2294 2281 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
2295 2282 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
2296 2283 'the working copy parent')),
2297 2284 ],
2298 2285 _('[-r REV]'))
2299 2286 def debugrebuilddirstate(ui, repo, rev, **opts):
2300 2287 """rebuild the dirstate as it would look like for the given revision
2301 2288
2302 2289 If no revision is specified the first current parent will be used.
2303 2290
2304 2291 The dirstate will be set to the files of the given revision.
2305 2292 The actual working directory content or existing dirstate
2306 2293 information such as adds or removes is not considered.
2307 2294
2308 2295 ``minimal`` will only rebuild the dirstate status for files that claim to be
2309 2296 tracked but are not in the parent manifest, or that exist in the parent
2310 2297 manifest but are not in the dirstate. It will not change adds, removes, or
2311 2298 modified files that are in the working copy parent.
2312 2299
2313 2300 One use of this command is to make the next :hg:`status` invocation
2314 2301 check the actual file content.
2315 2302 """
2316 2303 ctx = scmutil.revsingle(repo, rev)
2317 2304 with repo.wlock():
2318 2305 dirstate = repo.dirstate
2319 2306 changedfiles = None
2320 2307 # See command doc for what minimal does.
2321 2308 if opts.get('minimal'):
2322 2309 manifestfiles = set(ctx.manifest().keys())
2323 2310 dirstatefiles = set(dirstate)
2324 2311 manifestonly = manifestfiles - dirstatefiles
2325 2312 dsonly = dirstatefiles - manifestfiles
2326 2313 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
2327 2314 changedfiles = manifestonly | dsnotadded
2328 2315
2329 2316 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
2330 2317
2331 2318 @command('debugrebuildfncache', [], '')
2332 2319 def debugrebuildfncache(ui, repo):
2333 2320 """rebuild the fncache file"""
2334 2321 repair.rebuildfncache(ui, repo)
2335 2322
2336 2323 @command('debugrename',
2337 2324 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2338 2325 _('[-r REV] FILE'))
2339 2326 def debugrename(ui, repo, file1, *pats, **opts):
2340 2327 """dump rename information"""
2341 2328
2342 2329 ctx = scmutil.revsingle(repo, opts.get('rev'))
2343 2330 m = scmutil.match(ctx, (file1,) + pats, opts)
2344 2331 for abs in ctx.walk(m):
2345 2332 fctx = ctx[abs]
2346 2333 o = fctx.filelog().renamed(fctx.filenode())
2347 2334 rel = m.rel(abs)
2348 2335 if o:
2349 2336 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2350 2337 else:
2351 2338 ui.write(_("%s not renamed\n") % rel)
2352 2339
2353 2340 @command('debugrevlog', debugrevlogopts +
2354 2341 [('d', 'dump', False, _('dump index data'))],
2355 2342 _('-c|-m|FILE'),
2356 2343 optionalrepo=True)
2357 2344 def debugrevlog(ui, repo, file_=None, **opts):
2358 2345 """show data and statistics about a revlog"""
2359 2346 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2360 2347
2361 2348 if opts.get("dump"):
2362 2349 numrevs = len(r)
2363 2350 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
2364 2351 " rawsize totalsize compression heads chainlen\n"))
2365 2352 ts = 0
2366 2353 heads = set()
2367 2354
2368 2355 for rev in xrange(numrevs):
2369 2356 dbase = r.deltaparent(rev)
2370 2357 if dbase == -1:
2371 2358 dbase = rev
2372 2359 cbase = r.chainbase(rev)
2373 2360 clen = r.chainlen(rev)
2374 2361 p1, p2 = r.parentrevs(rev)
2375 2362 rs = r.rawsize(rev)
2376 2363 ts = ts + rs
2377 2364 heads -= set(r.parentrevs(rev))
2378 2365 heads.add(rev)
2379 2366 try:
2380 2367 compression = ts / r.end(rev)
2381 2368 except ZeroDivisionError:
2382 2369 compression = 0
2383 2370 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2384 2371 "%11d %5d %8d\n" %
2385 2372 (rev, p1, p2, r.start(rev), r.end(rev),
2386 2373 r.start(dbase), r.start(cbase),
2387 2374 r.start(p1), r.start(p2),
2388 2375 rs, ts, compression, len(heads), clen))
2389 2376 return 0
2390 2377
2391 2378 v = r.version
2392 2379 format = v & 0xFFFF
2393 2380 flags = []
2394 2381 gdelta = False
2395 2382 if v & revlog.REVLOGNGINLINEDATA:
2396 2383 flags.append('inline')
2397 2384 if v & revlog.REVLOGGENERALDELTA:
2398 2385 gdelta = True
2399 2386 flags.append('generaldelta')
2400 2387 if not flags:
2401 2388 flags = ['(none)']
2402 2389
2403 2390 nummerges = 0
2404 2391 numfull = 0
2405 2392 numprev = 0
2406 2393 nump1 = 0
2407 2394 nump2 = 0
2408 2395 numother = 0
2409 2396 nump1prev = 0
2410 2397 nump2prev = 0
2411 2398 chainlengths = []
2412 2399
2413 2400 datasize = [None, 0, 0]
2414 2401 fullsize = [None, 0, 0]
2415 2402 deltasize = [None, 0, 0]
2416 2403 chunktypecounts = {}
2417 2404 chunktypesizes = {}
2418 2405
2419 2406 def addsize(size, l):
2420 2407 if l[0] is None or size < l[0]:
2421 2408 l[0] = size
2422 2409 if size > l[1]:
2423 2410 l[1] = size
2424 2411 l[2] += size
2425 2412
2426 2413 numrevs = len(r)
2427 2414 for rev in xrange(numrevs):
2428 2415 p1, p2 = r.parentrevs(rev)
2429 2416 delta = r.deltaparent(rev)
2430 2417 if format > 0:
2431 2418 addsize(r.rawsize(rev), datasize)
2432 2419 if p2 != nullrev:
2433 2420 nummerges += 1
2434 2421 size = r.length(rev)
2435 2422 if delta == nullrev:
2436 2423 chainlengths.append(0)
2437 2424 numfull += 1
2438 2425 addsize(size, fullsize)
2439 2426 else:
2440 2427 chainlengths.append(chainlengths[delta] + 1)
2441 2428 addsize(size, deltasize)
2442 2429 if delta == rev - 1:
2443 2430 numprev += 1
2444 2431 if delta == p1:
2445 2432 nump1prev += 1
2446 2433 elif delta == p2:
2447 2434 nump2prev += 1
2448 2435 elif delta == p1:
2449 2436 nump1 += 1
2450 2437 elif delta == p2:
2451 2438 nump2 += 1
2452 2439 elif delta != nullrev:
2453 2440 numother += 1
2454 2441
2455 2442 # Obtain data on the raw chunks in the revlog.
2456 2443 chunk = r._chunkraw(rev, rev)[1]
2457 2444 if chunk:
2458 2445 chunktype = chunk[0]
2459 2446 else:
2460 2447 chunktype = 'empty'
2461 2448
2462 2449 if chunktype not in chunktypecounts:
2463 2450 chunktypecounts[chunktype] = 0
2464 2451 chunktypesizes[chunktype] = 0
2465 2452
2466 2453 chunktypecounts[chunktype] += 1
2467 2454 chunktypesizes[chunktype] += size
2468 2455
2469 2456 # Adjust size min value for empty cases
2470 2457 for size in (datasize, fullsize, deltasize):
2471 2458 if size[0] is None:
2472 2459 size[0] = 0
2473 2460
2474 2461 numdeltas = numrevs - numfull
2475 2462 numoprev = numprev - nump1prev - nump2prev
2476 2463 totalrawsize = datasize[2]
2477 2464 datasize[2] /= numrevs
2478 2465 fulltotal = fullsize[2]
2479 2466 fullsize[2] /= numfull
2480 2467 deltatotal = deltasize[2]
2481 2468 if numrevs - numfull > 0:
2482 2469 deltasize[2] /= numrevs - numfull
2483 2470 totalsize = fulltotal + deltatotal
2484 2471 avgchainlen = sum(chainlengths) / numrevs
2485 2472 maxchainlen = max(chainlengths)
2486 2473 compratio = 1
2487 2474 if totalsize:
2488 2475 compratio = totalrawsize / totalsize
2489 2476
2490 2477 basedfmtstr = '%%%dd\n'
2491 2478 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2492 2479
2493 2480 def dfmtstr(max):
2494 2481 return basedfmtstr % len(str(max))
2495 2482 def pcfmtstr(max, padding=0):
2496 2483 return basepcfmtstr % (len(str(max)), ' ' * padding)
2497 2484
2498 2485 def pcfmt(value, total):
2499 2486 if total:
2500 2487 return (value, 100 * float(value) / total)
2501 2488 else:
2502 2489 return value, 100.0
2503 2490
2504 2491 ui.write(('format : %d\n') % format)
2505 2492 ui.write(('flags : %s\n') % ', '.join(flags))
2506 2493
2507 2494 ui.write('\n')
2508 2495 fmt = pcfmtstr(totalsize)
2509 2496 fmt2 = dfmtstr(totalsize)
2510 2497 ui.write(('revisions : ') + fmt2 % numrevs)
2511 2498 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2512 2499 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2513 2500 ui.write(('revisions : ') + fmt2 % numrevs)
2514 2501 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2515 2502 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2516 2503 ui.write(('revision size : ') + fmt2 % totalsize)
2517 2504 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2518 2505 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2519 2506
2520 2507 def fmtchunktype(chunktype):
2521 2508 if chunktype == 'empty':
2522 2509 return ' %s : ' % chunktype
2523 2510 elif chunktype in string.ascii_letters:
2524 2511 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
2525 2512 else:
2526 2513 return ' 0x%s : ' % hex(chunktype)
2527 2514
2528 2515 ui.write('\n')
2529 2516 ui.write(('chunks : ') + fmt2 % numrevs)
2530 2517 for chunktype in sorted(chunktypecounts):
2531 2518 ui.write(fmtchunktype(chunktype))
2532 2519 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
2533 2520 ui.write(('chunks size : ') + fmt2 % totalsize)
2534 2521 for chunktype in sorted(chunktypecounts):
2535 2522 ui.write(fmtchunktype(chunktype))
2536 2523 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
2537 2524
2538 2525 ui.write('\n')
2539 2526 fmt = dfmtstr(max(avgchainlen, compratio))
2540 2527 ui.write(('avg chain length : ') + fmt % avgchainlen)
2541 2528 ui.write(('max chain length : ') + fmt % maxchainlen)
2542 2529 ui.write(('compression ratio : ') + fmt % compratio)
2543 2530
2544 2531 if format > 0:
2545 2532 ui.write('\n')
2546 2533 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2547 2534 % tuple(datasize))
2548 2535 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2549 2536 % tuple(fullsize))
2550 2537 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2551 2538 % tuple(deltasize))
2552 2539
2553 2540 if numdeltas > 0:
2554 2541 ui.write('\n')
2555 2542 fmt = pcfmtstr(numdeltas)
2556 2543 fmt2 = pcfmtstr(numdeltas, 4)
2557 2544 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2558 2545 if numprev > 0:
2559 2546 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2560 2547 numprev))
2561 2548 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2562 2549 numprev))
2563 2550 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2564 2551 numprev))
2565 2552 if gdelta:
2566 2553 ui.write(('deltas against p1 : ')
2567 2554 + fmt % pcfmt(nump1, numdeltas))
2568 2555 ui.write(('deltas against p2 : ')
2569 2556 + fmt % pcfmt(nump2, numdeltas))
2570 2557 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2571 2558 numdeltas))
2572 2559
2573 2560 @command('debugrevspec',
2574 2561 [('', 'optimize', None,
2575 2562 _('print parsed tree after optimizing (DEPRECATED)')),
2576 2563 ('p', 'show-stage', [],
2577 2564 _('print parsed tree at the given stage'), _('NAME')),
2578 2565 ('', 'no-optimized', False, _('evaluate tree without optimization')),
2579 2566 ('', 'verify-optimized', False, _('verify optimized result')),
2580 2567 ],
2581 2568 ('REVSPEC'))
2582 2569 def debugrevspec(ui, repo, expr, **opts):
2583 2570 """parse and apply a revision specification
2584 2571
2585 2572 Use -p/--show-stage option to print the parsed tree at the given stages.
2586 2573 Use -p all to print tree at every stage.
2587 2574
2588 2575 Use --verify-optimized to compare the optimized result with the unoptimized
2589 2576 one. Returns 1 if the optimized result differs.
2590 2577 """
2591 2578 stages = [
2592 2579 ('parsed', lambda tree: tree),
2593 2580 ('expanded', lambda tree: revset.expandaliases(ui, tree)),
2594 2581 ('concatenated', revset.foldconcat),
2595 2582 ('analyzed', revset.analyze),
2596 2583 ('optimized', revset.optimize),
2597 2584 ]
2598 2585 if opts['no_optimized']:
2599 2586 stages = stages[:-1]
2600 2587 if opts['verify_optimized'] and opts['no_optimized']:
2601 2588 raise error.Abort(_('cannot use --verify-optimized with '
2602 2589 '--no-optimized'))
2603 2590 stagenames = set(n for n, f in stages)
2604 2591
2605 2592 showalways = set()
2606 2593 showchanged = set()
2607 2594 if ui.verbose and not opts['show_stage']:
2608 2595 # show parsed tree by --verbose (deprecated)
2609 2596 showalways.add('parsed')
2610 2597 showchanged.update(['expanded', 'concatenated'])
2611 2598 if opts['optimize']:
2612 2599 showalways.add('optimized')
2613 2600 if opts['show_stage'] and opts['optimize']:
2614 2601 raise error.Abort(_('cannot use --optimize with --show-stage'))
2615 2602 if opts['show_stage'] == ['all']:
2616 2603 showalways.update(stagenames)
2617 2604 else:
2618 2605 for n in opts['show_stage']:
2619 2606 if n not in stagenames:
2620 2607 raise error.Abort(_('invalid stage name: %s') % n)
2621 2608 showalways.update(opts['show_stage'])
2622 2609
2623 2610 treebystage = {}
2624 2611 printedtree = None
2625 2612 tree = revset.parse(expr, lookup=repo.__contains__)
2626 2613 for n, f in stages:
2627 2614 treebystage[n] = tree = f(tree)
2628 2615 if n in showalways or (n in showchanged and tree != printedtree):
2629 2616 if opts['show_stage'] or n != 'parsed':
2630 2617 ui.write(("* %s:\n") % n)
2631 2618 ui.write(revset.prettyformat(tree), "\n")
2632 2619 printedtree = tree
2633 2620
2634 2621 if opts['verify_optimized']:
2635 2622 arevs = revset.makematcher(treebystage['analyzed'])(repo)
2636 2623 brevs = revset.makematcher(treebystage['optimized'])(repo)
2637 2624 if ui.verbose:
2638 2625 ui.note(("* analyzed set:\n"), smartset.prettyformat(arevs), "\n")
2639 2626 ui.note(("* optimized set:\n"), smartset.prettyformat(brevs), "\n")
2640 2627 arevs = list(arevs)
2641 2628 brevs = list(brevs)
2642 2629 if arevs == brevs:
2643 2630 return 0
2644 2631 ui.write(('--- analyzed\n'), label='diff.file_a')
2645 2632 ui.write(('+++ optimized\n'), label='diff.file_b')
2646 2633 sm = difflib.SequenceMatcher(None, arevs, brevs)
2647 2634 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2648 2635 if tag in ('delete', 'replace'):
2649 2636 for c in arevs[alo:ahi]:
2650 2637 ui.write('-%s\n' % c, label='diff.deleted')
2651 2638 if tag in ('insert', 'replace'):
2652 2639 for c in brevs[blo:bhi]:
2653 2640 ui.write('+%s\n' % c, label='diff.inserted')
2654 2641 if tag == 'equal':
2655 2642 for c in arevs[alo:ahi]:
2656 2643 ui.write(' %s\n' % c)
2657 2644 return 1
2658 2645
2659 2646 func = revset.makematcher(tree)
2660 2647 revs = func(repo)
2661 2648 if ui.verbose:
2662 2649 ui.note(("* set:\n"), smartset.prettyformat(revs), "\n")
2663 2650 for c in revs:
2664 2651 ui.write("%s\n" % c)
2665 2652
2666 2653 @command('debugsetparents', [], _('REV1 [REV2]'))
2667 2654 def debugsetparents(ui, repo, rev1, rev2=None):
2668 2655 """manually set the parents of the current working directory
2669 2656
2670 2657 This is useful for writing repository conversion tools, but should
2671 2658 be used with care. For example, neither the working directory nor the
2672 2659 dirstate is updated, so file status may be incorrect after running this
2673 2660 command.
2674 2661
2675 2662 Returns 0 on success.
2676 2663 """
2677 2664
2678 2665 r1 = scmutil.revsingle(repo, rev1).node()
2679 2666 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2680 2667
2681 2668 with repo.wlock():
2682 2669 repo.setparents(r1, r2)
2683 2670
2684 2671 @command('debugdirstate|debugstate',
2685 2672 [('', 'nodates', None, _('do not display the saved mtime')),
2686 2673 ('', 'datesort', None, _('sort by saved mtime'))],
2687 2674 _('[OPTION]...'))
2688 2675 def debugstate(ui, repo, **opts):
2689 2676 """show the contents of the current dirstate"""
2690 2677
2691 2678 nodates = opts.get('nodates')
2692 2679 datesort = opts.get('datesort')
2693 2680
2694 2681 timestr = ""
2695 2682 if datesort:
2696 2683 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2697 2684 else:
2698 2685 keyfunc = None # sort by filename
2699 2686 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2700 2687 if ent[3] == -1:
2701 2688 timestr = 'unset '
2702 2689 elif nodates:
2703 2690 timestr = 'set '
2704 2691 else:
2705 2692 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2706 2693 time.localtime(ent[3]))
2707 2694 if ent[1] & 0o20000:
2708 2695 mode = 'lnk'
2709 2696 else:
2710 2697 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
2711 2698 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2712 2699 for f in repo.dirstate.copies():
2713 2700 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2714 2701
2715 2702 @command('debugsub',
2716 2703 [('r', 'rev', '',
2717 2704 _('revision to check'), _('REV'))],
2718 2705 _('[-r REV] [REV]'))
2719 2706 def debugsub(ui, repo, rev=None):
2720 2707 ctx = scmutil.revsingle(repo, rev, None)
2721 2708 for k, v in sorted(ctx.substate.items()):
2722 2709 ui.write(('path %s\n') % k)
2723 2710 ui.write((' source %s\n') % v[0])
2724 2711 ui.write((' revision %s\n') % v[1])
2725 2712
2726 2713 @command('debugsuccessorssets',
2727 2714 [],
2728 2715 _('[REV]'))
2729 2716 def debugsuccessorssets(ui, repo, *revs):
2730 2717 """show set of successors for revision
2731 2718
2732 2719 A successors set of changeset A is a consistent group of revisions that
2733 2720 succeed A. It contains non-obsolete changesets only.
2734 2721
2735 2722 In most cases a changeset A has a single successors set containing a single
2736 2723 successor (changeset A replaced by A').
2737 2724
2738 2725 A changeset that is made obsolete with no successors are called "pruned".
2739 2726 Such changesets have no successors sets at all.
2740 2727
2741 2728 A changeset that has been "split" will have a successors set containing
2742 2729 more than one successor.
2743 2730
2744 2731 A changeset that has been rewritten in multiple different ways is called
2745 2732 "divergent". Such changesets have multiple successor sets (each of which
2746 2733 may also be split, i.e. have multiple successors).
2747 2734
2748 2735 Results are displayed as follows::
2749 2736
2750 2737 <rev1>
2751 2738 <successors-1A>
2752 2739 <rev2>
2753 2740 <successors-2A>
2754 2741 <successors-2B1> <successors-2B2> <successors-2B3>
2755 2742
2756 2743 Here rev2 has two possible (i.e. divergent) successors sets. The first
2757 2744 holds one element, whereas the second holds three (i.e. the changeset has
2758 2745 been split).
2759 2746 """
2760 2747 # passed to successorssets caching computation from one call to another
2761 2748 cache = {}
2762 2749 ctx2str = str
2763 2750 node2str = short
2764 2751 if ui.debug():
2765 2752 def ctx2str(ctx):
2766 2753 return ctx.hex()
2767 2754 node2str = hex
2768 2755 for rev in scmutil.revrange(repo, revs):
2769 2756 ctx = repo[rev]
2770 2757 ui.write('%s\n'% ctx2str(ctx))
2771 2758 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
2772 2759 if succsset:
2773 2760 ui.write(' ')
2774 2761 ui.write(node2str(succsset[0]))
2775 2762 for node in succsset[1:]:
2776 2763 ui.write(' ')
2777 2764 ui.write(node2str(node))
2778 2765 ui.write('\n')
2779 2766
2780 2767 @command('debugtemplate',
2781 2768 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
2782 2769 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
2783 2770 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
2784 2771 optionalrepo=True)
2785 2772 def debugtemplate(ui, repo, tmpl, **opts):
2786 2773 """parse and apply a template
2787 2774
2788 2775 If -r/--rev is given, the template is processed as a log template and
2789 2776 applied to the given changesets. Otherwise, it is processed as a generic
2790 2777 template.
2791 2778
2792 2779 Use --verbose to print the parsed tree.
2793 2780 """
2794 2781 revs = None
2795 2782 if opts['rev']:
2796 2783 if repo is None:
2797 2784 raise error.RepoError(_('there is no Mercurial repository here '
2798 2785 '(.hg not found)'))
2799 2786 revs = scmutil.revrange(repo, opts['rev'])
2800 2787
2801 2788 props = {}
2802 2789 for d in opts['define']:
2803 2790 try:
2804 2791 k, v = (e.strip() for e in d.split('=', 1))
2805 2792 if not k:
2806 2793 raise ValueError
2807 2794 props[k] = v
2808 2795 except ValueError:
2809 2796 raise error.Abort(_('malformed keyword definition: %s') % d)
2810 2797
2811 2798 if ui.verbose:
2812 2799 aliases = ui.configitems('templatealias')
2813 2800 tree = templater.parse(tmpl)
2814 2801 ui.note(templater.prettyformat(tree), '\n')
2815 2802 newtree = templater.expandaliases(tree, aliases)
2816 2803 if newtree != tree:
2817 2804 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
2818 2805
2819 2806 mapfile = None
2820 2807 if revs is None:
2821 2808 k = 'debugtemplate'
2822 2809 t = formatter.maketemplater(ui, k, tmpl)
2823 2810 ui.write(templater.stringify(t(k, **props)))
2824 2811 else:
2825 2812 displayer = cmdutil.changeset_templater(ui, repo, None, opts, tmpl,
2826 2813 mapfile, buffered=False)
2827 2814 for r in revs:
2828 2815 displayer.show(repo[r], **props)
2829 2816 displayer.close()
2830 2817
2831 2818 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
2832 2819 def debugwalk(ui, repo, *pats, **opts):
2833 2820 """show how files match on given patterns"""
2834 2821 m = scmutil.match(repo[None], pats, opts)
2835 2822 items = list(repo.walk(m))
2836 2823 if not items:
2837 2824 return
2838 2825 f = lambda fn: fn
2839 2826 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
2840 2827 f = lambda fn: util.normpath(fn)
2841 2828 fmt = 'f %%-%ds %%-%ds %%s' % (
2842 2829 max([len(abs) for abs in items]),
2843 2830 max([len(m.rel(abs)) for abs in items]))
2844 2831 for abs in items:
2845 2832 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2846 2833 ui.write("%s\n" % line.rstrip())
2847 2834
2848 2835 @command('debugwireargs',
2849 2836 [('', 'three', '', 'three'),
2850 2837 ('', 'four', '', 'four'),
2851 2838 ('', 'five', '', 'five'),
2852 2839 ] + remoteopts,
2853 2840 _('REPO [OPTIONS]... [ONE [TWO]]'),
2854 2841 norepo=True)
2855 2842 def debugwireargs(ui, repopath, *vals, **opts):
2856 2843 repo = hg.peer(ui, opts, repopath)
2857 2844 for opt in remoteopts:
2858 2845 del opts[opt[1]]
2859 2846 args = {}
2860 2847 for k, v in opts.iteritems():
2861 2848 if v:
2862 2849 args[k] = v
2863 2850 # run twice to check that we don't mess up the stream for the next command
2864 2851 res1 = repo.debugwireargs(*vals, **args)
2865 2852 res2 = repo.debugwireargs(*vals, **args)
2866 2853 ui.write("%s\n" % res1)
2867 2854 if res1 != res2:
2868 2855 ui.warn("%s\n" % res2)
2869 2856
2870 2857 @command('^diff',
2871 2858 [('r', 'rev', [], _('revision'), _('REV')),
2872 2859 ('c', 'change', '', _('change made by revision'), _('REV'))
2873 2860 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2874 2861 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2875 2862 inferrepo=True)
2876 2863 def diff(ui, repo, *pats, **opts):
2877 2864 """diff repository (or selected files)
2878 2865
2879 2866 Show differences between revisions for the specified files.
2880 2867
2881 2868 Differences between files are shown using the unified diff format.
2882 2869
2883 2870 .. note::
2884 2871
2885 2872 :hg:`diff` may generate unexpected results for merges, as it will
2886 2873 default to comparing against the working directory's first
2887 2874 parent changeset if no revisions are specified.
2888 2875
2889 2876 When two revision arguments are given, then changes are shown
2890 2877 between those revisions. If only one revision is specified then
2891 2878 that revision is compared to the working directory, and, when no
2892 2879 revisions are specified, the working directory files are compared
2893 2880 to its first parent.
2894 2881
2895 2882 Alternatively you can specify -c/--change with a revision to see
2896 2883 the changes in that changeset relative to its first parent.
2897 2884
2898 2885 Without the -a/--text option, diff will avoid generating diffs of
2899 2886 files it detects as binary. With -a, diff will generate a diff
2900 2887 anyway, probably with undesirable results.
2901 2888
2902 2889 Use the -g/--git option to generate diffs in the git extended diff
2903 2890 format. For more information, read :hg:`help diffs`.
2904 2891
2905 2892 .. container:: verbose
2906 2893
2907 2894 Examples:
2908 2895
2909 2896 - compare a file in the current working directory to its parent::
2910 2897
2911 2898 hg diff foo.c
2912 2899
2913 2900 - compare two historical versions of a directory, with rename info::
2914 2901
2915 2902 hg diff --git -r 1.0:1.2 lib/
2916 2903
2917 2904 - get change stats relative to the last change on some date::
2918 2905
2919 2906 hg diff --stat -r "date('may 2')"
2920 2907
2921 2908 - diff all newly-added files that contain a keyword::
2922 2909
2923 2910 hg diff "set:added() and grep(GNU)"
2924 2911
2925 2912 - compare a revision and its parents::
2926 2913
2927 2914 hg diff -c 9353 # compare against first parent
2928 2915 hg diff -r 9353^:9353 # same using revset syntax
2929 2916 hg diff -r 9353^2:9353 # compare against the second parent
2930 2917
2931 2918 Returns 0 on success.
2932 2919 """
2933 2920
2934 2921 revs = opts.get('rev')
2935 2922 change = opts.get('change')
2936 2923 stat = opts.get('stat')
2937 2924 reverse = opts.get('reverse')
2938 2925
2939 2926 if revs and change:
2940 2927 msg = _('cannot specify --rev and --change at the same time')
2941 2928 raise error.Abort(msg)
2942 2929 elif change:
2943 2930 node2 = scmutil.revsingle(repo, change, None).node()
2944 2931 node1 = repo[node2].p1().node()
2945 2932 else:
2946 2933 node1, node2 = scmutil.revpair(repo, revs)
2947 2934
2948 2935 if reverse:
2949 2936 node1, node2 = node2, node1
2950 2937
2951 2938 diffopts = patch.diffallopts(ui, opts)
2952 2939 m = scmutil.match(repo[node2], pats, opts)
2953 2940 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2954 2941 listsubrepos=opts.get('subrepos'),
2955 2942 root=opts.get('root'))
2956 2943
2957 2944 @command('^export',
2958 2945 [('o', 'output', '',
2959 2946 _('print output to file with formatted name'), _('FORMAT')),
2960 2947 ('', 'switch-parent', None, _('diff against the second parent')),
2961 2948 ('r', 'rev', [], _('revisions to export'), _('REV')),
2962 2949 ] + diffopts,
2963 2950 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
2964 2951 def export(ui, repo, *changesets, **opts):
2965 2952 """dump the header and diffs for one or more changesets
2966 2953
2967 2954 Print the changeset header and diffs for one or more revisions.
2968 2955 If no revision is given, the parent of the working directory is used.
2969 2956
2970 2957 The information shown in the changeset header is: author, date,
2971 2958 branch name (if non-default), changeset hash, parent(s) and commit
2972 2959 comment.
2973 2960
2974 2961 .. note::
2975 2962
2976 2963 :hg:`export` may generate unexpected diff output for merge
2977 2964 changesets, as it will compare the merge changeset against its
2978 2965 first parent only.
2979 2966
2980 2967 Output may be to a file, in which case the name of the file is
2981 2968 given using a format string. The formatting rules are as follows:
2982 2969
2983 2970 :``%%``: literal "%" character
2984 2971 :``%H``: changeset hash (40 hexadecimal digits)
2985 2972 :``%N``: number of patches being generated
2986 2973 :``%R``: changeset revision number
2987 2974 :``%b``: basename of the exporting repository
2988 2975 :``%h``: short-form changeset hash (12 hexadecimal digits)
2989 2976 :``%m``: first line of the commit message (only alphanumeric characters)
2990 2977 :``%n``: zero-padded sequence number, starting at 1
2991 2978 :``%r``: zero-padded changeset revision number
2992 2979
2993 2980 Without the -a/--text option, export will avoid generating diffs
2994 2981 of files it detects as binary. With -a, export will generate a
2995 2982 diff anyway, probably with undesirable results.
2996 2983
2997 2984 Use the -g/--git option to generate diffs in the git extended diff
2998 2985 format. See :hg:`help diffs` for more information.
2999 2986
3000 2987 With the --switch-parent option, the diff will be against the
3001 2988 second parent. It can be useful to review a merge.
3002 2989
3003 2990 .. container:: verbose
3004 2991
3005 2992 Examples:
3006 2993
3007 2994 - use export and import to transplant a bugfix to the current
3008 2995 branch::
3009 2996
3010 2997 hg export -r 9353 | hg import -
3011 2998
3012 2999 - export all the changesets between two revisions to a file with
3013 3000 rename information::
3014 3001
3015 3002 hg export --git -r 123:150 > changes.txt
3016 3003
3017 3004 - split outgoing changes into a series of patches with
3018 3005 descriptive names::
3019 3006
3020 3007 hg export -r "outgoing()" -o "%n-%m.patch"
3021 3008
3022 3009 Returns 0 on success.
3023 3010 """
3024 3011 changesets += tuple(opts.get('rev', []))
3025 3012 if not changesets:
3026 3013 changesets = ['.']
3027 3014 revs = scmutil.revrange(repo, changesets)
3028 3015 if not revs:
3029 3016 raise error.Abort(_("export requires at least one changeset"))
3030 3017 if len(revs) > 1:
3031 3018 ui.note(_('exporting patches:\n'))
3032 3019 else:
3033 3020 ui.note(_('exporting patch:\n'))
3034 3021 cmdutil.export(repo, revs, template=opts.get('output'),
3035 3022 switch_parent=opts.get('switch_parent'),
3036 3023 opts=patch.diffallopts(ui, opts))
3037 3024
3038 3025 @command('files',
3039 3026 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3040 3027 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3041 3028 ] + walkopts + formatteropts + subrepoopts,
3042 3029 _('[OPTION]... [FILE]...'))
3043 3030 def files(ui, repo, *pats, **opts):
3044 3031 """list tracked files
3045 3032
3046 3033 Print files under Mercurial control in the working directory or
3047 3034 specified revision for given files (excluding removed files).
3048 3035 Files can be specified as filenames or filesets.
3049 3036
3050 3037 If no files are given to match, this command prints the names
3051 3038 of all files under Mercurial control.
3052 3039
3053 3040 .. container:: verbose
3054 3041
3055 3042 Examples:
3056 3043
3057 3044 - list all files under the current directory::
3058 3045
3059 3046 hg files .
3060 3047
3061 3048 - shows sizes and flags for current revision::
3062 3049
3063 3050 hg files -vr .
3064 3051
3065 3052 - list all files named README::
3066 3053
3067 3054 hg files -I "**/README"
3068 3055
3069 3056 - list all binary files::
3070 3057
3071 3058 hg files "set:binary()"
3072 3059
3073 3060 - find files containing a regular expression::
3074 3061
3075 3062 hg files "set:grep('bob')"
3076 3063
3077 3064 - search tracked file contents with xargs and grep::
3078 3065
3079 3066 hg files -0 | xargs -0 grep foo
3080 3067
3081 3068 See :hg:`help patterns` and :hg:`help filesets` for more information
3082 3069 on specifying file patterns.
3083 3070
3084 3071 Returns 0 if a match is found, 1 otherwise.
3085 3072
3086 3073 """
3087 3074 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3088 3075
3089 3076 end = '\n'
3090 3077 if opts.get('print0'):
3091 3078 end = '\0'
3092 3079 fmt = '%s' + end
3093 3080
3094 3081 m = scmutil.match(ctx, pats, opts)
3095 3082 with ui.formatter('files', opts) as fm:
3096 3083 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
3097 3084
3098 3085 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3099 3086 def forget(ui, repo, *pats, **opts):
3100 3087 """forget the specified files on the next commit
3101 3088
3102 3089 Mark the specified files so they will no longer be tracked
3103 3090 after the next commit.
3104 3091
3105 3092 This only removes files from the current branch, not from the
3106 3093 entire project history, and it does not delete them from the
3107 3094 working directory.
3108 3095
3109 3096 To delete the file from the working directory, see :hg:`remove`.
3110 3097
3111 3098 To undo a forget before the next commit, see :hg:`add`.
3112 3099
3113 3100 .. container:: verbose
3114 3101
3115 3102 Examples:
3116 3103
3117 3104 - forget newly-added binary files::
3118 3105
3119 3106 hg forget "set:added() and binary()"
3120 3107
3121 3108 - forget files that would be excluded by .hgignore::
3122 3109
3123 3110 hg forget "set:hgignore()"
3124 3111
3125 3112 Returns 0 on success.
3126 3113 """
3127 3114
3128 3115 if not pats:
3129 3116 raise error.Abort(_('no files specified'))
3130 3117
3131 3118 m = scmutil.match(repo[None], pats, opts)
3132 3119 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3133 3120 return rejected and 1 or 0
3134 3121
3135 3122 @command(
3136 3123 'graft',
3137 3124 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3138 3125 ('c', 'continue', False, _('resume interrupted graft')),
3139 3126 ('e', 'edit', False, _('invoke editor on commit messages')),
3140 3127 ('', 'log', None, _('append graft info to log message')),
3141 3128 ('f', 'force', False, _('force graft')),
3142 3129 ('D', 'currentdate', False,
3143 3130 _('record the current date as commit date')),
3144 3131 ('U', 'currentuser', False,
3145 3132 _('record the current user as committer'), _('DATE'))]
3146 3133 + commitopts2 + mergetoolopts + dryrunopts,
3147 3134 _('[OPTION]... [-r REV]... REV...'))
3148 3135 def graft(ui, repo, *revs, **opts):
3149 3136 '''copy changes from other branches onto the current branch
3150 3137
3151 3138 This command uses Mercurial's merge logic to copy individual
3152 3139 changes from other branches without merging branches in the
3153 3140 history graph. This is sometimes known as 'backporting' or
3154 3141 'cherry-picking'. By default, graft will copy user, date, and
3155 3142 description from the source changesets.
3156 3143
3157 3144 Changesets that are ancestors of the current revision, that have
3158 3145 already been grafted, or that are merges will be skipped.
3159 3146
3160 3147 If --log is specified, log messages will have a comment appended
3161 3148 of the form::
3162 3149
3163 3150 (grafted from CHANGESETHASH)
3164 3151
3165 3152 If --force is specified, revisions will be grafted even if they
3166 3153 are already ancestors of or have been grafted to the destination.
3167 3154 This is useful when the revisions have since been backed out.
3168 3155
3169 3156 If a graft merge results in conflicts, the graft process is
3170 3157 interrupted so that the current merge can be manually resolved.
3171 3158 Once all conflicts are addressed, the graft process can be
3172 3159 continued with the -c/--continue option.
3173 3160
3174 3161 .. note::
3175 3162
3176 3163 The -c/--continue option does not reapply earlier options, except
3177 3164 for --force.
3178 3165
3179 3166 .. container:: verbose
3180 3167
3181 3168 Examples:
3182 3169
3183 3170 - copy a single change to the stable branch and edit its description::
3184 3171
3185 3172 hg update stable
3186 3173 hg graft --edit 9393
3187 3174
3188 3175 - graft a range of changesets with one exception, updating dates::
3189 3176
3190 3177 hg graft -D "2085::2093 and not 2091"
3191 3178
3192 3179 - continue a graft after resolving conflicts::
3193 3180
3194 3181 hg graft -c
3195 3182
3196 3183 - show the source of a grafted changeset::
3197 3184
3198 3185 hg log --debug -r .
3199 3186
3200 3187 - show revisions sorted by date::
3201 3188
3202 3189 hg log -r "sort(all(), date)"
3203 3190
3204 3191 See :hg:`help revisions` for more about specifying revisions.
3205 3192
3206 3193 Returns 0 on successful completion.
3207 3194 '''
3208 3195 with repo.wlock():
3209 3196 return _dograft(ui, repo, *revs, **opts)
3210 3197
3211 3198 def _dograft(ui, repo, *revs, **opts):
3212 3199 if revs and opts.get('rev'):
3213 3200 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
3214 3201 'revision ordering!\n'))
3215 3202
3216 3203 revs = list(revs)
3217 3204 revs.extend(opts.get('rev'))
3218 3205
3219 3206 if not opts.get('user') and opts.get('currentuser'):
3220 3207 opts['user'] = ui.username()
3221 3208 if not opts.get('date') and opts.get('currentdate'):
3222 3209 opts['date'] = "%d %d" % util.makedate()
3223 3210
3224 3211 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3225 3212
3226 3213 cont = False
3227 3214 if opts.get('continue'):
3228 3215 cont = True
3229 3216 if revs:
3230 3217 raise error.Abort(_("can't specify --continue and revisions"))
3231 3218 # read in unfinished revisions
3232 3219 try:
3233 3220 nodes = repo.vfs.read('graftstate').splitlines()
3234 3221 revs = [repo[node].rev() for node in nodes]
3235 3222 except IOError as inst:
3236 3223 if inst.errno != errno.ENOENT:
3237 3224 raise
3238 3225 cmdutil.wrongtooltocontinue(repo, _('graft'))
3239 3226 else:
3240 3227 cmdutil.checkunfinished(repo)
3241 3228 cmdutil.bailifchanged(repo)
3242 3229 if not revs:
3243 3230 raise error.Abort(_('no revisions specified'))
3244 3231 revs = scmutil.revrange(repo, revs)
3245 3232
3246 3233 skipped = set()
3247 3234 # check for merges
3248 3235 for rev in repo.revs('%ld and merge()', revs):
3249 3236 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3250 3237 skipped.add(rev)
3251 3238 revs = [r for r in revs if r not in skipped]
3252 3239 if not revs:
3253 3240 return -1
3254 3241
3255 3242 # Don't check in the --continue case, in effect retaining --force across
3256 3243 # --continues. That's because without --force, any revisions we decided to
3257 3244 # skip would have been filtered out here, so they wouldn't have made their
3258 3245 # way to the graftstate. With --force, any revisions we would have otherwise
3259 3246 # skipped would not have been filtered out, and if they hadn't been applied
3260 3247 # already, they'd have been in the graftstate.
3261 3248 if not (cont or opts.get('force')):
3262 3249 # check for ancestors of dest branch
3263 3250 crev = repo['.'].rev()
3264 3251 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3265 3252 # XXX make this lazy in the future
3266 3253 # don't mutate while iterating, create a copy
3267 3254 for rev in list(revs):
3268 3255 if rev in ancestors:
3269 3256 ui.warn(_('skipping ancestor revision %d:%s\n') %
3270 3257 (rev, repo[rev]))
3271 3258 # XXX remove on list is slow
3272 3259 revs.remove(rev)
3273 3260 if not revs:
3274 3261 return -1
3275 3262
3276 3263 # analyze revs for earlier grafts
3277 3264 ids = {}
3278 3265 for ctx in repo.set("%ld", revs):
3279 3266 ids[ctx.hex()] = ctx.rev()
3280 3267 n = ctx.extra().get('source')
3281 3268 if n:
3282 3269 ids[n] = ctx.rev()
3283 3270
3284 3271 # check ancestors for earlier grafts
3285 3272 ui.debug('scanning for duplicate grafts\n')
3286 3273
3287 3274 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3288 3275 ctx = repo[rev]
3289 3276 n = ctx.extra().get('source')
3290 3277 if n in ids:
3291 3278 try:
3292 3279 r = repo[n].rev()
3293 3280 except error.RepoLookupError:
3294 3281 r = None
3295 3282 if r in revs:
3296 3283 ui.warn(_('skipping revision %d:%s '
3297 3284 '(already grafted to %d:%s)\n')
3298 3285 % (r, repo[r], rev, ctx))
3299 3286 revs.remove(r)
3300 3287 elif ids[n] in revs:
3301 3288 if r is None:
3302 3289 ui.warn(_('skipping already grafted revision %d:%s '
3303 3290 '(%d:%s also has unknown origin %s)\n')
3304 3291 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3305 3292 else:
3306 3293 ui.warn(_('skipping already grafted revision %d:%s '
3307 3294 '(%d:%s also has origin %d:%s)\n')
3308 3295 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3309 3296 revs.remove(ids[n])
3310 3297 elif ctx.hex() in ids:
3311 3298 r = ids[ctx.hex()]
3312 3299 ui.warn(_('skipping already grafted revision %d:%s '
3313 3300 '(was grafted from %d:%s)\n') %
3314 3301 (r, repo[r], rev, ctx))
3315 3302 revs.remove(r)
3316 3303 if not revs:
3317 3304 return -1
3318 3305
3319 3306 for pos, ctx in enumerate(repo.set("%ld", revs)):
3320 3307 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3321 3308 ctx.description().split('\n', 1)[0])
3322 3309 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3323 3310 if names:
3324 3311 desc += ' (%s)' % ' '.join(names)
3325 3312 ui.status(_('grafting %s\n') % desc)
3326 3313 if opts.get('dry_run'):
3327 3314 continue
3328 3315
3329 3316 source = ctx.extra().get('source')
3330 3317 extra = {}
3331 3318 if source:
3332 3319 extra['source'] = source
3333 3320 extra['intermediate-source'] = ctx.hex()
3334 3321 else:
3335 3322 extra['source'] = ctx.hex()
3336 3323 user = ctx.user()
3337 3324 if opts.get('user'):
3338 3325 user = opts['user']
3339 3326 date = ctx.date()
3340 3327 if opts.get('date'):
3341 3328 date = opts['date']
3342 3329 message = ctx.description()
3343 3330 if opts.get('log'):
3344 3331 message += '\n(grafted from %s)' % ctx.hex()
3345 3332
3346 3333 # we don't merge the first commit when continuing
3347 3334 if not cont:
3348 3335 # perform the graft merge with p1(rev) as 'ancestor'
3349 3336 try:
3350 3337 # ui.forcemerge is an internal variable, do not document
3351 3338 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3352 3339 'graft')
3353 3340 stats = mergemod.graft(repo, ctx, ctx.p1(),
3354 3341 ['local', 'graft'])
3355 3342 finally:
3356 3343 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3357 3344 # report any conflicts
3358 3345 if stats and stats[3] > 0:
3359 3346 # write out state for --continue
3360 3347 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3361 3348 repo.vfs.write('graftstate', ''.join(nodelines))
3362 3349 extra = ''
3363 3350 if opts.get('user'):
3364 3351 extra += ' --user %s' % util.shellquote(opts['user'])
3365 3352 if opts.get('date'):
3366 3353 extra += ' --date %s' % util.shellquote(opts['date'])
3367 3354 if opts.get('log'):
3368 3355 extra += ' --log'
3369 3356 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
3370 3357 raise error.Abort(
3371 3358 _("unresolved conflicts, can't continue"),
3372 3359 hint=hint)
3373 3360 else:
3374 3361 cont = False
3375 3362
3376 3363 # commit
3377 3364 node = repo.commit(text=message, user=user,
3378 3365 date=date, extra=extra, editor=editor)
3379 3366 if node is None:
3380 3367 ui.warn(
3381 3368 _('note: graft of %d:%s created no changes to commit\n') %
3382 3369 (ctx.rev(), ctx))
3383 3370
3384 3371 # remove state when we complete successfully
3385 3372 if not opts.get('dry_run'):
3386 3373 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3387 3374
3388 3375 return 0
3389 3376
3390 3377 @command('grep',
3391 3378 [('0', 'print0', None, _('end fields with NUL')),
3392 3379 ('', 'all', None, _('print all revisions that match')),
3393 3380 ('a', 'text', None, _('treat all files as text')),
3394 3381 ('f', 'follow', None,
3395 3382 _('follow changeset history,'
3396 3383 ' or file history across copies and renames')),
3397 3384 ('i', 'ignore-case', None, _('ignore case when matching')),
3398 3385 ('l', 'files-with-matches', None,
3399 3386 _('print only filenames and revisions that match')),
3400 3387 ('n', 'line-number', None, _('print matching line numbers')),
3401 3388 ('r', 'rev', [],
3402 3389 _('only search files changed within revision range'), _('REV')),
3403 3390 ('u', 'user', None, _('list the author (long with -v)')),
3404 3391 ('d', 'date', None, _('list the date (short with -q)')),
3405 3392 ] + formatteropts + walkopts,
3406 3393 _('[OPTION]... PATTERN [FILE]...'),
3407 3394 inferrepo=True)
3408 3395 def grep(ui, repo, pattern, *pats, **opts):
3409 3396 """search revision history for a pattern in specified files
3410 3397
3411 3398 Search revision history for a regular expression in the specified
3412 3399 files or the entire project.
3413 3400
3414 3401 By default, grep prints the most recent revision number for each
3415 3402 file in which it finds a match. To get it to print every revision
3416 3403 that contains a change in match status ("-" for a match that becomes
3417 3404 a non-match, or "+" for a non-match that becomes a match), use the
3418 3405 --all flag.
3419 3406
3420 3407 PATTERN can be any Python (roughly Perl-compatible) regular
3421 3408 expression.
3422 3409
3423 3410 If no FILEs are specified (and -f/--follow isn't set), all files in
3424 3411 the repository are searched, including those that don't exist in the
3425 3412 current branch or have been deleted in a prior changeset.
3426 3413
3427 3414 Returns 0 if a match is found, 1 otherwise.
3428 3415 """
3429 3416 reflags = re.M
3430 3417 if opts.get('ignore_case'):
3431 3418 reflags |= re.I
3432 3419 try:
3433 3420 regexp = util.re.compile(pattern, reflags)
3434 3421 except re.error as inst:
3435 3422 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3436 3423 return 1
3437 3424 sep, eol = ':', '\n'
3438 3425 if opts.get('print0'):
3439 3426 sep = eol = '\0'
3440 3427
3441 3428 getfile = util.lrucachefunc(repo.file)
3442 3429
3443 3430 def matchlines(body):
3444 3431 begin = 0
3445 3432 linenum = 0
3446 3433 while begin < len(body):
3447 3434 match = regexp.search(body, begin)
3448 3435 if not match:
3449 3436 break
3450 3437 mstart, mend = match.span()
3451 3438 linenum += body.count('\n', begin, mstart) + 1
3452 3439 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3453 3440 begin = body.find('\n', mend) + 1 or len(body) + 1
3454 3441 lend = begin - 1
3455 3442 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3456 3443
3457 3444 class linestate(object):
3458 3445 def __init__(self, line, linenum, colstart, colend):
3459 3446 self.line = line
3460 3447 self.linenum = linenum
3461 3448 self.colstart = colstart
3462 3449 self.colend = colend
3463 3450
3464 3451 def __hash__(self):
3465 3452 return hash((self.linenum, self.line))
3466 3453
3467 3454 def __eq__(self, other):
3468 3455 return self.line == other.line
3469 3456
3470 3457 def findpos(self):
3471 3458 """Iterate all (start, end) indices of matches"""
3472 3459 yield self.colstart, self.colend
3473 3460 p = self.colend
3474 3461 while p < len(self.line):
3475 3462 m = regexp.search(self.line, p)
3476 3463 if not m:
3477 3464 break
3478 3465 yield m.span()
3479 3466 p = m.end()
3480 3467
3481 3468 matches = {}
3482 3469 copies = {}
3483 3470 def grepbody(fn, rev, body):
3484 3471 matches[rev].setdefault(fn, [])
3485 3472 m = matches[rev][fn]
3486 3473 for lnum, cstart, cend, line in matchlines(body):
3487 3474 s = linestate(line, lnum, cstart, cend)
3488 3475 m.append(s)
3489 3476
3490 3477 def difflinestates(a, b):
3491 3478 sm = difflib.SequenceMatcher(None, a, b)
3492 3479 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3493 3480 if tag == 'insert':
3494 3481 for i in xrange(blo, bhi):
3495 3482 yield ('+', b[i])
3496 3483 elif tag == 'delete':
3497 3484 for i in xrange(alo, ahi):
3498 3485 yield ('-', a[i])
3499 3486 elif tag == 'replace':
3500 3487 for i in xrange(alo, ahi):
3501 3488 yield ('-', a[i])
3502 3489 for i in xrange(blo, bhi):
3503 3490 yield ('+', b[i])
3504 3491
3505 3492 def display(fm, fn, ctx, pstates, states):
3506 3493 rev = ctx.rev()
3507 3494 if fm.isplain():
3508 3495 formatuser = ui.shortuser
3509 3496 else:
3510 3497 formatuser = str
3511 3498 if ui.quiet:
3512 3499 datefmt = '%Y-%m-%d'
3513 3500 else:
3514 3501 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
3515 3502 found = False
3516 3503 @util.cachefunc
3517 3504 def binary():
3518 3505 flog = getfile(fn)
3519 3506 return util.binary(flog.read(ctx.filenode(fn)))
3520 3507
3521 3508 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
3522 3509 if opts.get('all'):
3523 3510 iter = difflinestates(pstates, states)
3524 3511 else:
3525 3512 iter = [('', l) for l in states]
3526 3513 for change, l in iter:
3527 3514 fm.startitem()
3528 3515 fm.data(node=fm.hexfunc(ctx.node()))
3529 3516 cols = [
3530 3517 ('filename', fn, True),
3531 3518 ('rev', rev, True),
3532 3519 ('linenumber', l.linenum, opts.get('line_number')),
3533 3520 ]
3534 3521 if opts.get('all'):
3535 3522 cols.append(('change', change, True))
3536 3523 cols.extend([
3537 3524 ('user', formatuser(ctx.user()), opts.get('user')),
3538 3525 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
3539 3526 ])
3540 3527 lastcol = next(name for name, data, cond in reversed(cols) if cond)
3541 3528 for name, data, cond in cols:
3542 3529 field = fieldnamemap.get(name, name)
3543 3530 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
3544 3531 if cond and name != lastcol:
3545 3532 fm.plain(sep, label='grep.sep')
3546 3533 if not opts.get('files_with_matches'):
3547 3534 fm.plain(sep, label='grep.sep')
3548 3535 if not opts.get('text') and binary():
3549 3536 fm.plain(_(" Binary file matches"))
3550 3537 else:
3551 3538 displaymatches(fm.nested('texts'), l)
3552 3539 fm.plain(eol)
3553 3540 found = True
3554 3541 if opts.get('files_with_matches'):
3555 3542 break
3556 3543 return found
3557 3544
3558 3545 def displaymatches(fm, l):
3559 3546 p = 0
3560 3547 for s, e in l.findpos():
3561 3548 if p < s:
3562 3549 fm.startitem()
3563 3550 fm.write('text', '%s', l.line[p:s])
3564 3551 fm.data(matched=False)
3565 3552 fm.startitem()
3566 3553 fm.write('text', '%s', l.line[s:e], label='grep.match')
3567 3554 fm.data(matched=True)
3568 3555 p = e
3569 3556 if p < len(l.line):
3570 3557 fm.startitem()
3571 3558 fm.write('text', '%s', l.line[p:])
3572 3559 fm.data(matched=False)
3573 3560 fm.end()
3574 3561
3575 3562 skip = {}
3576 3563 revfiles = {}
3577 3564 matchfn = scmutil.match(repo[None], pats, opts)
3578 3565 found = False
3579 3566 follow = opts.get('follow')
3580 3567
3581 3568 def prep(ctx, fns):
3582 3569 rev = ctx.rev()
3583 3570 pctx = ctx.p1()
3584 3571 parent = pctx.rev()
3585 3572 matches.setdefault(rev, {})
3586 3573 matches.setdefault(parent, {})
3587 3574 files = revfiles.setdefault(rev, [])
3588 3575 for fn in fns:
3589 3576 flog = getfile(fn)
3590 3577 try:
3591 3578 fnode = ctx.filenode(fn)
3592 3579 except error.LookupError:
3593 3580 continue
3594 3581
3595 3582 copied = flog.renamed(fnode)
3596 3583 copy = follow and copied and copied[0]
3597 3584 if copy:
3598 3585 copies.setdefault(rev, {})[fn] = copy
3599 3586 if fn in skip:
3600 3587 if copy:
3601 3588 skip[copy] = True
3602 3589 continue
3603 3590 files.append(fn)
3604 3591
3605 3592 if fn not in matches[rev]:
3606 3593 grepbody(fn, rev, flog.read(fnode))
3607 3594
3608 3595 pfn = copy or fn
3609 3596 if pfn not in matches[parent]:
3610 3597 try:
3611 3598 fnode = pctx.filenode(pfn)
3612 3599 grepbody(pfn, parent, flog.read(fnode))
3613 3600 except error.LookupError:
3614 3601 pass
3615 3602
3616 3603 fm = ui.formatter('grep', opts)
3617 3604 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3618 3605 rev = ctx.rev()
3619 3606 parent = ctx.p1().rev()
3620 3607 for fn in sorted(revfiles.get(rev, [])):
3621 3608 states = matches[rev][fn]
3622 3609 copy = copies.get(rev, {}).get(fn)
3623 3610 if fn in skip:
3624 3611 if copy:
3625 3612 skip[copy] = True
3626 3613 continue
3627 3614 pstates = matches.get(parent, {}).get(copy or fn, [])
3628 3615 if pstates or states:
3629 3616 r = display(fm, fn, ctx, pstates, states)
3630 3617 found = found or r
3631 3618 if r and not opts.get('all'):
3632 3619 skip[fn] = True
3633 3620 if copy:
3634 3621 skip[copy] = True
3635 3622 del matches[rev]
3636 3623 del revfiles[rev]
3637 3624 fm.end()
3638 3625
3639 3626 return not found
3640 3627
3641 3628 @command('heads',
3642 3629 [('r', 'rev', '',
3643 3630 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3644 3631 ('t', 'topo', False, _('show topological heads only')),
3645 3632 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3646 3633 ('c', 'closed', False, _('show normal and closed branch heads')),
3647 3634 ] + templateopts,
3648 3635 _('[-ct] [-r STARTREV] [REV]...'))
3649 3636 def heads(ui, repo, *branchrevs, **opts):
3650 3637 """show branch heads
3651 3638
3652 3639 With no arguments, show all open branch heads in the repository.
3653 3640 Branch heads are changesets that have no descendants on the
3654 3641 same branch. They are where development generally takes place and
3655 3642 are the usual targets for update and merge operations.
3656 3643
3657 3644 If one or more REVs are given, only open branch heads on the
3658 3645 branches associated with the specified changesets are shown. This
3659 3646 means that you can use :hg:`heads .` to see the heads on the
3660 3647 currently checked-out branch.
3661 3648
3662 3649 If -c/--closed is specified, also show branch heads marked closed
3663 3650 (see :hg:`commit --close-branch`).
3664 3651
3665 3652 If STARTREV is specified, only those heads that are descendants of
3666 3653 STARTREV will be displayed.
3667 3654
3668 3655 If -t/--topo is specified, named branch mechanics will be ignored and only
3669 3656 topological heads (changesets with no children) will be shown.
3670 3657
3671 3658 Returns 0 if matching heads are found, 1 if not.
3672 3659 """
3673 3660
3674 3661 start = None
3675 3662 if 'rev' in opts:
3676 3663 start = scmutil.revsingle(repo, opts['rev'], None).node()
3677 3664
3678 3665 if opts.get('topo'):
3679 3666 heads = [repo[h] for h in repo.heads(start)]
3680 3667 else:
3681 3668 heads = []
3682 3669 for branch in repo.branchmap():
3683 3670 heads += repo.branchheads(branch, start, opts.get('closed'))
3684 3671 heads = [repo[h] for h in heads]
3685 3672
3686 3673 if branchrevs:
3687 3674 branches = set(repo[br].branch() for br in branchrevs)
3688 3675 heads = [h for h in heads if h.branch() in branches]
3689 3676
3690 3677 if opts.get('active') and branchrevs:
3691 3678 dagheads = repo.heads(start)
3692 3679 heads = [h for h in heads if h.node() in dagheads]
3693 3680
3694 3681 if branchrevs:
3695 3682 haveheads = set(h.branch() for h in heads)
3696 3683 if branches - haveheads:
3697 3684 headless = ', '.join(b for b in branches - haveheads)
3698 3685 msg = _('no open branch heads found on branches %s')
3699 3686 if opts.get('rev'):
3700 3687 msg += _(' (started at %s)') % opts['rev']
3701 3688 ui.warn((msg + '\n') % headless)
3702 3689
3703 3690 if not heads:
3704 3691 return 1
3705 3692
3706 3693 heads = sorted(heads, key=lambda x: -x.rev())
3707 3694 displayer = cmdutil.show_changeset(ui, repo, opts)
3708 3695 for ctx in heads:
3709 3696 displayer.show(ctx)
3710 3697 displayer.close()
3711 3698
3712 3699 @command('help',
3713 3700 [('e', 'extension', None, _('show only help for extensions')),
3714 3701 ('c', 'command', None, _('show only help for commands')),
3715 3702 ('k', 'keyword', None, _('show topics matching keyword')),
3716 3703 ('s', 'system', [], _('show help for specific platform(s)')),
3717 3704 ],
3718 3705 _('[-ecks] [TOPIC]'),
3719 3706 norepo=True)
3720 3707 def help_(ui, name=None, **opts):
3721 3708 """show help for a given topic or a help overview
3722 3709
3723 3710 With no arguments, print a list of commands with short help messages.
3724 3711
3725 3712 Given a topic, extension, or command name, print help for that
3726 3713 topic.
3727 3714
3728 3715 Returns 0 if successful.
3729 3716 """
3730 3717
3731 3718 textwidth = ui.configint('ui', 'textwidth', 78)
3732 3719 termwidth = ui.termwidth() - 2
3733 3720 if textwidth <= 0 or termwidth < textwidth:
3734 3721 textwidth = termwidth
3735 3722
3736 3723 keep = opts.get('system') or []
3737 3724 if len(keep) == 0:
3738 3725 if pycompat.sysplatform.startswith('win'):
3739 3726 keep.append('windows')
3740 3727 elif pycompat.sysplatform == 'OpenVMS':
3741 3728 keep.append('vms')
3742 3729 elif pycompat.sysplatform == 'plan9':
3743 3730 keep.append('plan9')
3744 3731 else:
3745 3732 keep.append('unix')
3746 3733 keep.append(pycompat.sysplatform.lower())
3747 3734 if ui.verbose:
3748 3735 keep.append('verbose')
3749 3736
3750 3737 fullname = name
3751 3738 section = None
3752 3739 subtopic = None
3753 3740 if name and '.' in name:
3754 3741 name, remaining = name.split('.', 1)
3755 3742 remaining = encoding.lower(remaining)
3756 3743 if '.' in remaining:
3757 3744 subtopic, section = remaining.split('.', 1)
3758 3745 else:
3759 3746 if name in help.subtopics:
3760 3747 subtopic = remaining
3761 3748 else:
3762 3749 section = remaining
3763 3750
3764 3751 text = help.help_(ui, name, subtopic=subtopic, **opts)
3765 3752
3766 3753 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3767 3754 section=section)
3768 3755
3769 3756 # We could have been given a weird ".foo" section without a name
3770 3757 # to look for, or we could have simply failed to found "foo.bar"
3771 3758 # because bar isn't a section of foo
3772 3759 if section and not (formatted and name):
3773 3760 raise error.Abort(_("help section not found: %s") % fullname)
3774 3761
3775 3762 if 'verbose' in pruned:
3776 3763 keep.append('omitted')
3777 3764 else:
3778 3765 keep.append('notomitted')
3779 3766 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3780 3767 section=section)
3781 3768 ui.write(formatted)
3782 3769
3783 3770
3784 3771 @command('identify|id',
3785 3772 [('r', 'rev', '',
3786 3773 _('identify the specified revision'), _('REV')),
3787 3774 ('n', 'num', None, _('show local revision number')),
3788 3775 ('i', 'id', None, _('show global revision id')),
3789 3776 ('b', 'branch', None, _('show branch')),
3790 3777 ('t', 'tags', None, _('show tags')),
3791 3778 ('B', 'bookmarks', None, _('show bookmarks')),
3792 3779 ] + remoteopts,
3793 3780 _('[-nibtB] [-r REV] [SOURCE]'),
3794 3781 optionalrepo=True)
3795 3782 def identify(ui, repo, source=None, rev=None,
3796 3783 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3797 3784 """identify the working directory or specified revision
3798 3785
3799 3786 Print a summary identifying the repository state at REV using one or
3800 3787 two parent hash identifiers, followed by a "+" if the working
3801 3788 directory has uncommitted changes, the branch name (if not default),
3802 3789 a list of tags, and a list of bookmarks.
3803 3790
3804 3791 When REV is not given, print a summary of the current state of the
3805 3792 repository.
3806 3793
3807 3794 Specifying a path to a repository root or Mercurial bundle will
3808 3795 cause lookup to operate on that repository/bundle.
3809 3796
3810 3797 .. container:: verbose
3811 3798
3812 3799 Examples:
3813 3800
3814 3801 - generate a build identifier for the working directory::
3815 3802
3816 3803 hg id --id > build-id.dat
3817 3804
3818 3805 - find the revision corresponding to a tag::
3819 3806
3820 3807 hg id -n -r 1.3
3821 3808
3822 3809 - check the most recent revision of a remote repository::
3823 3810
3824 3811 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3825 3812
3826 3813 See :hg:`log` for generating more information about specific revisions,
3827 3814 including full hash identifiers.
3828 3815
3829 3816 Returns 0 if successful.
3830 3817 """
3831 3818
3832 3819 if not repo and not source:
3833 3820 raise error.Abort(_("there is no Mercurial repository here "
3834 3821 "(.hg not found)"))
3835 3822
3836 3823 if ui.debugflag:
3837 3824 hexfunc = hex
3838 3825 else:
3839 3826 hexfunc = short
3840 3827 default = not (num or id or branch or tags or bookmarks)
3841 3828 output = []
3842 3829 revs = []
3843 3830
3844 3831 if source:
3845 3832 source, branches = hg.parseurl(ui.expandpath(source))
3846 3833 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3847 3834 repo = peer.local()
3848 3835 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3849 3836
3850 3837 if not repo:
3851 3838 if num or branch or tags:
3852 3839 raise error.Abort(
3853 3840 _("can't query remote revision number, branch, or tags"))
3854 3841 if not rev and revs:
3855 3842 rev = revs[0]
3856 3843 if not rev:
3857 3844 rev = "tip"
3858 3845
3859 3846 remoterev = peer.lookup(rev)
3860 3847 if default or id:
3861 3848 output = [hexfunc(remoterev)]
3862 3849
3863 3850 def getbms():
3864 3851 bms = []
3865 3852
3866 3853 if 'bookmarks' in peer.listkeys('namespaces'):
3867 3854 hexremoterev = hex(remoterev)
3868 3855 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3869 3856 if bmr == hexremoterev]
3870 3857
3871 3858 return sorted(bms)
3872 3859
3873 3860 if bookmarks:
3874 3861 output.extend(getbms())
3875 3862 elif default and not ui.quiet:
3876 3863 # multiple bookmarks for a single parent separated by '/'
3877 3864 bm = '/'.join(getbms())
3878 3865 if bm:
3879 3866 output.append(bm)
3880 3867 else:
3881 3868 ctx = scmutil.revsingle(repo, rev, None)
3882 3869
3883 3870 if ctx.rev() is None:
3884 3871 ctx = repo[None]
3885 3872 parents = ctx.parents()
3886 3873 taglist = []
3887 3874 for p in parents:
3888 3875 taglist.extend(p.tags())
3889 3876
3890 3877 changed = ""
3891 3878 if default or id or num:
3892 3879 if (any(repo.status())
3893 3880 or any(ctx.sub(s).dirty() for s in ctx.substate)):
3894 3881 changed = '+'
3895 3882 if default or id:
3896 3883 output = ["%s%s" %
3897 3884 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3898 3885 if num:
3899 3886 output.append("%s%s" %
3900 3887 ('+'.join([str(p.rev()) for p in parents]), changed))
3901 3888 else:
3902 3889 if default or id:
3903 3890 output = [hexfunc(ctx.node())]
3904 3891 if num:
3905 3892 output.append(str(ctx.rev()))
3906 3893 taglist = ctx.tags()
3907 3894
3908 3895 if default and not ui.quiet:
3909 3896 b = ctx.branch()
3910 3897 if b != 'default':
3911 3898 output.append("(%s)" % b)
3912 3899
3913 3900 # multiple tags for a single parent separated by '/'
3914 3901 t = '/'.join(taglist)
3915 3902 if t:
3916 3903 output.append(t)
3917 3904
3918 3905 # multiple bookmarks for a single parent separated by '/'
3919 3906 bm = '/'.join(ctx.bookmarks())
3920 3907 if bm:
3921 3908 output.append(bm)
3922 3909 else:
3923 3910 if branch:
3924 3911 output.append(ctx.branch())
3925 3912
3926 3913 if tags:
3927 3914 output.extend(taglist)
3928 3915
3929 3916 if bookmarks:
3930 3917 output.extend(ctx.bookmarks())
3931 3918
3932 3919 ui.write("%s\n" % ' '.join(output))
3933 3920
3934 3921 @command('import|patch',
3935 3922 [('p', 'strip', 1,
3936 3923 _('directory strip option for patch. This has the same '
3937 3924 'meaning as the corresponding patch option'), _('NUM')),
3938 3925 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3939 3926 ('e', 'edit', False, _('invoke editor on commit messages')),
3940 3927 ('f', 'force', None,
3941 3928 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3942 3929 ('', 'no-commit', None,
3943 3930 _("don't commit, just update the working directory")),
3944 3931 ('', 'bypass', None,
3945 3932 _("apply patch without touching the working directory")),
3946 3933 ('', 'partial', None,
3947 3934 _('commit even if some hunks fail')),
3948 3935 ('', 'exact', None,
3949 3936 _('abort if patch would apply lossily')),
3950 3937 ('', 'prefix', '',
3951 3938 _('apply patch to subdirectory'), _('DIR')),
3952 3939 ('', 'import-branch', None,
3953 3940 _('use any branch information in patch (implied by --exact)'))] +
3954 3941 commitopts + commitopts2 + similarityopts,
3955 3942 _('[OPTION]... PATCH...'))
3956 3943 def import_(ui, repo, patch1=None, *patches, **opts):
3957 3944 """import an ordered set of patches
3958 3945
3959 3946 Import a list of patches and commit them individually (unless
3960 3947 --no-commit is specified).
3961 3948
3962 3949 To read a patch from standard input (stdin), use "-" as the patch
3963 3950 name. If a URL is specified, the patch will be downloaded from
3964 3951 there.
3965 3952
3966 3953 Import first applies changes to the working directory (unless
3967 3954 --bypass is specified), import will abort if there are outstanding
3968 3955 changes.
3969 3956
3970 3957 Use --bypass to apply and commit patches directly to the
3971 3958 repository, without affecting the working directory. Without
3972 3959 --exact, patches will be applied on top of the working directory
3973 3960 parent revision.
3974 3961
3975 3962 You can import a patch straight from a mail message. Even patches
3976 3963 as attachments work (to use the body part, it must have type
3977 3964 text/plain or text/x-patch). From and Subject headers of email
3978 3965 message are used as default committer and commit message. All
3979 3966 text/plain body parts before first diff are added to the commit
3980 3967 message.
3981 3968
3982 3969 If the imported patch was generated by :hg:`export`, user and
3983 3970 description from patch override values from message headers and
3984 3971 body. Values given on command line with -m/--message and -u/--user
3985 3972 override these.
3986 3973
3987 3974 If --exact is specified, import will set the working directory to
3988 3975 the parent of each patch before applying it, and will abort if the
3989 3976 resulting changeset has a different ID than the one recorded in
3990 3977 the patch. This will guard against various ways that portable
3991 3978 patch formats and mail systems might fail to transfer Mercurial
3992 3979 data or metadata. See :hg:`bundle` for lossless transmission.
3993 3980
3994 3981 Use --partial to ensure a changeset will be created from the patch
3995 3982 even if some hunks fail to apply. Hunks that fail to apply will be
3996 3983 written to a <target-file>.rej file. Conflicts can then be resolved
3997 3984 by hand before :hg:`commit --amend` is run to update the created
3998 3985 changeset. This flag exists to let people import patches that
3999 3986 partially apply without losing the associated metadata (author,
4000 3987 date, description, ...).
4001 3988
4002 3989 .. note::
4003 3990
4004 3991 When no hunks apply cleanly, :hg:`import --partial` will create
4005 3992 an empty changeset, importing only the patch metadata.
4006 3993
4007 3994 With -s/--similarity, hg will attempt to discover renames and
4008 3995 copies in the patch in the same way as :hg:`addremove`.
4009 3996
4010 3997 It is possible to use external patch programs to perform the patch
4011 3998 by setting the ``ui.patch`` configuration option. For the default
4012 3999 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4013 4000 See :hg:`help config` for more information about configuration
4014 4001 files and how to use these options.
4015 4002
4016 4003 See :hg:`help dates` for a list of formats valid for -d/--date.
4017 4004
4018 4005 .. container:: verbose
4019 4006
4020 4007 Examples:
4021 4008
4022 4009 - import a traditional patch from a website and detect renames::
4023 4010
4024 4011 hg import -s 80 http://example.com/bugfix.patch
4025 4012
4026 4013 - import a changeset from an hgweb server::
4027 4014
4028 4015 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4029 4016
4030 4017 - import all the patches in an Unix-style mbox::
4031 4018
4032 4019 hg import incoming-patches.mbox
4033 4020
4034 4021 - import patches from stdin::
4035 4022
4036 4023 hg import -
4037 4024
4038 4025 - attempt to exactly restore an exported changeset (not always
4039 4026 possible)::
4040 4027
4041 4028 hg import --exact proposed-fix.patch
4042 4029
4043 4030 - use an external tool to apply a patch which is too fuzzy for
4044 4031 the default internal tool.
4045 4032
4046 4033 hg import --config ui.patch="patch --merge" fuzzy.patch
4047 4034
4048 4035 - change the default fuzzing from 2 to a less strict 7
4049 4036
4050 4037 hg import --config ui.fuzz=7 fuzz.patch
4051 4038
4052 4039 Returns 0 on success, 1 on partial success (see --partial).
4053 4040 """
4054 4041
4055 4042 if not patch1:
4056 4043 raise error.Abort(_('need at least one patch to import'))
4057 4044
4058 4045 patches = (patch1,) + patches
4059 4046
4060 4047 date = opts.get('date')
4061 4048 if date:
4062 4049 opts['date'] = util.parsedate(date)
4063 4050
4064 4051 exact = opts.get('exact')
4065 4052 update = not opts.get('bypass')
4066 4053 if not update and opts.get('no_commit'):
4067 4054 raise error.Abort(_('cannot use --no-commit with --bypass'))
4068 4055 try:
4069 4056 sim = float(opts.get('similarity') or 0)
4070 4057 except ValueError:
4071 4058 raise error.Abort(_('similarity must be a number'))
4072 4059 if sim < 0 or sim > 100:
4073 4060 raise error.Abort(_('similarity must be between 0 and 100'))
4074 4061 if sim and not update:
4075 4062 raise error.Abort(_('cannot use --similarity with --bypass'))
4076 4063 if exact:
4077 4064 if opts.get('edit'):
4078 4065 raise error.Abort(_('cannot use --exact with --edit'))
4079 4066 if opts.get('prefix'):
4080 4067 raise error.Abort(_('cannot use --exact with --prefix'))
4081 4068
4082 4069 base = opts["base"]
4083 4070 wlock = dsguard = lock = tr = None
4084 4071 msgs = []
4085 4072 ret = 0
4086 4073
4087 4074
4088 4075 try:
4089 4076 wlock = repo.wlock()
4090 4077
4091 4078 if update:
4092 4079 cmdutil.checkunfinished(repo)
4093 4080 if (exact or not opts.get('force')):
4094 4081 cmdutil.bailifchanged(repo)
4095 4082
4096 4083 if not opts.get('no_commit'):
4097 4084 lock = repo.lock()
4098 4085 tr = repo.transaction('import')
4099 4086 else:
4100 4087 dsguard = dirstateguard.dirstateguard(repo, 'import')
4101 4088 parents = repo[None].parents()
4102 4089 for patchurl in patches:
4103 4090 if patchurl == '-':
4104 4091 ui.status(_('applying patch from stdin\n'))
4105 4092 patchfile = ui.fin
4106 4093 patchurl = 'stdin' # for error message
4107 4094 else:
4108 4095 patchurl = os.path.join(base, patchurl)
4109 4096 ui.status(_('applying %s\n') % patchurl)
4110 4097 patchfile = hg.openpath(ui, patchurl)
4111 4098
4112 4099 haspatch = False
4113 4100 for hunk in patch.split(patchfile):
4114 4101 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4115 4102 parents, opts,
4116 4103 msgs, hg.clean)
4117 4104 if msg:
4118 4105 haspatch = True
4119 4106 ui.note(msg + '\n')
4120 4107 if update or exact:
4121 4108 parents = repo[None].parents()
4122 4109 else:
4123 4110 parents = [repo[node]]
4124 4111 if rej:
4125 4112 ui.write_err(_("patch applied partially\n"))
4126 4113 ui.write_err(_("(fix the .rej files and run "
4127 4114 "`hg commit --amend`)\n"))
4128 4115 ret = 1
4129 4116 break
4130 4117
4131 4118 if not haspatch:
4132 4119 raise error.Abort(_('%s: no diffs found') % patchurl)
4133 4120
4134 4121 if tr:
4135 4122 tr.close()
4136 4123 if msgs:
4137 4124 repo.savecommitmessage('\n* * *\n'.join(msgs))
4138 4125 if dsguard:
4139 4126 dsguard.close()
4140 4127 return ret
4141 4128 finally:
4142 4129 if tr:
4143 4130 tr.release()
4144 4131 release(lock, dsguard, wlock)
4145 4132
4146 4133 @command('incoming|in',
4147 4134 [('f', 'force', None,
4148 4135 _('run even if remote repository is unrelated')),
4149 4136 ('n', 'newest-first', None, _('show newest record first')),
4150 4137 ('', 'bundle', '',
4151 4138 _('file to store the bundles into'), _('FILE')),
4152 4139 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4153 4140 ('B', 'bookmarks', False, _("compare bookmarks")),
4154 4141 ('b', 'branch', [],
4155 4142 _('a specific branch you would like to pull'), _('BRANCH')),
4156 4143 ] + logopts + remoteopts + subrepoopts,
4157 4144 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4158 4145 def incoming(ui, repo, source="default", **opts):
4159 4146 """show new changesets found in source
4160 4147
4161 4148 Show new changesets found in the specified path/URL or the default
4162 4149 pull location. These are the changesets that would have been pulled
4163 4150 if a pull at the time you issued this command.
4164 4151
4165 4152 See pull for valid source format details.
4166 4153
4167 4154 .. container:: verbose
4168 4155
4169 4156 With -B/--bookmarks, the result of bookmark comparison between
4170 4157 local and remote repositories is displayed. With -v/--verbose,
4171 4158 status is also displayed for each bookmark like below::
4172 4159
4173 4160 BM1 01234567890a added
4174 4161 BM2 1234567890ab advanced
4175 4162 BM3 234567890abc diverged
4176 4163 BM4 34567890abcd changed
4177 4164
4178 4165 The action taken locally when pulling depends on the
4179 4166 status of each bookmark:
4180 4167
4181 4168 :``added``: pull will create it
4182 4169 :``advanced``: pull will update it
4183 4170 :``diverged``: pull will create a divergent bookmark
4184 4171 :``changed``: result depends on remote changesets
4185 4172
4186 4173 From the point of view of pulling behavior, bookmark
4187 4174 existing only in the remote repository are treated as ``added``,
4188 4175 even if it is in fact locally deleted.
4189 4176
4190 4177 .. container:: verbose
4191 4178
4192 4179 For remote repository, using --bundle avoids downloading the
4193 4180 changesets twice if the incoming is followed by a pull.
4194 4181
4195 4182 Examples:
4196 4183
4197 4184 - show incoming changes with patches and full description::
4198 4185
4199 4186 hg incoming -vp
4200 4187
4201 4188 - show incoming changes excluding merges, store a bundle::
4202 4189
4203 4190 hg in -vpM --bundle incoming.hg
4204 4191 hg pull incoming.hg
4205 4192
4206 4193 - briefly list changes inside a bundle::
4207 4194
4208 4195 hg in changes.hg -T "{desc|firstline}\\n"
4209 4196
4210 4197 Returns 0 if there are incoming changes, 1 otherwise.
4211 4198 """
4212 4199 if opts.get('graph'):
4213 4200 cmdutil.checkunsupportedgraphflags([], opts)
4214 4201 def display(other, chlist, displayer):
4215 4202 revdag = cmdutil.graphrevs(other, chlist, opts)
4216 4203 cmdutil.displaygraph(ui, repo, revdag, displayer,
4217 4204 graphmod.asciiedges)
4218 4205
4219 4206 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4220 4207 return 0
4221 4208
4222 4209 if opts.get('bundle') and opts.get('subrepos'):
4223 4210 raise error.Abort(_('cannot combine --bundle and --subrepos'))
4224 4211
4225 4212 if opts.get('bookmarks'):
4226 4213 source, branches = hg.parseurl(ui.expandpath(source),
4227 4214 opts.get('branch'))
4228 4215 other = hg.peer(repo, opts, source)
4229 4216 if 'bookmarks' not in other.listkeys('namespaces'):
4230 4217 ui.warn(_("remote doesn't support bookmarks\n"))
4231 4218 return 0
4232 4219 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4233 4220 return bookmarks.incoming(ui, repo, other)
4234 4221
4235 4222 repo._subtoppath = ui.expandpath(source)
4236 4223 try:
4237 4224 return hg.incoming(ui, repo, source, opts)
4238 4225 finally:
4239 4226 del repo._subtoppath
4240 4227
4241 4228
4242 4229 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4243 4230 norepo=True)
4244 4231 def init(ui, dest=".", **opts):
4245 4232 """create a new repository in the given directory
4246 4233
4247 4234 Initialize a new repository in the given directory. If the given
4248 4235 directory does not exist, it will be created.
4249 4236
4250 4237 If no directory is given, the current directory is used.
4251 4238
4252 4239 It is possible to specify an ``ssh://`` URL as the destination.
4253 4240 See :hg:`help urls` for more information.
4254 4241
4255 4242 Returns 0 on success.
4256 4243 """
4257 4244 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4258 4245
4259 4246 @command('locate',
4260 4247 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4261 4248 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4262 4249 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4263 4250 ] + walkopts,
4264 4251 _('[OPTION]... [PATTERN]...'))
4265 4252 def locate(ui, repo, *pats, **opts):
4266 4253 """locate files matching specific patterns (DEPRECATED)
4267 4254
4268 4255 Print files under Mercurial control in the working directory whose
4269 4256 names match the given patterns.
4270 4257
4271 4258 By default, this command searches all directories in the working
4272 4259 directory. To search just the current directory and its
4273 4260 subdirectories, use "--include .".
4274 4261
4275 4262 If no patterns are given to match, this command prints the names
4276 4263 of all files under Mercurial control in the working directory.
4277 4264
4278 4265 If you want to feed the output of this command into the "xargs"
4279 4266 command, use the -0 option to both this command and "xargs". This
4280 4267 will avoid the problem of "xargs" treating single filenames that
4281 4268 contain whitespace as multiple filenames.
4282 4269
4283 4270 See :hg:`help files` for a more versatile command.
4284 4271
4285 4272 Returns 0 if a match is found, 1 otherwise.
4286 4273 """
4287 4274 if opts.get('print0'):
4288 4275 end = '\0'
4289 4276 else:
4290 4277 end = '\n'
4291 4278 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4292 4279
4293 4280 ret = 1
4294 4281 ctx = repo[rev]
4295 4282 m = scmutil.match(ctx, pats, opts, default='relglob',
4296 4283 badfn=lambda x, y: False)
4297 4284
4298 4285 for abs in ctx.matches(m):
4299 4286 if opts.get('fullpath'):
4300 4287 ui.write(repo.wjoin(abs), end)
4301 4288 else:
4302 4289 ui.write(((pats and m.rel(abs)) or abs), end)
4303 4290 ret = 0
4304 4291
4305 4292 return ret
4306 4293
4307 4294 @command('^log|history',
4308 4295 [('f', 'follow', None,
4309 4296 _('follow changeset history, or file history across copies and renames')),
4310 4297 ('', 'follow-first', None,
4311 4298 _('only follow the first parent of merge changesets (DEPRECATED)')),
4312 4299 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4313 4300 ('C', 'copies', None, _('show copied files')),
4314 4301 ('k', 'keyword', [],
4315 4302 _('do case-insensitive search for a given text'), _('TEXT')),
4316 4303 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4317 4304 ('', 'removed', None, _('include revisions where files were removed')),
4318 4305 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4319 4306 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4320 4307 ('', 'only-branch', [],
4321 4308 _('show only changesets within the given named branch (DEPRECATED)'),
4322 4309 _('BRANCH')),
4323 4310 ('b', 'branch', [],
4324 4311 _('show changesets within the given named branch'), _('BRANCH')),
4325 4312 ('P', 'prune', [],
4326 4313 _('do not display revision or any of its ancestors'), _('REV')),
4327 4314 ] + logopts + walkopts,
4328 4315 _('[OPTION]... [FILE]'),
4329 4316 inferrepo=True)
4330 4317 def log(ui, repo, *pats, **opts):
4331 4318 """show revision history of entire repository or files
4332 4319
4333 4320 Print the revision history of the specified files or the entire
4334 4321 project.
4335 4322
4336 4323 If no revision range is specified, the default is ``tip:0`` unless
4337 4324 --follow is set, in which case the working directory parent is
4338 4325 used as the starting revision.
4339 4326
4340 4327 File history is shown without following rename or copy history of
4341 4328 files. Use -f/--follow with a filename to follow history across
4342 4329 renames and copies. --follow without a filename will only show
4343 4330 ancestors or descendants of the starting revision.
4344 4331
4345 4332 By default this command prints revision number and changeset id,
4346 4333 tags, non-trivial parents, user, date and time, and a summary for
4347 4334 each commit. When the -v/--verbose switch is used, the list of
4348 4335 changed files and full commit message are shown.
4349 4336
4350 4337 With --graph the revisions are shown as an ASCII art DAG with the most
4351 4338 recent changeset at the top.
4352 4339 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4353 4340 and '+' represents a fork where the changeset from the lines below is a
4354 4341 parent of the 'o' merge on the same line.
4355 4342
4356 4343 .. note::
4357 4344
4358 4345 :hg:`log --patch` may generate unexpected diff output for merge
4359 4346 changesets, as it will only compare the merge changeset against
4360 4347 its first parent. Also, only files different from BOTH parents
4361 4348 will appear in files:.
4362 4349
4363 4350 .. note::
4364 4351
4365 4352 For performance reasons, :hg:`log FILE` may omit duplicate changes
4366 4353 made on branches and will not show removals or mode changes. To
4367 4354 see all such changes, use the --removed switch.
4368 4355
4369 4356 .. container:: verbose
4370 4357
4371 4358 Some examples:
4372 4359
4373 4360 - changesets with full descriptions and file lists::
4374 4361
4375 4362 hg log -v
4376 4363
4377 4364 - changesets ancestral to the working directory::
4378 4365
4379 4366 hg log -f
4380 4367
4381 4368 - last 10 commits on the current branch::
4382 4369
4383 4370 hg log -l 10 -b .
4384 4371
4385 4372 - changesets showing all modifications of a file, including removals::
4386 4373
4387 4374 hg log --removed file.c
4388 4375
4389 4376 - all changesets that touch a directory, with diffs, excluding merges::
4390 4377
4391 4378 hg log -Mp lib/
4392 4379
4393 4380 - all revision numbers that match a keyword::
4394 4381
4395 4382 hg log -k bug --template "{rev}\\n"
4396 4383
4397 4384 - the full hash identifier of the working directory parent::
4398 4385
4399 4386 hg log -r . --template "{node}\\n"
4400 4387
4401 4388 - list available log templates::
4402 4389
4403 4390 hg log -T list
4404 4391
4405 4392 - check if a given changeset is included in a tagged release::
4406 4393
4407 4394 hg log -r "a21ccf and ancestor(1.9)"
4408 4395
4409 4396 - find all changesets by some user in a date range::
4410 4397
4411 4398 hg log -k alice -d "may 2008 to jul 2008"
4412 4399
4413 4400 - summary of all changesets after the last tag::
4414 4401
4415 4402 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4416 4403
4417 4404 See :hg:`help dates` for a list of formats valid for -d/--date.
4418 4405
4419 4406 See :hg:`help revisions` for more about specifying and ordering
4420 4407 revisions.
4421 4408
4422 4409 See :hg:`help templates` for more about pre-packaged styles and
4423 4410 specifying custom templates.
4424 4411
4425 4412 Returns 0 on success.
4426 4413
4427 4414 """
4428 4415 if opts.get('follow') and opts.get('rev'):
4429 4416 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
4430 4417 del opts['follow']
4431 4418
4432 4419 if opts.get('graph'):
4433 4420 return cmdutil.graphlog(ui, repo, *pats, **opts)
4434 4421
4435 4422 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4436 4423 limit = cmdutil.loglimit(opts)
4437 4424 count = 0
4438 4425
4439 4426 getrenamed = None
4440 4427 if opts.get('copies'):
4441 4428 endrev = None
4442 4429 if opts.get('rev'):
4443 4430 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4444 4431 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4445 4432
4446 4433 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4447 4434 for rev in revs:
4448 4435 if count == limit:
4449 4436 break
4450 4437 ctx = repo[rev]
4451 4438 copies = None
4452 4439 if getrenamed is not None and rev:
4453 4440 copies = []
4454 4441 for fn in ctx.files():
4455 4442 rename = getrenamed(fn, rev)
4456 4443 if rename:
4457 4444 copies.append((fn, rename[0]))
4458 4445 if filematcher:
4459 4446 revmatchfn = filematcher(ctx.rev())
4460 4447 else:
4461 4448 revmatchfn = None
4462 4449 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4463 4450 if displayer.flush(ctx):
4464 4451 count += 1
4465 4452
4466 4453 displayer.close()
4467 4454
4468 4455 @command('manifest',
4469 4456 [('r', 'rev', '', _('revision to display'), _('REV')),
4470 4457 ('', 'all', False, _("list files from all revisions"))]
4471 4458 + formatteropts,
4472 4459 _('[-r REV]'))
4473 4460 def manifest(ui, repo, node=None, rev=None, **opts):
4474 4461 """output the current or given revision of the project manifest
4475 4462
4476 4463 Print a list of version controlled files for the given revision.
4477 4464 If no revision is given, the first parent of the working directory
4478 4465 is used, or the null revision if no revision is checked out.
4479 4466
4480 4467 With -v, print file permissions, symlink and executable bits.
4481 4468 With --debug, print file revision hashes.
4482 4469
4483 4470 If option --all is specified, the list of all files from all revisions
4484 4471 is printed. This includes deleted and renamed files.
4485 4472
4486 4473 Returns 0 on success.
4487 4474 """
4488 4475
4489 4476 fm = ui.formatter('manifest', opts)
4490 4477
4491 4478 if opts.get('all'):
4492 4479 if rev or node:
4493 4480 raise error.Abort(_("can't specify a revision with --all"))
4494 4481
4495 4482 res = []
4496 4483 prefix = "data/"
4497 4484 suffix = ".i"
4498 4485 plen = len(prefix)
4499 4486 slen = len(suffix)
4500 4487 with repo.lock():
4501 4488 for fn, b, size in repo.store.datafiles():
4502 4489 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4503 4490 res.append(fn[plen:-slen])
4504 4491 for f in res:
4505 4492 fm.startitem()
4506 4493 fm.write("path", '%s\n', f)
4507 4494 fm.end()
4508 4495 return
4509 4496
4510 4497 if rev and node:
4511 4498 raise error.Abort(_("please specify just one revision"))
4512 4499
4513 4500 if not node:
4514 4501 node = rev
4515 4502
4516 4503 char = {'l': '@', 'x': '*', '': ''}
4517 4504 mode = {'l': '644', 'x': '755', '': '644'}
4518 4505 ctx = scmutil.revsingle(repo, node)
4519 4506 mf = ctx.manifest()
4520 4507 for f in ctx:
4521 4508 fm.startitem()
4522 4509 fl = ctx[f].flags()
4523 4510 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4524 4511 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4525 4512 fm.write('path', '%s\n', f)
4526 4513 fm.end()
4527 4514
4528 4515 @command('^merge',
4529 4516 [('f', 'force', None,
4530 4517 _('force a merge including outstanding changes (DEPRECATED)')),
4531 4518 ('r', 'rev', '', _('revision to merge'), _('REV')),
4532 4519 ('P', 'preview', None,
4533 4520 _('review revisions to merge (no merge is performed)'))
4534 4521 ] + mergetoolopts,
4535 4522 _('[-P] [[-r] REV]'))
4536 4523 def merge(ui, repo, node=None, **opts):
4537 4524 """merge another revision into working directory
4538 4525
4539 4526 The current working directory is updated with all changes made in
4540 4527 the requested revision since the last common predecessor revision.
4541 4528
4542 4529 Files that changed between either parent are marked as changed for
4543 4530 the next commit and a commit must be performed before any further
4544 4531 updates to the repository are allowed. The next commit will have
4545 4532 two parents.
4546 4533
4547 4534 ``--tool`` can be used to specify the merge tool used for file
4548 4535 merges. It overrides the HGMERGE environment variable and your
4549 4536 configuration files. See :hg:`help merge-tools` for options.
4550 4537
4551 4538 If no revision is specified, the working directory's parent is a
4552 4539 head revision, and the current branch contains exactly one other
4553 4540 head, the other head is merged with by default. Otherwise, an
4554 4541 explicit revision with which to merge with must be provided.
4555 4542
4556 4543 See :hg:`help resolve` for information on handling file conflicts.
4557 4544
4558 4545 To undo an uncommitted merge, use :hg:`update --clean .` which
4559 4546 will check out a clean copy of the original merge parent, losing
4560 4547 all changes.
4561 4548
4562 4549 Returns 0 on success, 1 if there are unresolved files.
4563 4550 """
4564 4551
4565 4552 if opts.get('rev') and node:
4566 4553 raise error.Abort(_("please specify just one revision"))
4567 4554 if not node:
4568 4555 node = opts.get('rev')
4569 4556
4570 4557 if node:
4571 4558 node = scmutil.revsingle(repo, node).node()
4572 4559
4573 4560 if not node:
4574 4561 node = repo[destutil.destmerge(repo)].node()
4575 4562
4576 4563 if opts.get('preview'):
4577 4564 # find nodes that are ancestors of p2 but not of p1
4578 4565 p1 = repo.lookup('.')
4579 4566 p2 = repo.lookup(node)
4580 4567 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4581 4568
4582 4569 displayer = cmdutil.show_changeset(ui, repo, opts)
4583 4570 for node in nodes:
4584 4571 displayer.show(repo[node])
4585 4572 displayer.close()
4586 4573 return 0
4587 4574
4588 4575 try:
4589 4576 # ui.forcemerge is an internal variable, do not document
4590 4577 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4591 4578 force = opts.get('force')
4592 4579 labels = ['working copy', 'merge rev']
4593 4580 return hg.merge(repo, node, force=force, mergeforce=force,
4594 4581 labels=labels)
4595 4582 finally:
4596 4583 ui.setconfig('ui', 'forcemerge', '', 'merge')
4597 4584
4598 4585 @command('outgoing|out',
4599 4586 [('f', 'force', None, _('run even when the destination is unrelated')),
4600 4587 ('r', 'rev', [],
4601 4588 _('a changeset intended to be included in the destination'), _('REV')),
4602 4589 ('n', 'newest-first', None, _('show newest record first')),
4603 4590 ('B', 'bookmarks', False, _('compare bookmarks')),
4604 4591 ('b', 'branch', [], _('a specific branch you would like to push'),
4605 4592 _('BRANCH')),
4606 4593 ] + logopts + remoteopts + subrepoopts,
4607 4594 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4608 4595 def outgoing(ui, repo, dest=None, **opts):
4609 4596 """show changesets not found in the destination
4610 4597
4611 4598 Show changesets not found in the specified destination repository
4612 4599 or the default push location. These are the changesets that would
4613 4600 be pushed if a push was requested.
4614 4601
4615 4602 See pull for details of valid destination formats.
4616 4603
4617 4604 .. container:: verbose
4618 4605
4619 4606 With -B/--bookmarks, the result of bookmark comparison between
4620 4607 local and remote repositories is displayed. With -v/--verbose,
4621 4608 status is also displayed for each bookmark like below::
4622 4609
4623 4610 BM1 01234567890a added
4624 4611 BM2 deleted
4625 4612 BM3 234567890abc advanced
4626 4613 BM4 34567890abcd diverged
4627 4614 BM5 4567890abcde changed
4628 4615
4629 4616 The action taken when pushing depends on the
4630 4617 status of each bookmark:
4631 4618
4632 4619 :``added``: push with ``-B`` will create it
4633 4620 :``deleted``: push with ``-B`` will delete it
4634 4621 :``advanced``: push will update it
4635 4622 :``diverged``: push with ``-B`` will update it
4636 4623 :``changed``: push with ``-B`` will update it
4637 4624
4638 4625 From the point of view of pushing behavior, bookmarks
4639 4626 existing only in the remote repository are treated as
4640 4627 ``deleted``, even if it is in fact added remotely.
4641 4628
4642 4629 Returns 0 if there are outgoing changes, 1 otherwise.
4643 4630 """
4644 4631 if opts.get('graph'):
4645 4632 cmdutil.checkunsupportedgraphflags([], opts)
4646 4633 o, other = hg._outgoing(ui, repo, dest, opts)
4647 4634 if not o:
4648 4635 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4649 4636 return
4650 4637
4651 4638 revdag = cmdutil.graphrevs(repo, o, opts)
4652 4639 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4653 4640 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
4654 4641 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4655 4642 return 0
4656 4643
4657 4644 if opts.get('bookmarks'):
4658 4645 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4659 4646 dest, branches = hg.parseurl(dest, opts.get('branch'))
4660 4647 other = hg.peer(repo, opts, dest)
4661 4648 if 'bookmarks' not in other.listkeys('namespaces'):
4662 4649 ui.warn(_("remote doesn't support bookmarks\n"))
4663 4650 return 0
4664 4651 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4665 4652 return bookmarks.outgoing(ui, repo, other)
4666 4653
4667 4654 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4668 4655 try:
4669 4656 return hg.outgoing(ui, repo, dest, opts)
4670 4657 finally:
4671 4658 del repo._subtoppath
4672 4659
4673 4660 @command('parents',
4674 4661 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4675 4662 ] + templateopts,
4676 4663 _('[-r REV] [FILE]'),
4677 4664 inferrepo=True)
4678 4665 def parents(ui, repo, file_=None, **opts):
4679 4666 """show the parents of the working directory or revision (DEPRECATED)
4680 4667
4681 4668 Print the working directory's parent revisions. If a revision is
4682 4669 given via -r/--rev, the parent of that revision will be printed.
4683 4670 If a file argument is given, the revision in which the file was
4684 4671 last changed (before the working directory revision or the
4685 4672 argument to --rev if given) is printed.
4686 4673
4687 4674 This command is equivalent to::
4688 4675
4689 4676 hg log -r "p1()+p2()" or
4690 4677 hg log -r "p1(REV)+p2(REV)" or
4691 4678 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4692 4679 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4693 4680
4694 4681 See :hg:`summary` and :hg:`help revsets` for related information.
4695 4682
4696 4683 Returns 0 on success.
4697 4684 """
4698 4685
4699 4686 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4700 4687
4701 4688 if file_:
4702 4689 m = scmutil.match(ctx, (file_,), opts)
4703 4690 if m.anypats() or len(m.files()) != 1:
4704 4691 raise error.Abort(_('can only specify an explicit filename'))
4705 4692 file_ = m.files()[0]
4706 4693 filenodes = []
4707 4694 for cp in ctx.parents():
4708 4695 if not cp:
4709 4696 continue
4710 4697 try:
4711 4698 filenodes.append(cp.filenode(file_))
4712 4699 except error.LookupError:
4713 4700 pass
4714 4701 if not filenodes:
4715 4702 raise error.Abort(_("'%s' not found in manifest!") % file_)
4716 4703 p = []
4717 4704 for fn in filenodes:
4718 4705 fctx = repo.filectx(file_, fileid=fn)
4719 4706 p.append(fctx.node())
4720 4707 else:
4721 4708 p = [cp.node() for cp in ctx.parents()]
4722 4709
4723 4710 displayer = cmdutil.show_changeset(ui, repo, opts)
4724 4711 for n in p:
4725 4712 if n != nullid:
4726 4713 displayer.show(repo[n])
4727 4714 displayer.close()
4728 4715
4729 4716 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
4730 4717 def paths(ui, repo, search=None, **opts):
4731 4718 """show aliases for remote repositories
4732 4719
4733 4720 Show definition of symbolic path name NAME. If no name is given,
4734 4721 show definition of all available names.
4735 4722
4736 4723 Option -q/--quiet suppresses all output when searching for NAME
4737 4724 and shows only the path names when listing all definitions.
4738 4725
4739 4726 Path names are defined in the [paths] section of your
4740 4727 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4741 4728 repository, ``.hg/hgrc`` is used, too.
4742 4729
4743 4730 The path names ``default`` and ``default-push`` have a special
4744 4731 meaning. When performing a push or pull operation, they are used
4745 4732 as fallbacks if no location is specified on the command-line.
4746 4733 When ``default-push`` is set, it will be used for push and
4747 4734 ``default`` will be used for pull; otherwise ``default`` is used
4748 4735 as the fallback for both. When cloning a repository, the clone
4749 4736 source is written as ``default`` in ``.hg/hgrc``.
4750 4737
4751 4738 .. note::
4752 4739
4753 4740 ``default`` and ``default-push`` apply to all inbound (e.g.
4754 4741 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4755 4742 and :hg:`bundle`) operations.
4756 4743
4757 4744 See :hg:`help urls` for more information.
4758 4745
4759 4746 Returns 0 on success.
4760 4747 """
4761 4748 if search:
4762 4749 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4763 4750 if name == search]
4764 4751 else:
4765 4752 pathitems = sorted(ui.paths.iteritems())
4766 4753
4767 4754 fm = ui.formatter('paths', opts)
4768 4755 if fm.isplain():
4769 4756 hidepassword = util.hidepassword
4770 4757 else:
4771 4758 hidepassword = str
4772 4759 if ui.quiet:
4773 4760 namefmt = '%s\n'
4774 4761 else:
4775 4762 namefmt = '%s = '
4776 4763 showsubopts = not search and not ui.quiet
4777 4764
4778 4765 for name, path in pathitems:
4779 4766 fm.startitem()
4780 4767 fm.condwrite(not search, 'name', namefmt, name)
4781 4768 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4782 4769 for subopt, value in sorted(path.suboptions.items()):
4783 4770 assert subopt not in ('name', 'url')
4784 4771 if showsubopts:
4785 4772 fm.plain('%s:%s = ' % (name, subopt))
4786 4773 fm.condwrite(showsubopts, subopt, '%s\n', value)
4787 4774
4788 4775 fm.end()
4789 4776
4790 4777 if search and not pathitems:
4791 4778 if not ui.quiet:
4792 4779 ui.warn(_("not found!\n"))
4793 4780 return 1
4794 4781 else:
4795 4782 return 0
4796 4783
4797 4784 @command('phase',
4798 4785 [('p', 'public', False, _('set changeset phase to public')),
4799 4786 ('d', 'draft', False, _('set changeset phase to draft')),
4800 4787 ('s', 'secret', False, _('set changeset phase to secret')),
4801 4788 ('f', 'force', False, _('allow to move boundary backward')),
4802 4789 ('r', 'rev', [], _('target revision'), _('REV')),
4803 4790 ],
4804 4791 _('[-p|-d|-s] [-f] [-r] [REV...]'))
4805 4792 def phase(ui, repo, *revs, **opts):
4806 4793 """set or show the current phase name
4807 4794
4808 4795 With no argument, show the phase name of the current revision(s).
4809 4796
4810 4797 With one of -p/--public, -d/--draft or -s/--secret, change the
4811 4798 phase value of the specified revisions.
4812 4799
4813 4800 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4814 4801 lower phase to an higher phase. Phases are ordered as follows::
4815 4802
4816 4803 public < draft < secret
4817 4804
4818 4805 Returns 0 on success, 1 if some phases could not be changed.
4819 4806
4820 4807 (For more information about the phases concept, see :hg:`help phases`.)
4821 4808 """
4822 4809 # search for a unique phase argument
4823 4810 targetphase = None
4824 4811 for idx, name in enumerate(phases.phasenames):
4825 4812 if opts[name]:
4826 4813 if targetphase is not None:
4827 4814 raise error.Abort(_('only one phase can be specified'))
4828 4815 targetphase = idx
4829 4816
4830 4817 # look for specified revision
4831 4818 revs = list(revs)
4832 4819 revs.extend(opts['rev'])
4833 4820 if not revs:
4834 4821 # display both parents as the second parent phase can influence
4835 4822 # the phase of a merge commit
4836 4823 revs = [c.rev() for c in repo[None].parents()]
4837 4824
4838 4825 revs = scmutil.revrange(repo, revs)
4839 4826
4840 4827 lock = None
4841 4828 ret = 0
4842 4829 if targetphase is None:
4843 4830 # display
4844 4831 for r in revs:
4845 4832 ctx = repo[r]
4846 4833 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4847 4834 else:
4848 4835 tr = None
4849 4836 lock = repo.lock()
4850 4837 try:
4851 4838 tr = repo.transaction("phase")
4852 4839 # set phase
4853 4840 if not revs:
4854 4841 raise error.Abort(_('empty revision set'))
4855 4842 nodes = [repo[r].node() for r in revs]
4856 4843 # moving revision from public to draft may hide them
4857 4844 # We have to check result on an unfiltered repository
4858 4845 unfi = repo.unfiltered()
4859 4846 getphase = unfi._phasecache.phase
4860 4847 olddata = [getphase(unfi, r) for r in unfi]
4861 4848 phases.advanceboundary(repo, tr, targetphase, nodes)
4862 4849 if opts['force']:
4863 4850 phases.retractboundary(repo, tr, targetphase, nodes)
4864 4851 tr.close()
4865 4852 finally:
4866 4853 if tr is not None:
4867 4854 tr.release()
4868 4855 lock.release()
4869 4856 getphase = unfi._phasecache.phase
4870 4857 newdata = [getphase(unfi, r) for r in unfi]
4871 4858 changes = sum(newdata[r] != olddata[r] for r in unfi)
4872 4859 cl = unfi.changelog
4873 4860 rejected = [n for n in nodes
4874 4861 if newdata[cl.rev(n)] < targetphase]
4875 4862 if rejected:
4876 4863 ui.warn(_('cannot move %i changesets to a higher '
4877 4864 'phase, use --force\n') % len(rejected))
4878 4865 ret = 1
4879 4866 if changes:
4880 4867 msg = _('phase changed for %i changesets\n') % changes
4881 4868 if ret:
4882 4869 ui.status(msg)
4883 4870 else:
4884 4871 ui.note(msg)
4885 4872 else:
4886 4873 ui.warn(_('no phases changed\n'))
4887 4874 return ret
4888 4875
4889 4876 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4890 4877 """Run after a changegroup has been added via pull/unbundle
4891 4878
4892 4879 This takes arguments below:
4893 4880
4894 4881 :modheads: change of heads by pull/unbundle
4895 4882 :optupdate: updating working directory is needed or not
4896 4883 :checkout: update destination revision (or None to default destination)
4897 4884 :brev: a name, which might be a bookmark to be activated after updating
4898 4885 """
4899 4886 if modheads == 0:
4900 4887 return
4901 4888 if optupdate:
4902 4889 try:
4903 4890 return hg.updatetotally(ui, repo, checkout, brev)
4904 4891 except error.UpdateAbort as inst:
4905 4892 msg = _("not updating: %s") % str(inst)
4906 4893 hint = inst.hint
4907 4894 raise error.UpdateAbort(msg, hint=hint)
4908 4895 if modheads > 1:
4909 4896 currentbranchheads = len(repo.branchheads())
4910 4897 if currentbranchheads == modheads:
4911 4898 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4912 4899 elif currentbranchheads > 1:
4913 4900 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4914 4901 "merge)\n"))
4915 4902 else:
4916 4903 ui.status(_("(run 'hg heads' to see heads)\n"))
4917 4904 else:
4918 4905 ui.status(_("(run 'hg update' to get a working copy)\n"))
4919 4906
4920 4907 @command('^pull',
4921 4908 [('u', 'update', None,
4922 4909 _('update to new branch head if changesets were pulled')),
4923 4910 ('f', 'force', None, _('run even when remote repository is unrelated')),
4924 4911 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4925 4912 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4926 4913 ('b', 'branch', [], _('a specific branch you would like to pull'),
4927 4914 _('BRANCH')),
4928 4915 ] + remoteopts,
4929 4916 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4930 4917 def pull(ui, repo, source="default", **opts):
4931 4918 """pull changes from the specified source
4932 4919
4933 4920 Pull changes from a remote repository to a local one.
4934 4921
4935 4922 This finds all changes from the repository at the specified path
4936 4923 or URL and adds them to a local repository (the current one unless
4937 4924 -R is specified). By default, this does not update the copy of the
4938 4925 project in the working directory.
4939 4926
4940 4927 Use :hg:`incoming` if you want to see what would have been added
4941 4928 by a pull at the time you issued this command. If you then decide
4942 4929 to add those changes to the repository, you should use :hg:`pull
4943 4930 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4944 4931
4945 4932 If SOURCE is omitted, the 'default' path will be used.
4946 4933 See :hg:`help urls` for more information.
4947 4934
4948 4935 Specifying bookmark as ``.`` is equivalent to specifying the active
4949 4936 bookmark's name.
4950 4937
4951 4938 Returns 0 on success, 1 if an update had unresolved files.
4952 4939 """
4953 4940 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4954 4941 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4955 4942 other = hg.peer(repo, opts, source)
4956 4943 try:
4957 4944 revs, checkout = hg.addbranchrevs(repo, other, branches,
4958 4945 opts.get('rev'))
4959 4946
4960 4947
4961 4948 pullopargs = {}
4962 4949 if opts.get('bookmark'):
4963 4950 if not revs:
4964 4951 revs = []
4965 4952 # The list of bookmark used here is not the one used to actually
4966 4953 # update the bookmark name. This can result in the revision pulled
4967 4954 # not ending up with the name of the bookmark because of a race
4968 4955 # condition on the server. (See issue 4689 for details)
4969 4956 remotebookmarks = other.listkeys('bookmarks')
4970 4957 pullopargs['remotebookmarks'] = remotebookmarks
4971 4958 for b in opts['bookmark']:
4972 4959 b = repo._bookmarks.expandname(b)
4973 4960 if b not in remotebookmarks:
4974 4961 raise error.Abort(_('remote bookmark %s not found!') % b)
4975 4962 revs.append(remotebookmarks[b])
4976 4963
4977 4964 if revs:
4978 4965 try:
4979 4966 # When 'rev' is a bookmark name, we cannot guarantee that it
4980 4967 # will be updated with that name because of a race condition
4981 4968 # server side. (See issue 4689 for details)
4982 4969 oldrevs = revs
4983 4970 revs = [] # actually, nodes
4984 4971 for r in oldrevs:
4985 4972 node = other.lookup(r)
4986 4973 revs.append(node)
4987 4974 if r == checkout:
4988 4975 checkout = node
4989 4976 except error.CapabilityError:
4990 4977 err = _("other repository doesn't support revision lookup, "
4991 4978 "so a rev cannot be specified.")
4992 4979 raise error.Abort(err)
4993 4980
4994 4981 pullopargs.update(opts.get('opargs', {}))
4995 4982 modheads = exchange.pull(repo, other, heads=revs,
4996 4983 force=opts.get('force'),
4997 4984 bookmarks=opts.get('bookmark', ()),
4998 4985 opargs=pullopargs).cgresult
4999 4986
5000 4987 # brev is a name, which might be a bookmark to be activated at
5001 4988 # the end of the update. In other words, it is an explicit
5002 4989 # destination of the update
5003 4990 brev = None
5004 4991
5005 4992 if checkout:
5006 4993 checkout = str(repo.changelog.rev(checkout))
5007 4994
5008 4995 # order below depends on implementation of
5009 4996 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5010 4997 # because 'checkout' is determined without it.
5011 4998 if opts.get('rev'):
5012 4999 brev = opts['rev'][0]
5013 5000 elif opts.get('branch'):
5014 5001 brev = opts['branch'][0]
5015 5002 else:
5016 5003 brev = branches[0]
5017 5004 repo._subtoppath = source
5018 5005 try:
5019 5006 ret = postincoming(ui, repo, modheads, opts.get('update'),
5020 5007 checkout, brev)
5021 5008
5022 5009 finally:
5023 5010 del repo._subtoppath
5024 5011
5025 5012 finally:
5026 5013 other.close()
5027 5014 return ret
5028 5015
5029 5016 @command('^push',
5030 5017 [('f', 'force', None, _('force push')),
5031 5018 ('r', 'rev', [],
5032 5019 _('a changeset intended to be included in the destination'),
5033 5020 _('REV')),
5034 5021 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5035 5022 ('b', 'branch', [],
5036 5023 _('a specific branch you would like to push'), _('BRANCH')),
5037 5024 ('', 'new-branch', False, _('allow pushing a new branch')),
5038 5025 ] + remoteopts,
5039 5026 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5040 5027 def push(ui, repo, dest=None, **opts):
5041 5028 """push changes to the specified destination
5042 5029
5043 5030 Push changesets from the local repository to the specified
5044 5031 destination.
5045 5032
5046 5033 This operation is symmetrical to pull: it is identical to a pull
5047 5034 in the destination repository from the current one.
5048 5035
5049 5036 By default, push will not allow creation of new heads at the
5050 5037 destination, since multiple heads would make it unclear which head
5051 5038 to use. In this situation, it is recommended to pull and merge
5052 5039 before pushing.
5053 5040
5054 5041 Use --new-branch if you want to allow push to create a new named
5055 5042 branch that is not present at the destination. This allows you to
5056 5043 only create a new branch without forcing other changes.
5057 5044
5058 5045 .. note::
5059 5046
5060 5047 Extra care should be taken with the -f/--force option,
5061 5048 which will push all new heads on all branches, an action which will
5062 5049 almost always cause confusion for collaborators.
5063 5050
5064 5051 If -r/--rev is used, the specified revision and all its ancestors
5065 5052 will be pushed to the remote repository.
5066 5053
5067 5054 If -B/--bookmark is used, the specified bookmarked revision, its
5068 5055 ancestors, and the bookmark will be pushed to the remote
5069 5056 repository. Specifying ``.`` is equivalent to specifying the active
5070 5057 bookmark's name.
5071 5058
5072 5059 Please see :hg:`help urls` for important details about ``ssh://``
5073 5060 URLs. If DESTINATION is omitted, a default path will be used.
5074 5061
5075 5062 Returns 0 if push was successful, 1 if nothing to push.
5076 5063 """
5077 5064
5078 5065 if opts.get('bookmark'):
5079 5066 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5080 5067 for b in opts['bookmark']:
5081 5068 # translate -B options to -r so changesets get pushed
5082 5069 b = repo._bookmarks.expandname(b)
5083 5070 if b in repo._bookmarks:
5084 5071 opts.setdefault('rev', []).append(b)
5085 5072 else:
5086 5073 # if we try to push a deleted bookmark, translate it to null
5087 5074 # this lets simultaneous -r, -b options continue working
5088 5075 opts.setdefault('rev', []).append("null")
5089 5076
5090 5077 path = ui.paths.getpath(dest, default=('default-push', 'default'))
5091 5078 if not path:
5092 5079 raise error.Abort(_('default repository not configured!'),
5093 5080 hint=_("see 'hg help config.paths'"))
5094 5081 dest = path.pushloc or path.loc
5095 5082 branches = (path.branch, opts.get('branch') or [])
5096 5083 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5097 5084 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5098 5085 other = hg.peer(repo, opts, dest)
5099 5086
5100 5087 if revs:
5101 5088 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5102 5089 if not revs:
5103 5090 raise error.Abort(_("specified revisions evaluate to an empty set"),
5104 5091 hint=_("use different revision arguments"))
5105 5092 elif path.pushrev:
5106 5093 # It doesn't make any sense to specify ancestor revisions. So limit
5107 5094 # to DAG heads to make discovery simpler.
5108 5095 expr = revset.formatspec('heads(%r)', path.pushrev)
5109 5096 revs = scmutil.revrange(repo, [expr])
5110 5097 revs = [repo[rev].node() for rev in revs]
5111 5098 if not revs:
5112 5099 raise error.Abort(_('default push revset for path evaluates to an '
5113 5100 'empty set'))
5114 5101
5115 5102 repo._subtoppath = dest
5116 5103 try:
5117 5104 # push subrepos depth-first for coherent ordering
5118 5105 c = repo['']
5119 5106 subs = c.substate # only repos that are committed
5120 5107 for s in sorted(subs):
5121 5108 result = c.sub(s).push(opts)
5122 5109 if result == 0:
5123 5110 return not result
5124 5111 finally:
5125 5112 del repo._subtoppath
5126 5113 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5127 5114 newbranch=opts.get('new_branch'),
5128 5115 bookmarks=opts.get('bookmark', ()),
5129 5116 opargs=opts.get('opargs'))
5130 5117
5131 5118 result = not pushop.cgresult
5132 5119
5133 5120 if pushop.bkresult is not None:
5134 5121 if pushop.bkresult == 2:
5135 5122 result = 2
5136 5123 elif not result and pushop.bkresult:
5137 5124 result = 2
5138 5125
5139 5126 return result
5140 5127
5141 5128 @command('recover', [])
5142 5129 def recover(ui, repo):
5143 5130 """roll back an interrupted transaction
5144 5131
5145 5132 Recover from an interrupted commit or pull.
5146 5133
5147 5134 This command tries to fix the repository status after an
5148 5135 interrupted operation. It should only be necessary when Mercurial
5149 5136 suggests it.
5150 5137
5151 5138 Returns 0 if successful, 1 if nothing to recover or verify fails.
5152 5139 """
5153 5140 if repo.recover():
5154 5141 return hg.verify(repo)
5155 5142 return 1
5156 5143
5157 5144 @command('^remove|rm',
5158 5145 [('A', 'after', None, _('record delete for missing files')),
5159 5146 ('f', 'force', None,
5160 5147 _('forget added files, delete modified files')),
5161 5148 ] + subrepoopts + walkopts,
5162 5149 _('[OPTION]... FILE...'),
5163 5150 inferrepo=True)
5164 5151 def remove(ui, repo, *pats, **opts):
5165 5152 """remove the specified files on the next commit
5166 5153
5167 5154 Schedule the indicated files for removal from the current branch.
5168 5155
5169 5156 This command schedules the files to be removed at the next commit.
5170 5157 To undo a remove before that, see :hg:`revert`. To undo added
5171 5158 files, see :hg:`forget`.
5172 5159
5173 5160 .. container:: verbose
5174 5161
5175 5162 -A/--after can be used to remove only files that have already
5176 5163 been deleted, -f/--force can be used to force deletion, and -Af
5177 5164 can be used to remove files from the next revision without
5178 5165 deleting them from the working directory.
5179 5166
5180 5167 The following table details the behavior of remove for different
5181 5168 file states (columns) and option combinations (rows). The file
5182 5169 states are Added [A], Clean [C], Modified [M] and Missing [!]
5183 5170 (as reported by :hg:`status`). The actions are Warn, Remove
5184 5171 (from branch) and Delete (from disk):
5185 5172
5186 5173 ========= == == == ==
5187 5174 opt/state A C M !
5188 5175 ========= == == == ==
5189 5176 none W RD W R
5190 5177 -f R RD RD R
5191 5178 -A W W W R
5192 5179 -Af R R R R
5193 5180 ========= == == == ==
5194 5181
5195 5182 .. note::
5196 5183
5197 5184 :hg:`remove` never deletes files in Added [A] state from the
5198 5185 working directory, not even if ``--force`` is specified.
5199 5186
5200 5187 Returns 0 on success, 1 if any warnings encountered.
5201 5188 """
5202 5189
5203 5190 after, force = opts.get('after'), opts.get('force')
5204 5191 if not pats and not after:
5205 5192 raise error.Abort(_('no files specified'))
5206 5193
5207 5194 m = scmutil.match(repo[None], pats, opts)
5208 5195 subrepos = opts.get('subrepos')
5209 5196 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5210 5197
5211 5198 @command('rename|move|mv',
5212 5199 [('A', 'after', None, _('record a rename that has already occurred')),
5213 5200 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5214 5201 ] + walkopts + dryrunopts,
5215 5202 _('[OPTION]... SOURCE... DEST'))
5216 5203 def rename(ui, repo, *pats, **opts):
5217 5204 """rename files; equivalent of copy + remove
5218 5205
5219 5206 Mark dest as copies of sources; mark sources for deletion. If dest
5220 5207 is a directory, copies are put in that directory. If dest is a
5221 5208 file, there can only be one source.
5222 5209
5223 5210 By default, this command copies the contents of files as they
5224 5211 exist in the working directory. If invoked with -A/--after, the
5225 5212 operation is recorded, but no copying is performed.
5226 5213
5227 5214 This command takes effect at the next commit. To undo a rename
5228 5215 before that, see :hg:`revert`.
5229 5216
5230 5217 Returns 0 on success, 1 if errors are encountered.
5231 5218 """
5232 5219 with repo.wlock(False):
5233 5220 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5234 5221
5235 5222 @command('resolve',
5236 5223 [('a', 'all', None, _('select all unresolved files')),
5237 5224 ('l', 'list', None, _('list state of files needing merge')),
5238 5225 ('m', 'mark', None, _('mark files as resolved')),
5239 5226 ('u', 'unmark', None, _('mark files as unresolved')),
5240 5227 ('n', 'no-status', None, _('hide status prefix'))]
5241 5228 + mergetoolopts + walkopts + formatteropts,
5242 5229 _('[OPTION]... [FILE]...'),
5243 5230 inferrepo=True)
5244 5231 def resolve(ui, repo, *pats, **opts):
5245 5232 """redo merges or set/view the merge status of files
5246 5233
5247 5234 Merges with unresolved conflicts are often the result of
5248 5235 non-interactive merging using the ``internal:merge`` configuration
5249 5236 setting, or a command-line merge tool like ``diff3``. The resolve
5250 5237 command is used to manage the files involved in a merge, after
5251 5238 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5252 5239 working directory must have two parents). See :hg:`help
5253 5240 merge-tools` for information on configuring merge tools.
5254 5241
5255 5242 The resolve command can be used in the following ways:
5256 5243
5257 5244 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5258 5245 files, discarding any previous merge attempts. Re-merging is not
5259 5246 performed for files already marked as resolved. Use ``--all/-a``
5260 5247 to select all unresolved files. ``--tool`` can be used to specify
5261 5248 the merge tool used for the given files. It overrides the HGMERGE
5262 5249 environment variable and your configuration files. Previous file
5263 5250 contents are saved with a ``.orig`` suffix.
5264 5251
5265 5252 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5266 5253 (e.g. after having manually fixed-up the files). The default is
5267 5254 to mark all unresolved files.
5268 5255
5269 5256 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5270 5257 default is to mark all resolved files.
5271 5258
5272 5259 - :hg:`resolve -l`: list files which had or still have conflicts.
5273 5260 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5274 5261
5275 5262 .. note::
5276 5263
5277 5264 Mercurial will not let you commit files with unresolved merge
5278 5265 conflicts. You must use :hg:`resolve -m ...` before you can
5279 5266 commit after a conflicting merge.
5280 5267
5281 5268 Returns 0 on success, 1 if any files fail a resolve attempt.
5282 5269 """
5283 5270
5284 5271 flaglist = 'all mark unmark list no_status'.split()
5285 5272 all, mark, unmark, show, nostatus = \
5286 5273 [opts.get(o) for o in flaglist]
5287 5274
5288 5275 if (show and (mark or unmark)) or (mark and unmark):
5289 5276 raise error.Abort(_("too many options specified"))
5290 5277 if pats and all:
5291 5278 raise error.Abort(_("can't specify --all and patterns"))
5292 5279 if not (all or pats or show or mark or unmark):
5293 5280 raise error.Abort(_('no files or directories specified'),
5294 5281 hint=('use --all to re-merge all unresolved files'))
5295 5282
5296 5283 if show:
5297 5284 fm = ui.formatter('resolve', opts)
5298 5285 ms = mergemod.mergestate.read(repo)
5299 5286 m = scmutil.match(repo[None], pats, opts)
5300 5287 for f in ms:
5301 5288 if not m(f):
5302 5289 continue
5303 5290 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
5304 5291 'd': 'driverresolved'}[ms[f]]
5305 5292 fm.startitem()
5306 5293 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
5307 5294 fm.write('path', '%s\n', f, label=l)
5308 5295 fm.end()
5309 5296 return 0
5310 5297
5311 5298 with repo.wlock():
5312 5299 ms = mergemod.mergestate.read(repo)
5313 5300
5314 5301 if not (ms.active() or repo.dirstate.p2() != nullid):
5315 5302 raise error.Abort(
5316 5303 _('resolve command not applicable when not merging'))
5317 5304
5318 5305 wctx = repo[None]
5319 5306
5320 5307 if ms.mergedriver and ms.mdstate() == 'u':
5321 5308 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5322 5309 ms.commit()
5323 5310 # allow mark and unmark to go through
5324 5311 if not mark and not unmark and not proceed:
5325 5312 return 1
5326 5313
5327 5314 m = scmutil.match(wctx, pats, opts)
5328 5315 ret = 0
5329 5316 didwork = False
5330 5317 runconclude = False
5331 5318
5332 5319 tocomplete = []
5333 5320 for f in ms:
5334 5321 if not m(f):
5335 5322 continue
5336 5323
5337 5324 didwork = True
5338 5325
5339 5326 # don't let driver-resolved files be marked, and run the conclude
5340 5327 # step if asked to resolve
5341 5328 if ms[f] == "d":
5342 5329 exact = m.exact(f)
5343 5330 if mark:
5344 5331 if exact:
5345 5332 ui.warn(_('not marking %s as it is driver-resolved\n')
5346 5333 % f)
5347 5334 elif unmark:
5348 5335 if exact:
5349 5336 ui.warn(_('not unmarking %s as it is driver-resolved\n')
5350 5337 % f)
5351 5338 else:
5352 5339 runconclude = True
5353 5340 continue
5354 5341
5355 5342 if mark:
5356 5343 ms.mark(f, "r")
5357 5344 elif unmark:
5358 5345 ms.mark(f, "u")
5359 5346 else:
5360 5347 # backup pre-resolve (merge uses .orig for its own purposes)
5361 5348 a = repo.wjoin(f)
5362 5349 try:
5363 5350 util.copyfile(a, a + ".resolve")
5364 5351 except (IOError, OSError) as inst:
5365 5352 if inst.errno != errno.ENOENT:
5366 5353 raise
5367 5354
5368 5355 try:
5369 5356 # preresolve file
5370 5357 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5371 5358 'resolve')
5372 5359 complete, r = ms.preresolve(f, wctx)
5373 5360 if not complete:
5374 5361 tocomplete.append(f)
5375 5362 elif r:
5376 5363 ret = 1
5377 5364 finally:
5378 5365 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5379 5366 ms.commit()
5380 5367
5381 5368 # replace filemerge's .orig file with our resolve file, but only
5382 5369 # for merges that are complete
5383 5370 if complete:
5384 5371 try:
5385 5372 util.rename(a + ".resolve",
5386 5373 scmutil.origpath(ui, repo, a))
5387 5374 except OSError as inst:
5388 5375 if inst.errno != errno.ENOENT:
5389 5376 raise
5390 5377
5391 5378 for f in tocomplete:
5392 5379 try:
5393 5380 # resolve file
5394 5381 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5395 5382 'resolve')
5396 5383 r = ms.resolve(f, wctx)
5397 5384 if r:
5398 5385 ret = 1
5399 5386 finally:
5400 5387 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5401 5388 ms.commit()
5402 5389
5403 5390 # replace filemerge's .orig file with our resolve file
5404 5391 a = repo.wjoin(f)
5405 5392 try:
5406 5393 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
5407 5394 except OSError as inst:
5408 5395 if inst.errno != errno.ENOENT:
5409 5396 raise
5410 5397
5411 5398 ms.commit()
5412 5399 ms.recordactions()
5413 5400
5414 5401 if not didwork and pats:
5415 5402 hint = None
5416 5403 if not any([p for p in pats if p.find(':') >= 0]):
5417 5404 pats = ['path:%s' % p for p in pats]
5418 5405 m = scmutil.match(wctx, pats, opts)
5419 5406 for f in ms:
5420 5407 if not m(f):
5421 5408 continue
5422 5409 flags = ''.join(['-%s ' % o[0] for o in flaglist
5423 5410 if opts.get(o)])
5424 5411 hint = _("(try: hg resolve %s%s)\n") % (
5425 5412 flags,
5426 5413 ' '.join(pats))
5427 5414 break
5428 5415 ui.warn(_("arguments do not match paths that need resolving\n"))
5429 5416 if hint:
5430 5417 ui.warn(hint)
5431 5418 elif ms.mergedriver and ms.mdstate() != 's':
5432 5419 # run conclude step when either a driver-resolved file is requested
5433 5420 # or there are no driver-resolved files
5434 5421 # we can't use 'ret' to determine whether any files are unresolved
5435 5422 # because we might not have tried to resolve some
5436 5423 if ((runconclude or not list(ms.driverresolved()))
5437 5424 and not list(ms.unresolved())):
5438 5425 proceed = mergemod.driverconclude(repo, ms, wctx)
5439 5426 ms.commit()
5440 5427 if not proceed:
5441 5428 return 1
5442 5429
5443 5430 # Nudge users into finishing an unfinished operation
5444 5431 unresolvedf = list(ms.unresolved())
5445 5432 driverresolvedf = list(ms.driverresolved())
5446 5433 if not unresolvedf and not driverresolvedf:
5447 5434 ui.status(_('(no more unresolved files)\n'))
5448 5435 cmdutil.checkafterresolved(repo)
5449 5436 elif not unresolvedf:
5450 5437 ui.status(_('(no more unresolved files -- '
5451 5438 'run "hg resolve --all" to conclude)\n'))
5452 5439
5453 5440 return ret
5454 5441
5455 5442 @command('revert',
5456 5443 [('a', 'all', None, _('revert all changes when no arguments given')),
5457 5444 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5458 5445 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5459 5446 ('C', 'no-backup', None, _('do not save backup copies of files')),
5460 5447 ('i', 'interactive', None,
5461 5448 _('interactively select the changes (EXPERIMENTAL)')),
5462 5449 ] + walkopts + dryrunopts,
5463 5450 _('[OPTION]... [-r REV] [NAME]...'))
5464 5451 def revert(ui, repo, *pats, **opts):
5465 5452 """restore files to their checkout state
5466 5453
5467 5454 .. note::
5468 5455
5469 5456 To check out earlier revisions, you should use :hg:`update REV`.
5470 5457 To cancel an uncommitted merge (and lose your changes),
5471 5458 use :hg:`update --clean .`.
5472 5459
5473 5460 With no revision specified, revert the specified files or directories
5474 5461 to the contents they had in the parent of the working directory.
5475 5462 This restores the contents of files to an unmodified
5476 5463 state and unschedules adds, removes, copies, and renames. If the
5477 5464 working directory has two parents, you must explicitly specify a
5478 5465 revision.
5479 5466
5480 5467 Using the -r/--rev or -d/--date options, revert the given files or
5481 5468 directories to their states as of a specific revision. Because
5482 5469 revert does not change the working directory parents, this will
5483 5470 cause these files to appear modified. This can be helpful to "back
5484 5471 out" some or all of an earlier change. See :hg:`backout` for a
5485 5472 related method.
5486 5473
5487 5474 Modified files are saved with a .orig suffix before reverting.
5488 5475 To disable these backups, use --no-backup. It is possible to store
5489 5476 the backup files in a custom directory relative to the root of the
5490 5477 repository by setting the ``ui.origbackuppath`` configuration
5491 5478 option.
5492 5479
5493 5480 See :hg:`help dates` for a list of formats valid for -d/--date.
5494 5481
5495 5482 See :hg:`help backout` for a way to reverse the effect of an
5496 5483 earlier changeset.
5497 5484
5498 5485 Returns 0 on success.
5499 5486 """
5500 5487
5501 5488 if opts.get("date"):
5502 5489 if opts.get("rev"):
5503 5490 raise error.Abort(_("you can't specify a revision and a date"))
5504 5491 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5505 5492
5506 5493 parent, p2 = repo.dirstate.parents()
5507 5494 if not opts.get('rev') and p2 != nullid:
5508 5495 # revert after merge is a trap for new users (issue2915)
5509 5496 raise error.Abort(_('uncommitted merge with no revision specified'),
5510 5497 hint=_("use 'hg update' or see 'hg help revert'"))
5511 5498
5512 5499 ctx = scmutil.revsingle(repo, opts.get('rev'))
5513 5500
5514 5501 if (not (pats or opts.get('include') or opts.get('exclude') or
5515 5502 opts.get('all') or opts.get('interactive'))):
5516 5503 msg = _("no files or directories specified")
5517 5504 if p2 != nullid:
5518 5505 hint = _("uncommitted merge, use --all to discard all changes,"
5519 5506 " or 'hg update -C .' to abort the merge")
5520 5507 raise error.Abort(msg, hint=hint)
5521 5508 dirty = any(repo.status())
5522 5509 node = ctx.node()
5523 5510 if node != parent:
5524 5511 if dirty:
5525 5512 hint = _("uncommitted changes, use --all to discard all"
5526 5513 " changes, or 'hg update %s' to update") % ctx.rev()
5527 5514 else:
5528 5515 hint = _("use --all to revert all files,"
5529 5516 " or 'hg update %s' to update") % ctx.rev()
5530 5517 elif dirty:
5531 5518 hint = _("uncommitted changes, use --all to discard all changes")
5532 5519 else:
5533 5520 hint = _("use --all to revert all files")
5534 5521 raise error.Abort(msg, hint=hint)
5535 5522
5536 5523 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5537 5524
5538 5525 @command('rollback', dryrunopts +
5539 5526 [('f', 'force', False, _('ignore safety measures'))])
5540 5527 def rollback(ui, repo, **opts):
5541 5528 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5542 5529
5543 5530 Please use :hg:`commit --amend` instead of rollback to correct
5544 5531 mistakes in the last commit.
5545 5532
5546 5533 This command should be used with care. There is only one level of
5547 5534 rollback, and there is no way to undo a rollback. It will also
5548 5535 restore the dirstate at the time of the last transaction, losing
5549 5536 any dirstate changes since that time. This command does not alter
5550 5537 the working directory.
5551 5538
5552 5539 Transactions are used to encapsulate the effects of all commands
5553 5540 that create new changesets or propagate existing changesets into a
5554 5541 repository.
5555 5542
5556 5543 .. container:: verbose
5557 5544
5558 5545 For example, the following commands are transactional, and their
5559 5546 effects can be rolled back:
5560 5547
5561 5548 - commit
5562 5549 - import
5563 5550 - pull
5564 5551 - push (with this repository as the destination)
5565 5552 - unbundle
5566 5553
5567 5554 To avoid permanent data loss, rollback will refuse to rollback a
5568 5555 commit transaction if it isn't checked out. Use --force to
5569 5556 override this protection.
5570 5557
5571 5558 The rollback command can be entirely disabled by setting the
5572 5559 ``ui.rollback`` configuration setting to false. If you're here
5573 5560 because you want to use rollback and it's disabled, you can
5574 5561 re-enable the command by setting ``ui.rollback`` to true.
5575 5562
5576 5563 This command is not intended for use on public repositories. Once
5577 5564 changes are visible for pull by other users, rolling a transaction
5578 5565 back locally is ineffective (someone else may already have pulled
5579 5566 the changes). Furthermore, a race is possible with readers of the
5580 5567 repository; for example an in-progress pull from the repository
5581 5568 may fail if a rollback is performed.
5582 5569
5583 5570 Returns 0 on success, 1 if no rollback data is available.
5584 5571 """
5585 5572 if not ui.configbool('ui', 'rollback', True):
5586 5573 raise error.Abort(_('rollback is disabled because it is unsafe'),
5587 5574 hint=('see `hg help -v rollback` for information'))
5588 5575 return repo.rollback(dryrun=opts.get('dry_run'),
5589 5576 force=opts.get('force'))
5590 5577
5591 5578 @command('root', [])
5592 5579 def root(ui, repo):
5593 5580 """print the root (top) of the current working directory
5594 5581
5595 5582 Print the root directory of the current repository.
5596 5583
5597 5584 Returns 0 on success.
5598 5585 """
5599 5586 ui.write(repo.root + "\n")
5600 5587
5601 5588 @command('^serve',
5602 5589 [('A', 'accesslog', '', _('name of access log file to write to'),
5603 5590 _('FILE')),
5604 5591 ('d', 'daemon', None, _('run server in background')),
5605 5592 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5606 5593 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5607 5594 # use string type, then we can check if something was passed
5608 5595 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5609 5596 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5610 5597 _('ADDR')),
5611 5598 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5612 5599 _('PREFIX')),
5613 5600 ('n', 'name', '',
5614 5601 _('name to show in web pages (default: working directory)'), _('NAME')),
5615 5602 ('', 'web-conf', '',
5616 5603 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5617 5604 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5618 5605 _('FILE')),
5619 5606 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5620 5607 ('', 'stdio', None, _('for remote clients')),
5621 5608 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5622 5609 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5623 5610 ('', 'style', '', _('template style to use'), _('STYLE')),
5624 5611 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5625 5612 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5626 5613 _('[OPTION]...'),
5627 5614 optionalrepo=True)
5628 5615 def serve(ui, repo, **opts):
5629 5616 """start stand-alone webserver
5630 5617
5631 5618 Start a local HTTP repository browser and pull server. You can use
5632 5619 this for ad-hoc sharing and browsing of repositories. It is
5633 5620 recommended to use a real web server to serve a repository for
5634 5621 longer periods of time.
5635 5622
5636 5623 Please note that the server does not implement access control.
5637 5624 This means that, by default, anybody can read from the server and
5638 5625 nobody can write to it by default. Set the ``web.allow_push``
5639 5626 option to ``*`` to allow everybody to push to the server. You
5640 5627 should use a real web server if you need to authenticate users.
5641 5628
5642 5629 By default, the server logs accesses to stdout and errors to
5643 5630 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5644 5631 files.
5645 5632
5646 5633 To have the server choose a free port number to listen on, specify
5647 5634 a port number of 0; in this case, the server will print the port
5648 5635 number it uses.
5649 5636
5650 5637 Returns 0 on success.
5651 5638 """
5652 5639
5653 5640 if opts["stdio"] and opts["cmdserver"]:
5654 5641 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5655 5642
5656 5643 if opts["stdio"]:
5657 5644 if repo is None:
5658 5645 raise error.RepoError(_("there is no Mercurial repository here"
5659 5646 " (.hg not found)"))
5660 5647 s = sshserver.sshserver(ui, repo)
5661 5648 s.serve_forever()
5662 5649
5663 5650 service = server.createservice(ui, repo, opts)
5664 5651 return server.runservice(opts, initfn=service.init, runfn=service.run)
5665 5652
5666 5653 @command('^status|st',
5667 5654 [('A', 'all', None, _('show status of all files')),
5668 5655 ('m', 'modified', None, _('show only modified files')),
5669 5656 ('a', 'added', None, _('show only added files')),
5670 5657 ('r', 'removed', None, _('show only removed files')),
5671 5658 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5672 5659 ('c', 'clean', None, _('show only files without changes')),
5673 5660 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5674 5661 ('i', 'ignored', None, _('show only ignored files')),
5675 5662 ('n', 'no-status', None, _('hide status prefix')),
5676 5663 ('C', 'copies', None, _('show source of copied files')),
5677 5664 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5678 5665 ('', 'rev', [], _('show difference from revision'), _('REV')),
5679 5666 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5680 5667 ] + walkopts + subrepoopts + formatteropts,
5681 5668 _('[OPTION]... [FILE]...'),
5682 5669 inferrepo=True)
5683 5670 def status(ui, repo, *pats, **opts):
5684 5671 """show changed files in the working directory
5685 5672
5686 5673 Show status of files in the repository. If names are given, only
5687 5674 files that match are shown. Files that are clean or ignored or
5688 5675 the source of a copy/move operation, are not listed unless
5689 5676 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5690 5677 Unless options described with "show only ..." are given, the
5691 5678 options -mardu are used.
5692 5679
5693 5680 Option -q/--quiet hides untracked (unknown and ignored) files
5694 5681 unless explicitly requested with -u/--unknown or -i/--ignored.
5695 5682
5696 5683 .. note::
5697 5684
5698 5685 :hg:`status` may appear to disagree with diff if permissions have
5699 5686 changed or a merge has occurred. The standard diff format does
5700 5687 not report permission changes and diff only reports changes
5701 5688 relative to one merge parent.
5702 5689
5703 5690 If one revision is given, it is used as the base revision.
5704 5691 If two revisions are given, the differences between them are
5705 5692 shown. The --change option can also be used as a shortcut to list
5706 5693 the changed files of a revision from its first parent.
5707 5694
5708 5695 The codes used to show the status of files are::
5709 5696
5710 5697 M = modified
5711 5698 A = added
5712 5699 R = removed
5713 5700 C = clean
5714 5701 ! = missing (deleted by non-hg command, but still tracked)
5715 5702 ? = not tracked
5716 5703 I = ignored
5717 5704 = origin of the previous file (with --copies)
5718 5705
5719 5706 .. container:: verbose
5720 5707
5721 5708 Examples:
5722 5709
5723 5710 - show changes in the working directory relative to a
5724 5711 changeset::
5725 5712
5726 5713 hg status --rev 9353
5727 5714
5728 5715 - show changes in the working directory relative to the
5729 5716 current directory (see :hg:`help patterns` for more information)::
5730 5717
5731 5718 hg status re:
5732 5719
5733 5720 - show all changes including copies in an existing changeset::
5734 5721
5735 5722 hg status --copies --change 9353
5736 5723
5737 5724 - get a NUL separated list of added files, suitable for xargs::
5738 5725
5739 5726 hg status -an0
5740 5727
5741 5728 Returns 0 on success.
5742 5729 """
5743 5730
5744 5731 revs = opts.get('rev')
5745 5732 change = opts.get('change')
5746 5733
5747 5734 if revs and change:
5748 5735 msg = _('cannot specify --rev and --change at the same time')
5749 5736 raise error.Abort(msg)
5750 5737 elif change:
5751 5738 node2 = scmutil.revsingle(repo, change, None).node()
5752 5739 node1 = repo[node2].p1().node()
5753 5740 else:
5754 5741 node1, node2 = scmutil.revpair(repo, revs)
5755 5742
5756 5743 if pats:
5757 5744 cwd = repo.getcwd()
5758 5745 else:
5759 5746 cwd = ''
5760 5747
5761 5748 if opts.get('print0'):
5762 5749 end = '\0'
5763 5750 else:
5764 5751 end = '\n'
5765 5752 copy = {}
5766 5753 states = 'modified added removed deleted unknown ignored clean'.split()
5767 5754 show = [k for k in states if opts.get(k)]
5768 5755 if opts.get('all'):
5769 5756 show += ui.quiet and (states[:4] + ['clean']) or states
5770 5757 if not show:
5771 5758 if ui.quiet:
5772 5759 show = states[:4]
5773 5760 else:
5774 5761 show = states[:5]
5775 5762
5776 5763 m = scmutil.match(repo[node2], pats, opts)
5777 5764 stat = repo.status(node1, node2, m,
5778 5765 'ignored' in show, 'clean' in show, 'unknown' in show,
5779 5766 opts.get('subrepos'))
5780 5767 changestates = zip(states, 'MAR!?IC', stat)
5781 5768
5782 5769 if (opts.get('all') or opts.get('copies')
5783 5770 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5784 5771 copy = copies.pathcopies(repo[node1], repo[node2], m)
5785 5772
5786 5773 fm = ui.formatter('status', opts)
5787 5774 fmt = '%s' + end
5788 5775 showchar = not opts.get('no_status')
5789 5776
5790 5777 for state, char, files in changestates:
5791 5778 if state in show:
5792 5779 label = 'status.' + state
5793 5780 for f in files:
5794 5781 fm.startitem()
5795 5782 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5796 5783 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5797 5784 if f in copy:
5798 5785 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5799 5786 label='status.copied')
5800 5787 fm.end()
5801 5788
5802 5789 @command('^summary|sum',
5803 5790 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5804 5791 def summary(ui, repo, **opts):
5805 5792 """summarize working directory state
5806 5793
5807 5794 This generates a brief summary of the working directory state,
5808 5795 including parents, branch, commit status, phase and available updates.
5809 5796
5810 5797 With the --remote option, this will check the default paths for
5811 5798 incoming and outgoing changes. This can be time-consuming.
5812 5799
5813 5800 Returns 0 on success.
5814 5801 """
5815 5802
5816 5803 ctx = repo[None]
5817 5804 parents = ctx.parents()
5818 5805 pnode = parents[0].node()
5819 5806 marks = []
5820 5807
5821 5808 ms = None
5822 5809 try:
5823 5810 ms = mergemod.mergestate.read(repo)
5824 5811 except error.UnsupportedMergeRecords as e:
5825 5812 s = ' '.join(e.recordtypes)
5826 5813 ui.warn(
5827 5814 _('warning: merge state has unsupported record types: %s\n') % s)
5828 5815 unresolved = 0
5829 5816 else:
5830 5817 unresolved = [f for f in ms if ms[f] == 'u']
5831 5818
5832 5819 for p in parents:
5833 5820 # label with log.changeset (instead of log.parent) since this
5834 5821 # shows a working directory parent *changeset*:
5835 5822 # i18n: column positioning for "hg summary"
5836 5823 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5837 5824 label=cmdutil._changesetlabels(p))
5838 5825 ui.write(' '.join(p.tags()), label='log.tag')
5839 5826 if p.bookmarks():
5840 5827 marks.extend(p.bookmarks())
5841 5828 if p.rev() == -1:
5842 5829 if not len(repo):
5843 5830 ui.write(_(' (empty repository)'))
5844 5831 else:
5845 5832 ui.write(_(' (no revision checked out)'))
5846 5833 if p.troubled():
5847 5834 ui.write(' ('
5848 5835 + ', '.join(ui.label(trouble, 'trouble.%s' % trouble)
5849 5836 for trouble in p.troubles())
5850 5837 + ')')
5851 5838 ui.write('\n')
5852 5839 if p.description():
5853 5840 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5854 5841 label='log.summary')
5855 5842
5856 5843 branch = ctx.branch()
5857 5844 bheads = repo.branchheads(branch)
5858 5845 # i18n: column positioning for "hg summary"
5859 5846 m = _('branch: %s\n') % branch
5860 5847 if branch != 'default':
5861 5848 ui.write(m, label='log.branch')
5862 5849 else:
5863 5850 ui.status(m, label='log.branch')
5864 5851
5865 5852 if marks:
5866 5853 active = repo._activebookmark
5867 5854 # i18n: column positioning for "hg summary"
5868 5855 ui.write(_('bookmarks:'), label='log.bookmark')
5869 5856 if active is not None:
5870 5857 if active in marks:
5871 5858 ui.write(' *' + active, label=activebookmarklabel)
5872 5859 marks.remove(active)
5873 5860 else:
5874 5861 ui.write(' [%s]' % active, label=activebookmarklabel)
5875 5862 for m in marks:
5876 5863 ui.write(' ' + m, label='log.bookmark')
5877 5864 ui.write('\n', label='log.bookmark')
5878 5865
5879 5866 status = repo.status(unknown=True)
5880 5867
5881 5868 c = repo.dirstate.copies()
5882 5869 copied, renamed = [], []
5883 5870 for d, s in c.iteritems():
5884 5871 if s in status.removed:
5885 5872 status.removed.remove(s)
5886 5873 renamed.append(d)
5887 5874 else:
5888 5875 copied.append(d)
5889 5876 if d in status.added:
5890 5877 status.added.remove(d)
5891 5878
5892 5879 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5893 5880
5894 5881 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5895 5882 (ui.label(_('%d added'), 'status.added'), status.added),
5896 5883 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5897 5884 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5898 5885 (ui.label(_('%d copied'), 'status.copied'), copied),
5899 5886 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5900 5887 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5901 5888 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5902 5889 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5903 5890 t = []
5904 5891 for l, s in labels:
5905 5892 if s:
5906 5893 t.append(l % len(s))
5907 5894
5908 5895 t = ', '.join(t)
5909 5896 cleanworkdir = False
5910 5897
5911 5898 if repo.vfs.exists('graftstate'):
5912 5899 t += _(' (graft in progress)')
5913 5900 if repo.vfs.exists('updatestate'):
5914 5901 t += _(' (interrupted update)')
5915 5902 elif len(parents) > 1:
5916 5903 t += _(' (merge)')
5917 5904 elif branch != parents[0].branch():
5918 5905 t += _(' (new branch)')
5919 5906 elif (parents[0].closesbranch() and
5920 5907 pnode in repo.branchheads(branch, closed=True)):
5921 5908 t += _(' (head closed)')
5922 5909 elif not (status.modified or status.added or status.removed or renamed or
5923 5910 copied or subs):
5924 5911 t += _(' (clean)')
5925 5912 cleanworkdir = True
5926 5913 elif pnode not in bheads:
5927 5914 t += _(' (new branch head)')
5928 5915
5929 5916 if parents:
5930 5917 pendingphase = max(p.phase() for p in parents)
5931 5918 else:
5932 5919 pendingphase = phases.public
5933 5920
5934 5921 if pendingphase > phases.newcommitphase(ui):
5935 5922 t += ' (%s)' % phases.phasenames[pendingphase]
5936 5923
5937 5924 if cleanworkdir:
5938 5925 # i18n: column positioning for "hg summary"
5939 5926 ui.status(_('commit: %s\n') % t.strip())
5940 5927 else:
5941 5928 # i18n: column positioning for "hg summary"
5942 5929 ui.write(_('commit: %s\n') % t.strip())
5943 5930
5944 5931 # all ancestors of branch heads - all ancestors of parent = new csets
5945 5932 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5946 5933 bheads))
5947 5934
5948 5935 if new == 0:
5949 5936 # i18n: column positioning for "hg summary"
5950 5937 ui.status(_('update: (current)\n'))
5951 5938 elif pnode not in bheads:
5952 5939 # i18n: column positioning for "hg summary"
5953 5940 ui.write(_('update: %d new changesets (update)\n') % new)
5954 5941 else:
5955 5942 # i18n: column positioning for "hg summary"
5956 5943 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5957 5944 (new, len(bheads)))
5958 5945
5959 5946 t = []
5960 5947 draft = len(repo.revs('draft()'))
5961 5948 if draft:
5962 5949 t.append(_('%d draft') % draft)
5963 5950 secret = len(repo.revs('secret()'))
5964 5951 if secret:
5965 5952 t.append(_('%d secret') % secret)
5966 5953
5967 5954 if draft or secret:
5968 5955 ui.status(_('phases: %s\n') % ', '.join(t))
5969 5956
5970 5957 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5971 5958 for trouble in ("unstable", "divergent", "bumped"):
5972 5959 numtrouble = len(repo.revs(trouble + "()"))
5973 5960 # We write all the possibilities to ease translation
5974 5961 troublemsg = {
5975 5962 "unstable": _("unstable: %d changesets"),
5976 5963 "divergent": _("divergent: %d changesets"),
5977 5964 "bumped": _("bumped: %d changesets"),
5978 5965 }
5979 5966 if numtrouble > 0:
5980 5967 ui.status(troublemsg[trouble] % numtrouble + "\n")
5981 5968
5982 5969 cmdutil.summaryhooks(ui, repo)
5983 5970
5984 5971 if opts.get('remote'):
5985 5972 needsincoming, needsoutgoing = True, True
5986 5973 else:
5987 5974 needsincoming, needsoutgoing = False, False
5988 5975 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5989 5976 if i:
5990 5977 needsincoming = True
5991 5978 if o:
5992 5979 needsoutgoing = True
5993 5980 if not needsincoming and not needsoutgoing:
5994 5981 return
5995 5982
5996 5983 def getincoming():
5997 5984 source, branches = hg.parseurl(ui.expandpath('default'))
5998 5985 sbranch = branches[0]
5999 5986 try:
6000 5987 other = hg.peer(repo, {}, source)
6001 5988 except error.RepoError:
6002 5989 if opts.get('remote'):
6003 5990 raise
6004 5991 return source, sbranch, None, None, None
6005 5992 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6006 5993 if revs:
6007 5994 revs = [other.lookup(rev) for rev in revs]
6008 5995 ui.debug('comparing with %s\n' % util.hidepassword(source))
6009 5996 repo.ui.pushbuffer()
6010 5997 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6011 5998 repo.ui.popbuffer()
6012 5999 return source, sbranch, other, commoninc, commoninc[1]
6013 6000
6014 6001 if needsincoming:
6015 6002 source, sbranch, sother, commoninc, incoming = getincoming()
6016 6003 else:
6017 6004 source = sbranch = sother = commoninc = incoming = None
6018 6005
6019 6006 def getoutgoing():
6020 6007 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
6021 6008 dbranch = branches[0]
6022 6009 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
6023 6010 if source != dest:
6024 6011 try:
6025 6012 dother = hg.peer(repo, {}, dest)
6026 6013 except error.RepoError:
6027 6014 if opts.get('remote'):
6028 6015 raise
6029 6016 return dest, dbranch, None, None
6030 6017 ui.debug('comparing with %s\n' % util.hidepassword(dest))
6031 6018 elif sother is None:
6032 6019 # there is no explicit destination peer, but source one is invalid
6033 6020 return dest, dbranch, None, None
6034 6021 else:
6035 6022 dother = sother
6036 6023 if (source != dest or (sbranch is not None and sbranch != dbranch)):
6037 6024 common = None
6038 6025 else:
6039 6026 common = commoninc
6040 6027 if revs:
6041 6028 revs = [repo.lookup(rev) for rev in revs]
6042 6029 repo.ui.pushbuffer()
6043 6030 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
6044 6031 commoninc=common)
6045 6032 repo.ui.popbuffer()
6046 6033 return dest, dbranch, dother, outgoing
6047 6034
6048 6035 if needsoutgoing:
6049 6036 dest, dbranch, dother, outgoing = getoutgoing()
6050 6037 else:
6051 6038 dest = dbranch = dother = outgoing = None
6052 6039
6053 6040 if opts.get('remote'):
6054 6041 t = []
6055 6042 if incoming:
6056 6043 t.append(_('1 or more incoming'))
6057 6044 o = outgoing.missing
6058 6045 if o:
6059 6046 t.append(_('%d outgoing') % len(o))
6060 6047 other = dother or sother
6061 6048 if 'bookmarks' in other.listkeys('namespaces'):
6062 6049 counts = bookmarks.summary(repo, other)
6063 6050 if counts[0] > 0:
6064 6051 t.append(_('%d incoming bookmarks') % counts[0])
6065 6052 if counts[1] > 0:
6066 6053 t.append(_('%d outgoing bookmarks') % counts[1])
6067 6054
6068 6055 if t:
6069 6056 # i18n: column positioning for "hg summary"
6070 6057 ui.write(_('remote: %s\n') % (', '.join(t)))
6071 6058 else:
6072 6059 # i18n: column positioning for "hg summary"
6073 6060 ui.status(_('remote: (synced)\n'))
6074 6061
6075 6062 cmdutil.summaryremotehooks(ui, repo, opts,
6076 6063 ((source, sbranch, sother, commoninc),
6077 6064 (dest, dbranch, dother, outgoing)))
6078 6065
6079 6066 @command('tag',
6080 6067 [('f', 'force', None, _('force tag')),
6081 6068 ('l', 'local', None, _('make the tag local')),
6082 6069 ('r', 'rev', '', _('revision to tag'), _('REV')),
6083 6070 ('', 'remove', None, _('remove a tag')),
6084 6071 # -l/--local is already there, commitopts cannot be used
6085 6072 ('e', 'edit', None, _('invoke editor on commit messages')),
6086 6073 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6087 6074 ] + commitopts2,
6088 6075 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6089 6076 def tag(ui, repo, name1, *names, **opts):
6090 6077 """add one or more tags for the current or given revision
6091 6078
6092 6079 Name a particular revision using <name>.
6093 6080
6094 6081 Tags are used to name particular revisions of the repository and are
6095 6082 very useful to compare different revisions, to go back to significant
6096 6083 earlier versions or to mark branch points as releases, etc. Changing
6097 6084 an existing tag is normally disallowed; use -f/--force to override.
6098 6085
6099 6086 If no revision is given, the parent of the working directory is
6100 6087 used.
6101 6088
6102 6089 To facilitate version control, distribution, and merging of tags,
6103 6090 they are stored as a file named ".hgtags" which is managed similarly
6104 6091 to other project files and can be hand-edited if necessary. This
6105 6092 also means that tagging creates a new commit. The file
6106 6093 ".hg/localtags" is used for local tags (not shared among
6107 6094 repositories).
6108 6095
6109 6096 Tag commits are usually made at the head of a branch. If the parent
6110 6097 of the working directory is not a branch head, :hg:`tag` aborts; use
6111 6098 -f/--force to force the tag commit to be based on a non-head
6112 6099 changeset.
6113 6100
6114 6101 See :hg:`help dates` for a list of formats valid for -d/--date.
6115 6102
6116 6103 Since tag names have priority over branch names during revision
6117 6104 lookup, using an existing branch name as a tag name is discouraged.
6118 6105
6119 6106 Returns 0 on success.
6120 6107 """
6121 6108 wlock = lock = None
6122 6109 try:
6123 6110 wlock = repo.wlock()
6124 6111 lock = repo.lock()
6125 6112 rev_ = "."
6126 6113 names = [t.strip() for t in (name1,) + names]
6127 6114 if len(names) != len(set(names)):
6128 6115 raise error.Abort(_('tag names must be unique'))
6129 6116 for n in names:
6130 6117 scmutil.checknewlabel(repo, n, 'tag')
6131 6118 if not n:
6132 6119 raise error.Abort(_('tag names cannot consist entirely of '
6133 6120 'whitespace'))
6134 6121 if opts.get('rev') and opts.get('remove'):
6135 6122 raise error.Abort(_("--rev and --remove are incompatible"))
6136 6123 if opts.get('rev'):
6137 6124 rev_ = opts['rev']
6138 6125 message = opts.get('message')
6139 6126 if opts.get('remove'):
6140 6127 if opts.get('local'):
6141 6128 expectedtype = 'local'
6142 6129 else:
6143 6130 expectedtype = 'global'
6144 6131
6145 6132 for n in names:
6146 6133 if not repo.tagtype(n):
6147 6134 raise error.Abort(_("tag '%s' does not exist") % n)
6148 6135 if repo.tagtype(n) != expectedtype:
6149 6136 if expectedtype == 'global':
6150 6137 raise error.Abort(_("tag '%s' is not a global tag") % n)
6151 6138 else:
6152 6139 raise error.Abort(_("tag '%s' is not a local tag") % n)
6153 6140 rev_ = 'null'
6154 6141 if not message:
6155 6142 # we don't translate commit messages
6156 6143 message = 'Removed tag %s' % ', '.join(names)
6157 6144 elif not opts.get('force'):
6158 6145 for n in names:
6159 6146 if n in repo.tags():
6160 6147 raise error.Abort(_("tag '%s' already exists "
6161 6148 "(use -f to force)") % n)
6162 6149 if not opts.get('local'):
6163 6150 p1, p2 = repo.dirstate.parents()
6164 6151 if p2 != nullid:
6165 6152 raise error.Abort(_('uncommitted merge'))
6166 6153 bheads = repo.branchheads()
6167 6154 if not opts.get('force') and bheads and p1 not in bheads:
6168 6155 raise error.Abort(_('working directory is not at a branch head '
6169 6156 '(use -f to force)'))
6170 6157 r = scmutil.revsingle(repo, rev_).node()
6171 6158
6172 6159 if not message:
6173 6160 # we don't translate commit messages
6174 6161 message = ('Added tag %s for changeset %s' %
6175 6162 (', '.join(names), short(r)))
6176 6163
6177 6164 date = opts.get('date')
6178 6165 if date:
6179 6166 date = util.parsedate(date)
6180 6167
6181 6168 if opts.get('remove'):
6182 6169 editform = 'tag.remove'
6183 6170 else:
6184 6171 editform = 'tag.add'
6185 6172 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6186 6173
6187 6174 # don't allow tagging the null rev
6188 6175 if (not opts.get('remove') and
6189 6176 scmutil.revsingle(repo, rev_).rev() == nullrev):
6190 6177 raise error.Abort(_("cannot tag null revision"))
6191 6178
6192 6179 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6193 6180 editor=editor)
6194 6181 finally:
6195 6182 release(lock, wlock)
6196 6183
6197 6184 @command('tags', formatteropts, '')
6198 6185 def tags(ui, repo, **opts):
6199 6186 """list repository tags
6200 6187
6201 6188 This lists both regular and local tags. When the -v/--verbose
6202 6189 switch is used, a third column "local" is printed for local tags.
6203 6190 When the -q/--quiet switch is used, only the tag name is printed.
6204 6191
6205 6192 Returns 0 on success.
6206 6193 """
6207 6194
6208 6195 fm = ui.formatter('tags', opts)
6209 6196 hexfunc = fm.hexfunc
6210 6197 tagtype = ""
6211 6198
6212 6199 for t, n in reversed(repo.tagslist()):
6213 6200 hn = hexfunc(n)
6214 6201 label = 'tags.normal'
6215 6202 tagtype = ''
6216 6203 if repo.tagtype(t) == 'local':
6217 6204 label = 'tags.local'
6218 6205 tagtype = 'local'
6219 6206
6220 6207 fm.startitem()
6221 6208 fm.write('tag', '%s', t, label=label)
6222 6209 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6223 6210 fm.condwrite(not ui.quiet, 'rev node', fmt,
6224 6211 repo.changelog.rev(n), hn, label=label)
6225 6212 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6226 6213 tagtype, label=label)
6227 6214 fm.plain('\n')
6228 6215 fm.end()
6229 6216
6230 6217 @command('tip',
6231 6218 [('p', 'patch', None, _('show patch')),
6232 6219 ('g', 'git', None, _('use git extended diff format')),
6233 6220 ] + templateopts,
6234 6221 _('[-p] [-g]'))
6235 6222 def tip(ui, repo, **opts):
6236 6223 """show the tip revision (DEPRECATED)
6237 6224
6238 6225 The tip revision (usually just called the tip) is the changeset
6239 6226 most recently added to the repository (and therefore the most
6240 6227 recently changed head).
6241 6228
6242 6229 If you have just made a commit, that commit will be the tip. If
6243 6230 you have just pulled changes from another repository, the tip of
6244 6231 that repository becomes the current tip. The "tip" tag is special
6245 6232 and cannot be renamed or assigned to a different changeset.
6246 6233
6247 6234 This command is deprecated, please use :hg:`heads` instead.
6248 6235
6249 6236 Returns 0 on success.
6250 6237 """
6251 6238 displayer = cmdutil.show_changeset(ui, repo, opts)
6252 6239 displayer.show(repo['tip'])
6253 6240 displayer.close()
6254 6241
6255 6242 @command('unbundle',
6256 6243 [('u', 'update', None,
6257 6244 _('update to new branch head if changesets were unbundled'))],
6258 6245 _('[-u] FILE...'))
6259 6246 def unbundle(ui, repo, fname1, *fnames, **opts):
6260 6247 """apply one or more changegroup files
6261 6248
6262 6249 Apply one or more compressed changegroup files generated by the
6263 6250 bundle command.
6264 6251
6265 6252 Returns 0 on success, 1 if an update has unresolved files.
6266 6253 """
6267 6254 fnames = (fname1,) + fnames
6268 6255
6269 6256 with repo.lock():
6270 6257 for fname in fnames:
6271 6258 f = hg.openpath(ui, fname)
6272 6259 gen = exchange.readbundle(ui, f, fname)
6273 6260 if isinstance(gen, bundle2.unbundle20):
6274 6261 tr = repo.transaction('unbundle')
6275 6262 try:
6276 6263 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
6277 6264 url='bundle:' + fname)
6278 6265 tr.close()
6279 6266 except error.BundleUnknownFeatureError as exc:
6280 6267 raise error.Abort(_('%s: unknown bundle feature, %s')
6281 6268 % (fname, exc),
6282 6269 hint=_("see https://mercurial-scm.org/"
6283 6270 "wiki/BundleFeature for more "
6284 6271 "information"))
6285 6272 finally:
6286 6273 if tr:
6287 6274 tr.release()
6288 6275 changes = [r.get('return', 0)
6289 6276 for r in op.records['changegroup']]
6290 6277 modheads = changegroup.combineresults(changes)
6291 6278 elif isinstance(gen, streamclone.streamcloneapplier):
6292 6279 raise error.Abort(
6293 6280 _('packed bundles cannot be applied with '
6294 6281 '"hg unbundle"'),
6295 6282 hint=_('use "hg debugapplystreamclonebundle"'))
6296 6283 else:
6297 6284 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
6298 6285
6299 6286 return postincoming(ui, repo, modheads, opts.get('update'), None, None)
6300 6287
6301 6288 @command('^update|up|checkout|co',
6302 6289 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6303 6290 ('c', 'check', None, _('require clean working directory')),
6304 6291 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6305 6292 ('r', 'rev', '', _('revision'), _('REV'))
6306 6293 ] + mergetoolopts,
6307 6294 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6308 6295 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6309 6296 tool=None):
6310 6297 """update working directory (or switch revisions)
6311 6298
6312 6299 Update the repository's working directory to the specified
6313 6300 changeset. If no changeset is specified, update to the tip of the
6314 6301 current named branch and move the active bookmark (see :hg:`help
6315 6302 bookmarks`).
6316 6303
6317 6304 Update sets the working directory's parent revision to the specified
6318 6305 changeset (see :hg:`help parents`).
6319 6306
6320 6307 If the changeset is not a descendant or ancestor of the working
6321 6308 directory's parent and there are uncommitted changes, the update is
6322 6309 aborted. With the -c/--check option, the working directory is checked
6323 6310 for uncommitted changes; if none are found, the working directory is
6324 6311 updated to the specified changeset.
6325 6312
6326 6313 .. container:: verbose
6327 6314
6328 6315 The following rules apply when the working directory contains
6329 6316 uncommitted changes:
6330 6317
6331 6318 1. If neither -c/--check nor -C/--clean is specified, and if
6332 6319 the requested changeset is an ancestor or descendant of
6333 6320 the working directory's parent, the uncommitted changes
6334 6321 are merged into the requested changeset and the merged
6335 6322 result is left uncommitted. If the requested changeset is
6336 6323 not an ancestor or descendant (that is, it is on another
6337 6324 branch), the update is aborted and the uncommitted changes
6338 6325 are preserved.
6339 6326
6340 6327 2. With the -c/--check option, the update is aborted and the
6341 6328 uncommitted changes are preserved.
6342 6329
6343 6330 3. With the -C/--clean option, uncommitted changes are discarded and
6344 6331 the working directory is updated to the requested changeset.
6345 6332
6346 6333 To cancel an uncommitted merge (and lose your changes), use
6347 6334 :hg:`update --clean .`.
6348 6335
6349 6336 Use null as the changeset to remove the working directory (like
6350 6337 :hg:`clone -U`).
6351 6338
6352 6339 If you want to revert just one file to an older revision, use
6353 6340 :hg:`revert [-r REV] NAME`.
6354 6341
6355 6342 See :hg:`help dates` for a list of formats valid for -d/--date.
6356 6343
6357 6344 Returns 0 on success, 1 if there are unresolved files.
6358 6345 """
6359 6346 if rev and node:
6360 6347 raise error.Abort(_("please specify just one revision"))
6361 6348
6362 6349 if rev is None or rev == '':
6363 6350 rev = node
6364 6351
6365 6352 if date and rev is not None:
6366 6353 raise error.Abort(_("you can't specify a revision and a date"))
6367 6354
6368 6355 if check and clean:
6369 6356 raise error.Abort(_("cannot specify both -c/--check and -C/--clean"))
6370 6357
6371 6358 with repo.wlock():
6372 6359 cmdutil.clearunfinished(repo)
6373 6360
6374 6361 if date:
6375 6362 rev = cmdutil.finddate(ui, repo, date)
6376 6363
6377 6364 # if we defined a bookmark, we have to remember the original name
6378 6365 brev = rev
6379 6366 rev = scmutil.revsingle(repo, rev, rev).rev()
6380 6367
6381 6368 if check:
6382 6369 cmdutil.bailifchanged(repo, merge=False)
6383 6370
6384 6371 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6385 6372
6386 6373 return hg.updatetotally(ui, repo, rev, brev, clean=clean, check=check)
6387 6374
6388 6375 @command('verify', [])
6389 6376 def verify(ui, repo):
6390 6377 """verify the integrity of the repository
6391 6378
6392 6379 Verify the integrity of the current repository.
6393 6380
6394 6381 This will perform an extensive check of the repository's
6395 6382 integrity, validating the hashes and checksums of each entry in
6396 6383 the changelog, manifest, and tracked files, as well as the
6397 6384 integrity of their crosslinks and indices.
6398 6385
6399 6386 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6400 6387 for more information about recovery from corruption of the
6401 6388 repository.
6402 6389
6403 6390 Returns 0 on success, 1 if errors are encountered.
6404 6391 """
6405 6392 return hg.verify(repo)
6406 6393
6407 6394 @command('version', [] + formatteropts, norepo=True)
6408 6395 def version_(ui, **opts):
6409 6396 """output version and copyright information"""
6410 6397 fm = ui.formatter("version", opts)
6411 6398 fm.startitem()
6412 6399 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6413 6400 util.version())
6414 6401 license = _(
6415 6402 "(see https://mercurial-scm.org for more information)\n"
6416 6403 "\nCopyright (C) 2005-2017 Matt Mackall and others\n"
6417 6404 "This is free software; see the source for copying conditions. "
6418 6405 "There is NO\nwarranty; "
6419 6406 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6420 6407 )
6421 6408 if not ui.quiet:
6422 6409 fm.plain(license)
6423 6410
6424 6411 if ui.verbose:
6425 6412 fm.plain(_("\nEnabled extensions:\n\n"))
6426 6413 # format names and versions into columns
6427 6414 names = []
6428 6415 vers = []
6429 6416 isinternals = []
6430 6417 for name, module in extensions.extensions():
6431 6418 names.append(name)
6432 6419 vers.append(extensions.moduleversion(module) or None)
6433 6420 isinternals.append(extensions.ismoduleinternal(module))
6434 6421 fn = fm.nested("extensions")
6435 6422 if names:
6436 6423 namefmt = " %%-%ds " % max(len(n) for n in names)
6437 6424 places = [_("external"), _("internal")]
6438 6425 for n, v, p in zip(names, vers, isinternals):
6439 6426 fn.startitem()
6440 6427 fn.condwrite(ui.verbose, "name", namefmt, n)
6441 6428 if ui.verbose:
6442 6429 fn.plain("%s " % places[p])
6443 6430 fn.data(bundled=p)
6444 6431 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6445 6432 if ui.verbose:
6446 6433 fn.plain("\n")
6447 6434 fn.end()
6448 6435 fm.end()
6449 6436
6450 6437 def loadcmdtable(ui, name, cmdtable):
6451 6438 """Load command functions from specified cmdtable
6452 6439 """
6453 6440 overrides = [cmd for cmd in cmdtable if cmd in table]
6454 6441 if overrides:
6455 6442 ui.warn(_("extension '%s' overrides commands: %s\n")
6456 6443 % (name, " ".join(overrides)))
6457 6444 table.update(cmdtable)
@@ -1,1049 +1,1062
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 @command('debugknown', [], _('REPO ID...'), norepo=True)
1027 def debugknown(ui, repopath, *ids, **opts):
1028 """test whether node ids are known to a repo
1029
1030 Every ID must be a full-length hex node id string. Returns a list of 0s
1031 and 1s indicating unknown/known.
1032 """
1033 repo = hg.peer(ui, opts, repopath)
1034 if not repo.capable('known'):
1035 raise error.Abort("known() not supported by target repository")
1036 flags = repo.known([bin(s) for s in ids])
1037 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1038
1026 1039 @command('debugupgraderepo', [
1027 1040 ('o', 'optimize', [], _('extra optimization to perform'), _('NAME')),
1028 1041 ('', 'run', False, _('performs an upgrade')),
1029 1042 ])
1030 1043 def debugupgraderepo(ui, repo, run=False, optimize=None):
1031 1044 """upgrade a repository to use different features
1032 1045
1033 1046 If no arguments are specified, the repository is evaluated for upgrade
1034 1047 and a list of problems and potential optimizations is printed.
1035 1048
1036 1049 With ``--run``, a repository upgrade is performed. Behavior of the upgrade
1037 1050 can be influenced via additional arguments. More details will be provided
1038 1051 by the command output when run without ``--run``.
1039 1052
1040 1053 During the upgrade, the repository will be locked and no writes will be
1041 1054 allowed.
1042 1055
1043 1056 At the end of the upgrade, the repository may not be readable while new
1044 1057 repository data is swapped in. This window will be as long as it takes to
1045 1058 rename some directories inside the ``.hg`` directory. On most machines, this
1046 1059 should complete almost instantaneously and the chances of a consumer being
1047 1060 unable to access the repository should be low.
1048 1061 """
1049 1062 return repair.upgraderepo(ui, repo, run=run, optimize=optimize)
General Comments 0
You need to be logged in to leave comments. Login now