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