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