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