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