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