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