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