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