##// END OF EJS Templates
unshelve: add help text on --interactive in verbose mode...
Navaneeth Suresh -
r42867:0795bbe8 stable
parent child Browse files
Show More
@@ -1,6440 +1,6448 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import difflib
11 11 import errno
12 12 import os
13 13 import re
14 14 import sys
15 15
16 16 from .i18n import _
17 17 from .node import (
18 18 hex,
19 19 nullid,
20 20 nullrev,
21 21 short,
22 22 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 if fm.isplain():
1876 1876 source = source or 'none'
1877 1877 value = value.replace('\n', '\\n')
1878 1878 entryname = section + '.' + name
1879 1879 if values and not (section in selsections or entryname in selentries):
1880 1880 continue
1881 1881 fm.startitem()
1882 1882 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1883 1883 if uniquesel:
1884 1884 fm.data(name=entryname)
1885 1885 fm.write('value', '%s\n', value)
1886 1886 else:
1887 1887 fm.write('name value', '%s=%s\n', entryname, value)
1888 1888 matched = True
1889 1889 fm.end()
1890 1890 if matched:
1891 1891 return 0
1892 1892 return 1
1893 1893
1894 1894 @command('continue',
1895 1895 dryrunopts, helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
1896 1896 helpbasic=True)
1897 1897 def continuecmd(ui, repo, **opts):
1898 1898 """resumes an interrupted operation (EXPERIMENTAL)
1899 1899
1900 1900 Finishes a multistep operation like graft, histedit, rebase, merge,
1901 1901 and unshelve if they are in an interrupted state.
1902 1902
1903 1903 use --dry-run/-n to dry run the command.
1904 1904 """
1905 1905 dryrun = opts.get(r'dry_run')
1906 1906 contstate = cmdutil.getunfinishedstate(repo)
1907 1907 if not contstate:
1908 1908 raise error.Abort(_('no operation in progress'))
1909 1909 if not contstate.continuefunc:
1910 1910 raise error.Abort((_("%s in progress but does not support "
1911 1911 "'hg continue'") % (contstate._opname)),
1912 1912 hint=contstate.continuemsg())
1913 1913 if dryrun:
1914 1914 ui.status(_('%s in progress, will be resumed\n') % (contstate._opname))
1915 1915 return
1916 1916 return contstate.continuefunc(ui, repo)
1917 1917
1918 1918 @command('copy|cp',
1919 1919 [('A', 'after', None, _('record a copy that has already occurred')),
1920 1920 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1921 1921 ] + walkopts + dryrunopts,
1922 1922 _('[OPTION]... SOURCE... DEST'),
1923 1923 helpcategory=command.CATEGORY_FILE_CONTENTS)
1924 1924 def copy(ui, repo, *pats, **opts):
1925 1925 """mark files as copied for the next commit
1926 1926
1927 1927 Mark dest as having copies of source files. If dest is a
1928 1928 directory, copies are put in that directory. If dest is a file,
1929 1929 the source must be a single file.
1930 1930
1931 1931 By default, this command copies the contents of files as they
1932 1932 exist in the working directory. If invoked with -A/--after, the
1933 1933 operation is recorded, but no copying is performed.
1934 1934
1935 1935 This command takes effect with the next commit. To undo a copy
1936 1936 before that, see :hg:`revert`.
1937 1937
1938 1938 Returns 0 on success, 1 if errors are encountered.
1939 1939 """
1940 1940 opts = pycompat.byteskwargs(opts)
1941 1941 with repo.wlock(False):
1942 1942 return cmdutil.copy(ui, repo, pats, opts)
1943 1943
1944 1944 @command(
1945 1945 'debugcommands', [], _('[COMMAND]'),
1946 1946 helpcategory=command.CATEGORY_HELP,
1947 1947 norepo=True)
1948 1948 def debugcommands(ui, cmd='', *args):
1949 1949 """list all available commands and options"""
1950 1950 for cmd, vals in sorted(table.iteritems()):
1951 1951 cmd = cmd.split('|')[0]
1952 1952 opts = ', '.join([i[1] for i in vals[1]])
1953 1953 ui.write('%s: %s\n' % (cmd, opts))
1954 1954
1955 1955 @command('debugcomplete',
1956 1956 [('o', 'options', None, _('show the command options'))],
1957 1957 _('[-o] CMD'),
1958 1958 helpcategory=command.CATEGORY_HELP,
1959 1959 norepo=True)
1960 1960 def debugcomplete(ui, cmd='', **opts):
1961 1961 """returns the completion list associated with the given command"""
1962 1962
1963 1963 if opts.get(r'options'):
1964 1964 options = []
1965 1965 otables = [globalopts]
1966 1966 if cmd:
1967 1967 aliases, entry = cmdutil.findcmd(cmd, table, False)
1968 1968 otables.append(entry[1])
1969 1969 for t in otables:
1970 1970 for o in t:
1971 1971 if "(DEPRECATED)" in o[3]:
1972 1972 continue
1973 1973 if o[0]:
1974 1974 options.append('-%s' % o[0])
1975 1975 options.append('--%s' % o[1])
1976 1976 ui.write("%s\n" % "\n".join(options))
1977 1977 return
1978 1978
1979 1979 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1980 1980 if ui.verbose:
1981 1981 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1982 1982 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1983 1983
1984 1984 @command('diff',
1985 1985 [('r', 'rev', [], _('revision'), _('REV')),
1986 1986 ('c', 'change', '', _('change made by revision'), _('REV'))
1987 1987 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1988 1988 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1989 1989 helpcategory=command.CATEGORY_FILE_CONTENTS,
1990 1990 helpbasic=True, inferrepo=True, intents={INTENT_READONLY})
1991 1991 def diff(ui, repo, *pats, **opts):
1992 1992 """diff repository (or selected files)
1993 1993
1994 1994 Show differences between revisions for the specified files.
1995 1995
1996 1996 Differences between files are shown using the unified diff format.
1997 1997
1998 1998 .. note::
1999 1999
2000 2000 :hg:`diff` may generate unexpected results for merges, as it will
2001 2001 default to comparing against the working directory's first
2002 2002 parent changeset if no revisions are specified.
2003 2003
2004 2004 When two revision arguments are given, then changes are shown
2005 2005 between those revisions. If only one revision is specified then
2006 2006 that revision is compared to the working directory, and, when no
2007 2007 revisions are specified, the working directory files are compared
2008 2008 to its first parent.
2009 2009
2010 2010 Alternatively you can specify -c/--change with a revision to see
2011 2011 the changes in that changeset relative to its first parent.
2012 2012
2013 2013 Without the -a/--text option, diff will avoid generating diffs of
2014 2014 files it detects as binary. With -a, diff will generate a diff
2015 2015 anyway, probably with undesirable results.
2016 2016
2017 2017 Use the -g/--git option to generate diffs in the git extended diff
2018 2018 format. For more information, read :hg:`help diffs`.
2019 2019
2020 2020 .. container:: verbose
2021 2021
2022 2022 Examples:
2023 2023
2024 2024 - compare a file in the current working directory to its parent::
2025 2025
2026 2026 hg diff foo.c
2027 2027
2028 2028 - compare two historical versions of a directory, with rename info::
2029 2029
2030 2030 hg diff --git -r 1.0:1.2 lib/
2031 2031
2032 2032 - get change stats relative to the last change on some date::
2033 2033
2034 2034 hg diff --stat -r "date('may 2')"
2035 2035
2036 2036 - diff all newly-added files that contain a keyword::
2037 2037
2038 2038 hg diff "set:added() and grep(GNU)"
2039 2039
2040 2040 - compare a revision and its parents::
2041 2041
2042 2042 hg diff -c 9353 # compare against first parent
2043 2043 hg diff -r 9353^:9353 # same using revset syntax
2044 2044 hg diff -r 9353^2:9353 # compare against the second parent
2045 2045
2046 2046 Returns 0 on success.
2047 2047 """
2048 2048
2049 2049 opts = pycompat.byteskwargs(opts)
2050 2050 revs = opts.get('rev')
2051 2051 change = opts.get('change')
2052 2052 stat = opts.get('stat')
2053 2053 reverse = opts.get('reverse')
2054 2054
2055 2055 if revs and change:
2056 2056 msg = _('cannot specify --rev and --change at the same time')
2057 2057 raise error.Abort(msg)
2058 2058 elif change:
2059 2059 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
2060 2060 ctx2 = scmutil.revsingle(repo, change, None)
2061 2061 ctx1 = ctx2.p1()
2062 2062 else:
2063 2063 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
2064 2064 ctx1, ctx2 = scmutil.revpair(repo, revs)
2065 2065 node1, node2 = ctx1.node(), ctx2.node()
2066 2066
2067 2067 if reverse:
2068 2068 node1, node2 = node2, node1
2069 2069
2070 2070 diffopts = patch.diffallopts(ui, opts)
2071 2071 m = scmutil.match(ctx2, pats, opts)
2072 2072 m = repo.narrowmatch(m)
2073 2073 ui.pager('diff')
2074 2074 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2075 2075 listsubrepos=opts.get('subrepos'),
2076 2076 root=opts.get('root'))
2077 2077
2078 2078 @command('export',
2079 2079 [('B', 'bookmark', '',
2080 2080 _('export changes only reachable by given bookmark'), _('BOOKMARK')),
2081 2081 ('o', 'output', '',
2082 2082 _('print output to file with formatted name'), _('FORMAT')),
2083 2083 ('', 'switch-parent', None, _('diff against the second parent')),
2084 2084 ('r', 'rev', [], _('revisions to export'), _('REV')),
2085 2085 ] + diffopts + formatteropts,
2086 2086 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2087 2087 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2088 2088 helpbasic=True, intents={INTENT_READONLY})
2089 2089 def export(ui, repo, *changesets, **opts):
2090 2090 """dump the header and diffs for one or more changesets
2091 2091
2092 2092 Print the changeset header and diffs for one or more revisions.
2093 2093 If no revision is given, the parent of the working directory is used.
2094 2094
2095 2095 The information shown in the changeset header is: author, date,
2096 2096 branch name (if non-default), changeset hash, parent(s) and commit
2097 2097 comment.
2098 2098
2099 2099 .. note::
2100 2100
2101 2101 :hg:`export` may generate unexpected diff output for merge
2102 2102 changesets, as it will compare the merge changeset against its
2103 2103 first parent only.
2104 2104
2105 2105 Output may be to a file, in which case the name of the file is
2106 2106 given using a template string. See :hg:`help templates`. In addition
2107 2107 to the common template keywords, the following formatting rules are
2108 2108 supported:
2109 2109
2110 2110 :``%%``: literal "%" character
2111 2111 :``%H``: changeset hash (40 hexadecimal digits)
2112 2112 :``%N``: number of patches being generated
2113 2113 :``%R``: changeset revision number
2114 2114 :``%b``: basename of the exporting repository
2115 2115 :``%h``: short-form changeset hash (12 hexadecimal digits)
2116 2116 :``%m``: first line of the commit message (only alphanumeric characters)
2117 2117 :``%n``: zero-padded sequence number, starting at 1
2118 2118 :``%r``: zero-padded changeset revision number
2119 2119 :``\\``: literal "\\" character
2120 2120
2121 2121 Without the -a/--text option, export will avoid generating diffs
2122 2122 of files it detects as binary. With -a, export will generate a
2123 2123 diff anyway, probably with undesirable results.
2124 2124
2125 2125 With -B/--bookmark changesets reachable by the given bookmark are
2126 2126 selected.
2127 2127
2128 2128 Use the -g/--git option to generate diffs in the git extended diff
2129 2129 format. See :hg:`help diffs` for more information.
2130 2130
2131 2131 With the --switch-parent option, the diff will be against the
2132 2132 second parent. It can be useful to review a merge.
2133 2133
2134 2134 .. container:: verbose
2135 2135
2136 2136 Template:
2137 2137
2138 2138 The following keywords are supported in addition to the common template
2139 2139 keywords and functions. See also :hg:`help templates`.
2140 2140
2141 2141 :diff: String. Diff content.
2142 2142 :parents: List of strings. Parent nodes of the changeset.
2143 2143
2144 2144 Examples:
2145 2145
2146 2146 - use export and import to transplant a bugfix to the current
2147 2147 branch::
2148 2148
2149 2149 hg export -r 9353 | hg import -
2150 2150
2151 2151 - export all the changesets between two revisions to a file with
2152 2152 rename information::
2153 2153
2154 2154 hg export --git -r 123:150 > changes.txt
2155 2155
2156 2156 - split outgoing changes into a series of patches with
2157 2157 descriptive names::
2158 2158
2159 2159 hg export -r "outgoing()" -o "%n-%m.patch"
2160 2160
2161 2161 Returns 0 on success.
2162 2162 """
2163 2163 opts = pycompat.byteskwargs(opts)
2164 2164 bookmark = opts.get('bookmark')
2165 2165 changesets += tuple(opts.get('rev', []))
2166 2166
2167 2167 if bookmark and changesets:
2168 2168 raise error.Abort(_("-r and -B are mutually exclusive"))
2169 2169
2170 2170 if bookmark:
2171 2171 if bookmark not in repo._bookmarks:
2172 2172 raise error.Abort(_("bookmark '%s' not found") % bookmark)
2173 2173
2174 2174 revs = scmutil.bookmarkrevs(repo, bookmark)
2175 2175 else:
2176 2176 if not changesets:
2177 2177 changesets = ['.']
2178 2178
2179 2179 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
2180 2180 revs = scmutil.revrange(repo, changesets)
2181 2181
2182 2182 if not revs:
2183 2183 raise error.Abort(_("export requires at least one changeset"))
2184 2184 if len(revs) > 1:
2185 2185 ui.note(_('exporting patches:\n'))
2186 2186 else:
2187 2187 ui.note(_('exporting patch:\n'))
2188 2188
2189 2189 fntemplate = opts.get('output')
2190 2190 if cmdutil.isstdiofilename(fntemplate):
2191 2191 fntemplate = ''
2192 2192
2193 2193 if fntemplate:
2194 2194 fm = formatter.nullformatter(ui, 'export', opts)
2195 2195 else:
2196 2196 ui.pager('export')
2197 2197 fm = ui.formatter('export', opts)
2198 2198 with fm:
2199 2199 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
2200 2200 switch_parent=opts.get('switch_parent'),
2201 2201 opts=patch.diffallopts(ui, opts))
2202 2202
2203 2203 @command('files',
2204 2204 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2205 2205 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2206 2206 ] + walkopts + formatteropts + subrepoopts,
2207 2207 _('[OPTION]... [FILE]...'),
2208 2208 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2209 2209 intents={INTENT_READONLY})
2210 2210 def files(ui, repo, *pats, **opts):
2211 2211 """list tracked files
2212 2212
2213 2213 Print files under Mercurial control in the working directory or
2214 2214 specified revision for given files (excluding removed files).
2215 2215 Files can be specified as filenames or filesets.
2216 2216
2217 2217 If no files are given to match, this command prints the names
2218 2218 of all files under Mercurial control.
2219 2219
2220 2220 .. container:: verbose
2221 2221
2222 2222 Template:
2223 2223
2224 2224 The following keywords are supported in addition to the common template
2225 2225 keywords and functions. See also :hg:`help templates`.
2226 2226
2227 2227 :flags: String. Character denoting file's symlink and executable bits.
2228 2228 :path: String. Repository-absolute path of the file.
2229 2229 :size: Integer. Size of the file in bytes.
2230 2230
2231 2231 Examples:
2232 2232
2233 2233 - list all files under the current directory::
2234 2234
2235 2235 hg files .
2236 2236
2237 2237 - shows sizes and flags for current revision::
2238 2238
2239 2239 hg files -vr .
2240 2240
2241 2241 - list all files named README::
2242 2242
2243 2243 hg files -I "**/README"
2244 2244
2245 2245 - list all binary files::
2246 2246
2247 2247 hg files "set:binary()"
2248 2248
2249 2249 - find files containing a regular expression::
2250 2250
2251 2251 hg files "set:grep('bob')"
2252 2252
2253 2253 - search tracked file contents with xargs and grep::
2254 2254
2255 2255 hg files -0 | xargs -0 grep foo
2256 2256
2257 2257 See :hg:`help patterns` and :hg:`help filesets` for more information
2258 2258 on specifying file patterns.
2259 2259
2260 2260 Returns 0 if a match is found, 1 otherwise.
2261 2261
2262 2262 """
2263 2263
2264 2264 opts = pycompat.byteskwargs(opts)
2265 2265 rev = opts.get('rev')
2266 2266 if rev:
2267 2267 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2268 2268 ctx = scmutil.revsingle(repo, rev, None)
2269 2269
2270 2270 end = '\n'
2271 2271 if opts.get('print0'):
2272 2272 end = '\0'
2273 2273 fmt = '%s' + end
2274 2274
2275 2275 m = scmutil.match(ctx, pats, opts)
2276 2276 ui.pager('files')
2277 2277 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2278 2278 with ui.formatter('files', opts) as fm:
2279 2279 return cmdutil.files(ui, ctx, m, uipathfn, fm, fmt,
2280 2280 opts.get('subrepos'))
2281 2281
2282 2282 @command(
2283 2283 'forget',
2284 2284 [('i', 'interactive', None, _('use interactive mode')),
2285 2285 ] + walkopts + dryrunopts,
2286 2286 _('[OPTION]... FILE...'),
2287 2287 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2288 2288 helpbasic=True, inferrepo=True)
2289 2289 def forget(ui, repo, *pats, **opts):
2290 2290 """forget the specified files on the next commit
2291 2291
2292 2292 Mark the specified files so they will no longer be tracked
2293 2293 after the next commit.
2294 2294
2295 2295 This only removes files from the current branch, not from the
2296 2296 entire project history, and it does not delete them from the
2297 2297 working directory.
2298 2298
2299 2299 To delete the file from the working directory, see :hg:`remove`.
2300 2300
2301 2301 To undo a forget before the next commit, see :hg:`add`.
2302 2302
2303 2303 .. container:: verbose
2304 2304
2305 2305 Examples:
2306 2306
2307 2307 - forget newly-added binary files::
2308 2308
2309 2309 hg forget "set:added() and binary()"
2310 2310
2311 2311 - forget files that would be excluded by .hgignore::
2312 2312
2313 2313 hg forget "set:hgignore()"
2314 2314
2315 2315 Returns 0 on success.
2316 2316 """
2317 2317
2318 2318 opts = pycompat.byteskwargs(opts)
2319 2319 if not pats:
2320 2320 raise error.Abort(_('no files specified'))
2321 2321
2322 2322 m = scmutil.match(repo[None], pats, opts)
2323 2323 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2324 2324 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2325 2325 rejected = cmdutil.forget(ui, repo, m, prefix="", uipathfn=uipathfn,
2326 2326 explicitonly=False, dryrun=dryrun,
2327 2327 interactive=interactive)[0]
2328 2328 return rejected and 1 or 0
2329 2329
2330 2330 @command(
2331 2331 'graft',
2332 2332 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2333 2333 ('', 'base', '',
2334 2334 _('base revision when doing the graft merge (ADVANCED)'), _('REV')),
2335 2335 ('c', 'continue', False, _('resume interrupted graft')),
2336 2336 ('', 'stop', False, _('stop interrupted graft')),
2337 2337 ('', 'abort', False, _('abort interrupted graft')),
2338 2338 ('e', 'edit', False, _('invoke editor on commit messages')),
2339 2339 ('', 'log', None, _('append graft info to log message')),
2340 2340 ('', 'no-commit', None,
2341 2341 _("don't commit, just apply the changes in working directory")),
2342 2342 ('f', 'force', False, _('force graft')),
2343 2343 ('D', 'currentdate', False,
2344 2344 _('record the current date as commit date')),
2345 2345 ('U', 'currentuser', False,
2346 2346 _('record the current user as committer'))]
2347 2347 + commitopts2 + mergetoolopts + dryrunopts,
2348 2348 _('[OPTION]... [-r REV]... REV...'),
2349 2349 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
2350 2350 def graft(ui, repo, *revs, **opts):
2351 2351 '''copy changes from other branches onto the current branch
2352 2352
2353 2353 This command uses Mercurial's merge logic to copy individual
2354 2354 changes from other branches without merging branches in the
2355 2355 history graph. This is sometimes known as 'backporting' or
2356 2356 'cherry-picking'. By default, graft will copy user, date, and
2357 2357 description from the source changesets.
2358 2358
2359 2359 Changesets that are ancestors of the current revision, that have
2360 2360 already been grafted, or that are merges will be skipped.
2361 2361
2362 2362 If --log is specified, log messages will have a comment appended
2363 2363 of the form::
2364 2364
2365 2365 (grafted from CHANGESETHASH)
2366 2366
2367 2367 If --force is specified, revisions will be grafted even if they
2368 2368 are already ancestors of, or have been grafted to, the destination.
2369 2369 This is useful when the revisions have since been backed out.
2370 2370
2371 2371 If a graft merge results in conflicts, the graft process is
2372 2372 interrupted so that the current merge can be manually resolved.
2373 2373 Once all conflicts are addressed, the graft process can be
2374 2374 continued with the -c/--continue option.
2375 2375
2376 2376 The -c/--continue option reapplies all the earlier options.
2377 2377
2378 2378 .. container:: verbose
2379 2379
2380 2380 The --base option exposes more of how graft internally uses merge with a
2381 2381 custom base revision. --base can be used to specify another ancestor than
2382 2382 the first and only parent.
2383 2383
2384 2384 The command::
2385 2385
2386 2386 hg graft -r 345 --base 234
2387 2387
2388 2388 is thus pretty much the same as::
2389 2389
2390 2390 hg diff -r 234 -r 345 | hg import
2391 2391
2392 2392 but using merge to resolve conflicts and track moved files.
2393 2393
2394 2394 The result of a merge can thus be backported as a single commit by
2395 2395 specifying one of the merge parents as base, and thus effectively
2396 2396 grafting the changes from the other side.
2397 2397
2398 2398 It is also possible to collapse multiple changesets and clean up history
2399 2399 by specifying another ancestor as base, much like rebase --collapse
2400 2400 --keep.
2401 2401
2402 2402 The commit message can be tweaked after the fact using commit --amend .
2403 2403
2404 2404 For using non-ancestors as the base to backout changes, see the backout
2405 2405 command and the hidden --parent option.
2406 2406
2407 2407 .. container:: verbose
2408 2408
2409 2409 Examples:
2410 2410
2411 2411 - copy a single change to the stable branch and edit its description::
2412 2412
2413 2413 hg update stable
2414 2414 hg graft --edit 9393
2415 2415
2416 2416 - graft a range of changesets with one exception, updating dates::
2417 2417
2418 2418 hg graft -D "2085::2093 and not 2091"
2419 2419
2420 2420 - continue a graft after resolving conflicts::
2421 2421
2422 2422 hg graft -c
2423 2423
2424 2424 - show the source of a grafted changeset::
2425 2425
2426 2426 hg log --debug -r .
2427 2427
2428 2428 - show revisions sorted by date::
2429 2429
2430 2430 hg log -r "sort(all(), date)"
2431 2431
2432 2432 - backport the result of a merge as a single commit::
2433 2433
2434 2434 hg graft -r 123 --base 123^
2435 2435
2436 2436 - land a feature branch as one changeset::
2437 2437
2438 2438 hg up -cr default
2439 2439 hg graft -r featureX --base "ancestor('featureX', 'default')"
2440 2440
2441 2441 See :hg:`help revisions` for more about specifying revisions.
2442 2442
2443 2443 Returns 0 on successful completion.
2444 2444 '''
2445 2445 with repo.wlock():
2446 2446 return _dograft(ui, repo, *revs, **opts)
2447 2447
2448 2448 def _dograft(ui, repo, *revs, **opts):
2449 2449 opts = pycompat.byteskwargs(opts)
2450 2450 if revs and opts.get('rev'):
2451 2451 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2452 2452 'revision ordering!\n'))
2453 2453
2454 2454 revs = list(revs)
2455 2455 revs.extend(opts.get('rev'))
2456 2456 basectx = None
2457 2457 if opts.get('base'):
2458 2458 basectx = scmutil.revsingle(repo, opts['base'], None)
2459 2459 # a dict of data to be stored in state file
2460 2460 statedata = {}
2461 2461 # list of new nodes created by ongoing graft
2462 2462 statedata['newnodes'] = []
2463 2463
2464 2464 if opts.get('user') and opts.get('currentuser'):
2465 2465 raise error.Abort(_('--user and --currentuser are mutually exclusive'))
2466 2466 if opts.get('date') and opts.get('currentdate'):
2467 2467 raise error.Abort(_('--date and --currentdate are mutually exclusive'))
2468 2468 if not opts.get('user') and opts.get('currentuser'):
2469 2469 opts['user'] = ui.username()
2470 2470 if not opts.get('date') and opts.get('currentdate'):
2471 2471 opts['date'] = "%d %d" % dateutil.makedate()
2472 2472
2473 2473 editor = cmdutil.getcommiteditor(editform='graft',
2474 2474 **pycompat.strkwargs(opts))
2475 2475
2476 2476 cont = False
2477 2477 if opts.get('no_commit'):
2478 2478 if opts.get('edit'):
2479 2479 raise error.Abort(_("cannot specify --no-commit and "
2480 2480 "--edit together"))
2481 2481 if opts.get('currentuser'):
2482 2482 raise error.Abort(_("cannot specify --no-commit and "
2483 2483 "--currentuser together"))
2484 2484 if opts.get('currentdate'):
2485 2485 raise error.Abort(_("cannot specify --no-commit and "
2486 2486 "--currentdate together"))
2487 2487 if opts.get('log'):
2488 2488 raise error.Abort(_("cannot specify --no-commit and "
2489 2489 "--log together"))
2490 2490
2491 2491 graftstate = statemod.cmdstate(repo, 'graftstate')
2492 2492
2493 2493 if opts.get('stop'):
2494 2494 if opts.get('continue'):
2495 2495 raise error.Abort(_("cannot use '--continue' and "
2496 2496 "'--stop' together"))
2497 2497 if opts.get('abort'):
2498 2498 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2499 2499
2500 2500 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2501 2501 opts.get('date'), opts.get('currentdate'),
2502 2502 opts.get('currentuser'), opts.get('rev'))):
2503 2503 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2504 2504 return _stopgraft(ui, repo, graftstate)
2505 2505 elif opts.get('abort'):
2506 2506 if opts.get('continue'):
2507 2507 raise error.Abort(_("cannot use '--continue' and "
2508 2508 "'--abort' together"))
2509 2509 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2510 2510 opts.get('date'), opts.get('currentdate'),
2511 2511 opts.get('currentuser'), opts.get('rev'))):
2512 2512 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2513 2513
2514 2514 return cmdutil.abortgraft(ui, repo, graftstate)
2515 2515 elif opts.get('continue'):
2516 2516 cont = True
2517 2517 if revs:
2518 2518 raise error.Abort(_("can't specify --continue and revisions"))
2519 2519 # read in unfinished revisions
2520 2520 if graftstate.exists():
2521 2521 statedata = cmdutil.readgraftstate(repo, graftstate)
2522 2522 if statedata.get('date'):
2523 2523 opts['date'] = statedata['date']
2524 2524 if statedata.get('user'):
2525 2525 opts['user'] = statedata['user']
2526 2526 if statedata.get('log'):
2527 2527 opts['log'] = True
2528 2528 if statedata.get('no_commit'):
2529 2529 opts['no_commit'] = statedata.get('no_commit')
2530 2530 nodes = statedata['nodes']
2531 2531 revs = [repo[node].rev() for node in nodes]
2532 2532 else:
2533 2533 cmdutil.wrongtooltocontinue(repo, _('graft'))
2534 2534 else:
2535 2535 if not revs:
2536 2536 raise error.Abort(_('no revisions specified'))
2537 2537 cmdutil.checkunfinished(repo)
2538 2538 cmdutil.bailifchanged(repo)
2539 2539 revs = scmutil.revrange(repo, revs)
2540 2540
2541 2541 skipped = set()
2542 2542 if basectx is None:
2543 2543 # check for merges
2544 2544 for rev in repo.revs('%ld and merge()', revs):
2545 2545 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2546 2546 skipped.add(rev)
2547 2547 revs = [r for r in revs if r not in skipped]
2548 2548 if not revs:
2549 2549 return -1
2550 2550 if basectx is not None and len(revs) != 1:
2551 2551 raise error.Abort(_('only one revision allowed with --base '))
2552 2552
2553 2553 # Don't check in the --continue case, in effect retaining --force across
2554 2554 # --continues. That's because without --force, any revisions we decided to
2555 2555 # skip would have been filtered out here, so they wouldn't have made their
2556 2556 # way to the graftstate. With --force, any revisions we would have otherwise
2557 2557 # skipped would not have been filtered out, and if they hadn't been applied
2558 2558 # already, they'd have been in the graftstate.
2559 2559 if not (cont or opts.get('force')) and basectx is None:
2560 2560 # check for ancestors of dest branch
2561 2561 crev = repo['.'].rev()
2562 2562 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2563 2563 # XXX make this lazy in the future
2564 2564 # don't mutate while iterating, create a copy
2565 2565 for rev in list(revs):
2566 2566 if rev in ancestors:
2567 2567 ui.warn(_('skipping ancestor revision %d:%s\n') %
2568 2568 (rev, repo[rev]))
2569 2569 # XXX remove on list is slow
2570 2570 revs.remove(rev)
2571 2571 if not revs:
2572 2572 return -1
2573 2573
2574 2574 # analyze revs for earlier grafts
2575 2575 ids = {}
2576 2576 for ctx in repo.set("%ld", revs):
2577 2577 ids[ctx.hex()] = ctx.rev()
2578 2578 n = ctx.extra().get('source')
2579 2579 if n:
2580 2580 ids[n] = ctx.rev()
2581 2581
2582 2582 # check ancestors for earlier grafts
2583 2583 ui.debug('scanning for duplicate grafts\n')
2584 2584
2585 2585 # The only changesets we can be sure doesn't contain grafts of any
2586 2586 # revs, are the ones that are common ancestors of *all* revs:
2587 2587 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2588 2588 ctx = repo[rev]
2589 2589 n = ctx.extra().get('source')
2590 2590 if n in ids:
2591 2591 try:
2592 2592 r = repo[n].rev()
2593 2593 except error.RepoLookupError:
2594 2594 r = None
2595 2595 if r in revs:
2596 2596 ui.warn(_('skipping revision %d:%s '
2597 2597 '(already grafted to %d:%s)\n')
2598 2598 % (r, repo[r], rev, ctx))
2599 2599 revs.remove(r)
2600 2600 elif ids[n] in revs:
2601 2601 if r is None:
2602 2602 ui.warn(_('skipping already grafted revision %d:%s '
2603 2603 '(%d:%s also has unknown origin %s)\n')
2604 2604 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2605 2605 else:
2606 2606 ui.warn(_('skipping already grafted revision %d:%s '
2607 2607 '(%d:%s also has origin %d:%s)\n')
2608 2608 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2609 2609 revs.remove(ids[n])
2610 2610 elif ctx.hex() in ids:
2611 2611 r = ids[ctx.hex()]
2612 2612 if r in revs:
2613 2613 ui.warn(_('skipping already grafted revision %d:%s '
2614 2614 '(was grafted from %d:%s)\n') %
2615 2615 (r, repo[r], rev, ctx))
2616 2616 revs.remove(r)
2617 2617 if not revs:
2618 2618 return -1
2619 2619
2620 2620 if opts.get('no_commit'):
2621 2621 statedata['no_commit'] = True
2622 2622 for pos, ctx in enumerate(repo.set("%ld", revs)):
2623 2623 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2624 2624 ctx.description().split('\n', 1)[0])
2625 2625 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2626 2626 if names:
2627 2627 desc += ' (%s)' % ' '.join(names)
2628 2628 ui.status(_('grafting %s\n') % desc)
2629 2629 if opts.get('dry_run'):
2630 2630 continue
2631 2631
2632 2632 source = ctx.extra().get('source')
2633 2633 extra = {}
2634 2634 if source:
2635 2635 extra['source'] = source
2636 2636 extra['intermediate-source'] = ctx.hex()
2637 2637 else:
2638 2638 extra['source'] = ctx.hex()
2639 2639 user = ctx.user()
2640 2640 if opts.get('user'):
2641 2641 user = opts['user']
2642 2642 statedata['user'] = user
2643 2643 date = ctx.date()
2644 2644 if opts.get('date'):
2645 2645 date = opts['date']
2646 2646 statedata['date'] = date
2647 2647 message = ctx.description()
2648 2648 if opts.get('log'):
2649 2649 message += '\n(grafted from %s)' % ctx.hex()
2650 2650 statedata['log'] = True
2651 2651
2652 2652 # we don't merge the first commit when continuing
2653 2653 if not cont:
2654 2654 # perform the graft merge with p1(rev) as 'ancestor'
2655 2655 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2656 2656 base = ctx.p1() if basectx is None else basectx
2657 2657 with ui.configoverride(overrides, 'graft'):
2658 2658 stats = mergemod.graft(repo, ctx, base, ['local', 'graft'])
2659 2659 # report any conflicts
2660 2660 if stats.unresolvedcount > 0:
2661 2661 # write out state for --continue
2662 2662 nodes = [repo[rev].hex() for rev in revs[pos:]]
2663 2663 statedata['nodes'] = nodes
2664 2664 stateversion = 1
2665 2665 graftstate.save(stateversion, statedata)
2666 2666 hint = _("use 'hg resolve' and 'hg graft --continue'")
2667 2667 raise error.Abort(
2668 2668 _("unresolved conflicts, can't continue"),
2669 2669 hint=hint)
2670 2670 else:
2671 2671 cont = False
2672 2672
2673 2673 # commit if --no-commit is false
2674 2674 if not opts.get('no_commit'):
2675 2675 node = repo.commit(text=message, user=user, date=date, extra=extra,
2676 2676 editor=editor)
2677 2677 if node is None:
2678 2678 ui.warn(
2679 2679 _('note: graft of %d:%s created no changes to commit\n') %
2680 2680 (ctx.rev(), ctx))
2681 2681 # checking that newnodes exist because old state files won't have it
2682 2682 elif statedata.get('newnodes') is not None:
2683 2683 statedata['newnodes'].append(node)
2684 2684
2685 2685 # remove state when we complete successfully
2686 2686 if not opts.get('dry_run'):
2687 2687 graftstate.delete()
2688 2688
2689 2689 return 0
2690 2690
2691 2691 def _stopgraft(ui, repo, graftstate):
2692 2692 """stop the interrupted graft"""
2693 2693 if not graftstate.exists():
2694 2694 raise error.Abort(_("no interrupted graft found"))
2695 2695 pctx = repo['.']
2696 2696 hg.updaterepo(repo, pctx.node(), overwrite=True)
2697 2697 graftstate.delete()
2698 2698 ui.status(_("stopped the interrupted graft\n"))
2699 2699 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2700 2700 return 0
2701 2701
2702 2702 statemod.addunfinished(
2703 2703 'graft', fname='graftstate', clearable=True, stopflag=True,
2704 2704 continueflag=True, abortfunc=cmdutil.hgabortgraft,
2705 2705 cmdhint=_("use 'hg graft --continue' or 'hg graft --stop' to stop")
2706 2706 )
2707 2707
2708 2708 @command('grep',
2709 2709 [('0', 'print0', None, _('end fields with NUL')),
2710 2710 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2711 2711 ('', 'diff', None, _('print all revisions when the term was introduced '
2712 2712 'or removed')),
2713 2713 ('a', 'text', None, _('treat all files as text')),
2714 2714 ('f', 'follow', None,
2715 2715 _('follow changeset history,'
2716 2716 ' or file history across copies and renames')),
2717 2717 ('i', 'ignore-case', None, _('ignore case when matching')),
2718 2718 ('l', 'files-with-matches', None,
2719 2719 _('print only filenames and revisions that match')),
2720 2720 ('n', 'line-number', None, _('print matching line numbers')),
2721 2721 ('r', 'rev', [],
2722 2722 _('only search files changed within revision range'), _('REV')),
2723 2723 ('', 'all-files', None,
2724 2724 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2725 2725 ('u', 'user', None, _('list the author (long with -v)')),
2726 2726 ('d', 'date', None, _('list the date (short with -q)')),
2727 2727 ] + formatteropts + walkopts,
2728 2728 _('[OPTION]... PATTERN [FILE]...'),
2729 2729 helpcategory=command.CATEGORY_FILE_CONTENTS,
2730 2730 inferrepo=True,
2731 2731 intents={INTENT_READONLY})
2732 2732 def grep(ui, repo, pattern, *pats, **opts):
2733 2733 """search revision history for a pattern in specified files
2734 2734
2735 2735 Search revision history for a regular expression in the specified
2736 2736 files or the entire project.
2737 2737
2738 2738 By default, grep prints the most recent revision number for each
2739 2739 file in which it finds a match. To get it to print every revision
2740 2740 that contains a change in match status ("-" for a match that becomes
2741 2741 a non-match, or "+" for a non-match that becomes a match), use the
2742 2742 --diff flag.
2743 2743
2744 2744 PATTERN can be any Python (roughly Perl-compatible) regular
2745 2745 expression.
2746 2746
2747 2747 If no FILEs are specified (and -f/--follow isn't set), all files in
2748 2748 the repository are searched, including those that don't exist in the
2749 2749 current branch or have been deleted in a prior changeset.
2750 2750
2751 2751 .. container:: verbose
2752 2752
2753 2753 Template:
2754 2754
2755 2755 The following keywords are supported in addition to the common template
2756 2756 keywords and functions. See also :hg:`help templates`.
2757 2757
2758 2758 :change: String. Character denoting insertion ``+`` or removal ``-``.
2759 2759 Available if ``--diff`` is specified.
2760 2760 :lineno: Integer. Line number of the match.
2761 2761 :path: String. Repository-absolute path of the file.
2762 2762 :texts: List of text chunks.
2763 2763
2764 2764 And each entry of ``{texts}`` provides the following sub-keywords.
2765 2765
2766 2766 :matched: Boolean. True if the chunk matches the specified pattern.
2767 2767 :text: String. Chunk content.
2768 2768
2769 2769 See :hg:`help templates.operators` for the list expansion syntax.
2770 2770
2771 2771 Returns 0 if a match is found, 1 otherwise.
2772 2772 """
2773 2773 opts = pycompat.byteskwargs(opts)
2774 2774 diff = opts.get('all') or opts.get('diff')
2775 2775 all_files = opts.get('all_files')
2776 2776 if diff and opts.get('all_files'):
2777 2777 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2778 2778 # TODO: remove "not opts.get('rev')" if --all-files -rMULTIREV gets working
2779 2779 if opts.get('all_files') is None and not opts.get('rev') and not diff:
2780 2780 # experimental config: commands.grep.all-files
2781 2781 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2782 2782 plaingrep = opts.get('all_files') and not opts.get('rev')
2783 2783 if plaingrep:
2784 2784 opts['rev'] = ['wdir()']
2785 2785
2786 2786 reflags = re.M
2787 2787 if opts.get('ignore_case'):
2788 2788 reflags |= re.I
2789 2789 try:
2790 2790 regexp = util.re.compile(pattern, reflags)
2791 2791 except re.error as inst:
2792 2792 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2793 2793 return 1
2794 2794 sep, eol = ':', '\n'
2795 2795 if opts.get('print0'):
2796 2796 sep = eol = '\0'
2797 2797
2798 2798 getfile = util.lrucachefunc(repo.file)
2799 2799
2800 2800 def matchlines(body):
2801 2801 begin = 0
2802 2802 linenum = 0
2803 2803 while begin < len(body):
2804 2804 match = regexp.search(body, begin)
2805 2805 if not match:
2806 2806 break
2807 2807 mstart, mend = match.span()
2808 2808 linenum += body.count('\n', begin, mstart) + 1
2809 2809 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2810 2810 begin = body.find('\n', mend) + 1 or len(body) + 1
2811 2811 lend = begin - 1
2812 2812 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2813 2813
2814 2814 class linestate(object):
2815 2815 def __init__(self, line, linenum, colstart, colend):
2816 2816 self.line = line
2817 2817 self.linenum = linenum
2818 2818 self.colstart = colstart
2819 2819 self.colend = colend
2820 2820
2821 2821 def __hash__(self):
2822 2822 return hash((self.linenum, self.line))
2823 2823
2824 2824 def __eq__(self, other):
2825 2825 return self.line == other.line
2826 2826
2827 2827 def findpos(self):
2828 2828 """Iterate all (start, end) indices of matches"""
2829 2829 yield self.colstart, self.colend
2830 2830 p = self.colend
2831 2831 while p < len(self.line):
2832 2832 m = regexp.search(self.line, p)
2833 2833 if not m:
2834 2834 break
2835 2835 yield m.span()
2836 2836 p = m.end()
2837 2837
2838 2838 matches = {}
2839 2839 copies = {}
2840 2840 def grepbody(fn, rev, body):
2841 2841 matches[rev].setdefault(fn, [])
2842 2842 m = matches[rev][fn]
2843 2843 for lnum, cstart, cend, line in matchlines(body):
2844 2844 s = linestate(line, lnum, cstart, cend)
2845 2845 m.append(s)
2846 2846
2847 2847 def difflinestates(a, b):
2848 2848 sm = difflib.SequenceMatcher(None, a, b)
2849 2849 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2850 2850 if tag == r'insert':
2851 2851 for i in pycompat.xrange(blo, bhi):
2852 2852 yield ('+', b[i])
2853 2853 elif tag == r'delete':
2854 2854 for i in pycompat.xrange(alo, ahi):
2855 2855 yield ('-', a[i])
2856 2856 elif tag == r'replace':
2857 2857 for i in pycompat.xrange(alo, ahi):
2858 2858 yield ('-', a[i])
2859 2859 for i in pycompat.xrange(blo, bhi):
2860 2860 yield ('+', b[i])
2861 2861
2862 2862 uipathfn = scmutil.getuipathfn(repo)
2863 2863 def display(fm, fn, ctx, pstates, states):
2864 2864 rev = scmutil.intrev(ctx)
2865 2865 if fm.isplain():
2866 2866 formatuser = ui.shortuser
2867 2867 else:
2868 2868 formatuser = pycompat.bytestr
2869 2869 if ui.quiet:
2870 2870 datefmt = '%Y-%m-%d'
2871 2871 else:
2872 2872 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2873 2873 found = False
2874 2874 @util.cachefunc
2875 2875 def binary():
2876 2876 flog = getfile(fn)
2877 2877 try:
2878 2878 return stringutil.binary(flog.read(ctx.filenode(fn)))
2879 2879 except error.WdirUnsupported:
2880 2880 return ctx[fn].isbinary()
2881 2881
2882 2882 fieldnamemap = {'linenumber': 'lineno'}
2883 2883 if diff:
2884 2884 iter = difflinestates(pstates, states)
2885 2885 else:
2886 2886 iter = [('', l) for l in states]
2887 2887 for change, l in iter:
2888 2888 fm.startitem()
2889 2889 fm.context(ctx=ctx)
2890 2890 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
2891 2891 fm.plain(uipathfn(fn), label='grep.filename')
2892 2892
2893 2893 cols = [
2894 2894 ('rev', '%d', rev, not plaingrep, ''),
2895 2895 ('linenumber', '%d', l.linenum, opts.get('line_number'), ''),
2896 2896 ]
2897 2897 if diff:
2898 2898 cols.append(
2899 2899 ('change', '%s', change, True,
2900 2900 'grep.inserted ' if change == '+' else 'grep.deleted ')
2901 2901 )
2902 2902 cols.extend([
2903 2903 ('user', '%s', formatuser(ctx.user()), opts.get('user'), ''),
2904 2904 ('date', '%s', fm.formatdate(ctx.date(), datefmt),
2905 2905 opts.get('date'), ''),
2906 2906 ])
2907 2907 for name, fmt, data, cond, extra_label in cols:
2908 2908 if cond:
2909 2909 fm.plain(sep, label='grep.sep')
2910 2910 field = fieldnamemap.get(name, name)
2911 2911 label = extra_label + ('grep.%s' % name)
2912 2912 fm.condwrite(cond, field, fmt, data, label=label)
2913 2913 if not opts.get('files_with_matches'):
2914 2914 fm.plain(sep, label='grep.sep')
2915 2915 if not opts.get('text') and binary():
2916 2916 fm.plain(_(" Binary file matches"))
2917 2917 else:
2918 2918 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2919 2919 fm.plain(eol)
2920 2920 found = True
2921 2921 if opts.get('files_with_matches'):
2922 2922 break
2923 2923 return found
2924 2924
2925 2925 def displaymatches(fm, l):
2926 2926 p = 0
2927 2927 for s, e in l.findpos():
2928 2928 if p < s:
2929 2929 fm.startitem()
2930 2930 fm.write('text', '%s', l.line[p:s])
2931 2931 fm.data(matched=False)
2932 2932 fm.startitem()
2933 2933 fm.write('text', '%s', l.line[s:e], label='grep.match')
2934 2934 fm.data(matched=True)
2935 2935 p = e
2936 2936 if p < len(l.line):
2937 2937 fm.startitem()
2938 2938 fm.write('text', '%s', l.line[p:])
2939 2939 fm.data(matched=False)
2940 2940 fm.end()
2941 2941
2942 2942 skip = set()
2943 2943 revfiles = {}
2944 2944 match = scmutil.match(repo[None], pats, opts)
2945 2945 found = False
2946 2946 follow = opts.get('follow')
2947 2947
2948 2948 getrenamed = scmutil.getrenamedfn(repo)
2949 2949 def prep(ctx, fns):
2950 2950 rev = ctx.rev()
2951 2951 pctx = ctx.p1()
2952 2952 parent = pctx.rev()
2953 2953 matches.setdefault(rev, {})
2954 2954 matches.setdefault(parent, {})
2955 2955 files = revfiles.setdefault(rev, [])
2956 2956 for fn in fns:
2957 2957 flog = getfile(fn)
2958 2958 try:
2959 2959 fnode = ctx.filenode(fn)
2960 2960 except error.LookupError:
2961 2961 continue
2962 2962
2963 2963 copy = None
2964 2964 if follow:
2965 2965 copy = getrenamed(fn, rev)
2966 2966 if copy:
2967 2967 copies.setdefault(rev, {})[fn] = copy
2968 2968 if fn in skip:
2969 2969 skip.add(copy)
2970 2970 if fn in skip:
2971 2971 continue
2972 2972 files.append(fn)
2973 2973
2974 2974 if fn not in matches[rev]:
2975 2975 try:
2976 2976 content = flog.read(fnode)
2977 2977 except error.WdirUnsupported:
2978 2978 content = ctx[fn].data()
2979 2979 grepbody(fn, rev, content)
2980 2980
2981 2981 pfn = copy or fn
2982 2982 if pfn not in matches[parent]:
2983 2983 try:
2984 2984 fnode = pctx.filenode(pfn)
2985 2985 grepbody(pfn, parent, flog.read(fnode))
2986 2986 except error.LookupError:
2987 2987 pass
2988 2988
2989 2989 ui.pager('grep')
2990 2990 fm = ui.formatter('grep', opts)
2991 2991 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2992 2992 rev = ctx.rev()
2993 2993 parent = ctx.p1().rev()
2994 2994 for fn in sorted(revfiles.get(rev, [])):
2995 2995 states = matches[rev][fn]
2996 2996 copy = copies.get(rev, {}).get(fn)
2997 2997 if fn in skip:
2998 2998 if copy:
2999 2999 skip.add(copy)
3000 3000 continue
3001 3001 pstates = matches.get(parent, {}).get(copy or fn, [])
3002 3002 if pstates or states:
3003 3003 r = display(fm, fn, ctx, pstates, states)
3004 3004 found = found or r
3005 3005 if r and not diff and not all_files:
3006 3006 skip.add(fn)
3007 3007 if copy:
3008 3008 skip.add(copy)
3009 3009 del revfiles[rev]
3010 3010 # We will keep the matches dict for the duration of the window
3011 3011 # clear the matches dict once the window is over
3012 3012 if not revfiles:
3013 3013 matches.clear()
3014 3014 fm.end()
3015 3015
3016 3016 return not found
3017 3017
3018 3018 @command('heads',
3019 3019 [('r', 'rev', '',
3020 3020 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3021 3021 ('t', 'topo', False, _('show topological heads only')),
3022 3022 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3023 3023 ('c', 'closed', False, _('show normal and closed branch heads')),
3024 3024 ] + templateopts,
3025 3025 _('[-ct] [-r STARTREV] [REV]...'),
3026 3026 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3027 3027 intents={INTENT_READONLY})
3028 3028 def heads(ui, repo, *branchrevs, **opts):
3029 3029 """show branch heads
3030 3030
3031 3031 With no arguments, show all open branch heads in the repository.
3032 3032 Branch heads are changesets that have no descendants on the
3033 3033 same branch. They are where development generally takes place and
3034 3034 are the usual targets for update and merge operations.
3035 3035
3036 3036 If one or more REVs are given, only open branch heads on the
3037 3037 branches associated with the specified changesets are shown. This
3038 3038 means that you can use :hg:`heads .` to see the heads on the
3039 3039 currently checked-out branch.
3040 3040
3041 3041 If -c/--closed is specified, also show branch heads marked closed
3042 3042 (see :hg:`commit --close-branch`).
3043 3043
3044 3044 If STARTREV is specified, only those heads that are descendants of
3045 3045 STARTREV will be displayed.
3046 3046
3047 3047 If -t/--topo is specified, named branch mechanics will be ignored and only
3048 3048 topological heads (changesets with no children) will be shown.
3049 3049
3050 3050 Returns 0 if matching heads are found, 1 if not.
3051 3051 """
3052 3052
3053 3053 opts = pycompat.byteskwargs(opts)
3054 3054 start = None
3055 3055 rev = opts.get('rev')
3056 3056 if rev:
3057 3057 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3058 3058 start = scmutil.revsingle(repo, rev, None).node()
3059 3059
3060 3060 if opts.get('topo'):
3061 3061 heads = [repo[h] for h in repo.heads(start)]
3062 3062 else:
3063 3063 heads = []
3064 3064 for branch in repo.branchmap():
3065 3065 heads += repo.branchheads(branch, start, opts.get('closed'))
3066 3066 heads = [repo[h] for h in heads]
3067 3067
3068 3068 if branchrevs:
3069 3069 branches = set(repo[r].branch()
3070 3070 for r in scmutil.revrange(repo, branchrevs))
3071 3071 heads = [h for h in heads if h.branch() in branches]
3072 3072
3073 3073 if opts.get('active') and branchrevs:
3074 3074 dagheads = repo.heads(start)
3075 3075 heads = [h for h in heads if h.node() in dagheads]
3076 3076
3077 3077 if branchrevs:
3078 3078 haveheads = set(h.branch() for h in heads)
3079 3079 if branches - haveheads:
3080 3080 headless = ', '.join(b for b in branches - haveheads)
3081 3081 msg = _('no open branch heads found on branches %s')
3082 3082 if opts.get('rev'):
3083 3083 msg += _(' (started at %s)') % opts['rev']
3084 3084 ui.warn((msg + '\n') % headless)
3085 3085
3086 3086 if not heads:
3087 3087 return 1
3088 3088
3089 3089 ui.pager('heads')
3090 3090 heads = sorted(heads, key=lambda x: -x.rev())
3091 3091 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3092 3092 for ctx in heads:
3093 3093 displayer.show(ctx)
3094 3094 displayer.close()
3095 3095
3096 3096 @command('help',
3097 3097 [('e', 'extension', None, _('show only help for extensions')),
3098 3098 ('c', 'command', None, _('show only help for commands')),
3099 3099 ('k', 'keyword', None, _('show topics matching keyword')),
3100 3100 ('s', 'system', [],
3101 3101 _('show help for specific platform(s)'), _('PLATFORM')),
3102 3102 ],
3103 3103 _('[-eck] [-s PLATFORM] [TOPIC]'),
3104 3104 helpcategory=command.CATEGORY_HELP,
3105 3105 norepo=True,
3106 3106 intents={INTENT_READONLY})
3107 3107 def help_(ui, name=None, **opts):
3108 3108 """show help for a given topic or a help overview
3109 3109
3110 3110 With no arguments, print a list of commands with short help messages.
3111 3111
3112 3112 Given a topic, extension, or command name, print help for that
3113 3113 topic.
3114 3114
3115 3115 Returns 0 if successful.
3116 3116 """
3117 3117
3118 3118 keep = opts.get(r'system') or []
3119 3119 if len(keep) == 0:
3120 3120 if pycompat.sysplatform.startswith('win'):
3121 3121 keep.append('windows')
3122 3122 elif pycompat.sysplatform == 'OpenVMS':
3123 3123 keep.append('vms')
3124 3124 elif pycompat.sysplatform == 'plan9':
3125 3125 keep.append('plan9')
3126 3126 else:
3127 3127 keep.append('unix')
3128 3128 keep.append(pycompat.sysplatform.lower())
3129 3129 if ui.verbose:
3130 3130 keep.append('verbose')
3131 3131
3132 3132 commands = sys.modules[__name__]
3133 3133 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3134 3134 ui.pager('help')
3135 3135 ui.write(formatted)
3136 3136
3137 3137
3138 3138 @command('identify|id',
3139 3139 [('r', 'rev', '',
3140 3140 _('identify the specified revision'), _('REV')),
3141 3141 ('n', 'num', None, _('show local revision number')),
3142 3142 ('i', 'id', None, _('show global revision id')),
3143 3143 ('b', 'branch', None, _('show branch')),
3144 3144 ('t', 'tags', None, _('show tags')),
3145 3145 ('B', 'bookmarks', None, _('show bookmarks')),
3146 3146 ] + remoteopts + formatteropts,
3147 3147 _('[-nibtB] [-r REV] [SOURCE]'),
3148 3148 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3149 3149 optionalrepo=True,
3150 3150 intents={INTENT_READONLY})
3151 3151 def identify(ui, repo, source=None, rev=None,
3152 3152 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3153 3153 """identify the working directory or specified revision
3154 3154
3155 3155 Print a summary identifying the repository state at REV using one or
3156 3156 two parent hash identifiers, followed by a "+" if the working
3157 3157 directory has uncommitted changes, the branch name (if not default),
3158 3158 a list of tags, and a list of bookmarks.
3159 3159
3160 3160 When REV is not given, print a summary of the current state of the
3161 3161 repository including the working directory. Specify -r. to get information
3162 3162 of the working directory parent without scanning uncommitted changes.
3163 3163
3164 3164 Specifying a path to a repository root or Mercurial bundle will
3165 3165 cause lookup to operate on that repository/bundle.
3166 3166
3167 3167 .. container:: verbose
3168 3168
3169 3169 Template:
3170 3170
3171 3171 The following keywords are supported in addition to the common template
3172 3172 keywords and functions. See also :hg:`help templates`.
3173 3173
3174 3174 :dirty: String. Character ``+`` denoting if the working directory has
3175 3175 uncommitted changes.
3176 3176 :id: String. One or two nodes, optionally followed by ``+``.
3177 3177 :parents: List of strings. Parent nodes of the changeset.
3178 3178
3179 3179 Examples:
3180 3180
3181 3181 - generate a build identifier for the working directory::
3182 3182
3183 3183 hg id --id > build-id.dat
3184 3184
3185 3185 - find the revision corresponding to a tag::
3186 3186
3187 3187 hg id -n -r 1.3
3188 3188
3189 3189 - check the most recent revision of a remote repository::
3190 3190
3191 3191 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3192 3192
3193 3193 See :hg:`log` for generating more information about specific revisions,
3194 3194 including full hash identifiers.
3195 3195
3196 3196 Returns 0 if successful.
3197 3197 """
3198 3198
3199 3199 opts = pycompat.byteskwargs(opts)
3200 3200 if not repo and not source:
3201 3201 raise error.Abort(_("there is no Mercurial repository here "
3202 3202 "(.hg not found)"))
3203 3203
3204 3204 default = not (num or id or branch or tags or bookmarks)
3205 3205 output = []
3206 3206 revs = []
3207 3207
3208 3208 if source:
3209 3209 source, branches = hg.parseurl(ui.expandpath(source))
3210 3210 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3211 3211 repo = peer.local()
3212 3212 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3213 3213
3214 3214 fm = ui.formatter('identify', opts)
3215 3215 fm.startitem()
3216 3216
3217 3217 if not repo:
3218 3218 if num or branch or tags:
3219 3219 raise error.Abort(
3220 3220 _("can't query remote revision number, branch, or tags"))
3221 3221 if not rev and revs:
3222 3222 rev = revs[0]
3223 3223 if not rev:
3224 3224 rev = "tip"
3225 3225
3226 3226 remoterev = peer.lookup(rev)
3227 3227 hexrev = fm.hexfunc(remoterev)
3228 3228 if default or id:
3229 3229 output = [hexrev]
3230 3230 fm.data(id=hexrev)
3231 3231
3232 3232 @util.cachefunc
3233 3233 def getbms():
3234 3234 bms = []
3235 3235
3236 3236 if 'bookmarks' in peer.listkeys('namespaces'):
3237 3237 hexremoterev = hex(remoterev)
3238 3238 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3239 3239 if bmr == hexremoterev]
3240 3240
3241 3241 return sorted(bms)
3242 3242
3243 3243 if fm.isplain():
3244 3244 if bookmarks:
3245 3245 output.extend(getbms())
3246 3246 elif default and not ui.quiet:
3247 3247 # multiple bookmarks for a single parent separated by '/'
3248 3248 bm = '/'.join(getbms())
3249 3249 if bm:
3250 3250 output.append(bm)
3251 3251 else:
3252 3252 fm.data(node=hex(remoterev))
3253 3253 if bookmarks or 'bookmarks' in fm.datahint():
3254 3254 fm.data(bookmarks=fm.formatlist(getbms(), name='bookmark'))
3255 3255 else:
3256 3256 if rev:
3257 3257 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3258 3258 ctx = scmutil.revsingle(repo, rev, None)
3259 3259
3260 3260 if ctx.rev() is None:
3261 3261 ctx = repo[None]
3262 3262 parents = ctx.parents()
3263 3263 taglist = []
3264 3264 for p in parents:
3265 3265 taglist.extend(p.tags())
3266 3266
3267 3267 dirty = ""
3268 3268 if ctx.dirty(missing=True, merge=False, branch=False):
3269 3269 dirty = '+'
3270 3270 fm.data(dirty=dirty)
3271 3271
3272 3272 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3273 3273 if default or id:
3274 3274 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3275 3275 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3276 3276
3277 3277 if num:
3278 3278 numoutput = ["%d" % p.rev() for p in parents]
3279 3279 output.append("%s%s" % ('+'.join(numoutput), dirty))
3280 3280
3281 3281 fm.data(parents=fm.formatlist([fm.hexfunc(p.node())
3282 3282 for p in parents], name='node'))
3283 3283 else:
3284 3284 hexoutput = fm.hexfunc(ctx.node())
3285 3285 if default or id:
3286 3286 output = [hexoutput]
3287 3287 fm.data(id=hexoutput)
3288 3288
3289 3289 if num:
3290 3290 output.append(pycompat.bytestr(ctx.rev()))
3291 3291 taglist = ctx.tags()
3292 3292
3293 3293 if default and not ui.quiet:
3294 3294 b = ctx.branch()
3295 3295 if b != 'default':
3296 3296 output.append("(%s)" % b)
3297 3297
3298 3298 # multiple tags for a single parent separated by '/'
3299 3299 t = '/'.join(taglist)
3300 3300 if t:
3301 3301 output.append(t)
3302 3302
3303 3303 # multiple bookmarks for a single parent separated by '/'
3304 3304 bm = '/'.join(ctx.bookmarks())
3305 3305 if bm:
3306 3306 output.append(bm)
3307 3307 else:
3308 3308 if branch:
3309 3309 output.append(ctx.branch())
3310 3310
3311 3311 if tags:
3312 3312 output.extend(taglist)
3313 3313
3314 3314 if bookmarks:
3315 3315 output.extend(ctx.bookmarks())
3316 3316
3317 3317 fm.data(node=ctx.hex())
3318 3318 fm.data(branch=ctx.branch())
3319 3319 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3320 3320 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3321 3321 fm.context(ctx=ctx)
3322 3322
3323 3323 fm.plain("%s\n" % ' '.join(output))
3324 3324 fm.end()
3325 3325
3326 3326 @command('import|patch',
3327 3327 [('p', 'strip', 1,
3328 3328 _('directory strip option for patch. This has the same '
3329 3329 'meaning as the corresponding patch option'), _('NUM')),
3330 3330 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3331 3331 ('e', 'edit', False, _('invoke editor on commit messages')),
3332 3332 ('f', 'force', None,
3333 3333 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3334 3334 ('', 'no-commit', None,
3335 3335 _("don't commit, just update the working directory")),
3336 3336 ('', 'bypass', None,
3337 3337 _("apply patch without touching the working directory")),
3338 3338 ('', 'partial', None,
3339 3339 _('commit even if some hunks fail')),
3340 3340 ('', 'exact', None,
3341 3341 _('abort if patch would apply lossily')),
3342 3342 ('', 'prefix', '',
3343 3343 _('apply patch to subdirectory'), _('DIR')),
3344 3344 ('', 'import-branch', None,
3345 3345 _('use any branch information in patch (implied by --exact)'))] +
3346 3346 commitopts + commitopts2 + similarityopts,
3347 3347 _('[OPTION]... PATCH...'),
3348 3348 helpcategory=command.CATEGORY_IMPORT_EXPORT)
3349 3349 def import_(ui, repo, patch1=None, *patches, **opts):
3350 3350 """import an ordered set of patches
3351 3351
3352 3352 Import a list of patches and commit them individually (unless
3353 3353 --no-commit is specified).
3354 3354
3355 3355 To read a patch from standard input (stdin), use "-" as the patch
3356 3356 name. If a URL is specified, the patch will be downloaded from
3357 3357 there.
3358 3358
3359 3359 Import first applies changes to the working directory (unless
3360 3360 --bypass is specified), import will abort if there are outstanding
3361 3361 changes.
3362 3362
3363 3363 Use --bypass to apply and commit patches directly to the
3364 3364 repository, without affecting the working directory. Without
3365 3365 --exact, patches will be applied on top of the working directory
3366 3366 parent revision.
3367 3367
3368 3368 You can import a patch straight from a mail message. Even patches
3369 3369 as attachments work (to use the body part, it must have type
3370 3370 text/plain or text/x-patch). From and Subject headers of email
3371 3371 message are used as default committer and commit message. All
3372 3372 text/plain body parts before first diff are added to the commit
3373 3373 message.
3374 3374
3375 3375 If the imported patch was generated by :hg:`export`, user and
3376 3376 description from patch override values from message headers and
3377 3377 body. Values given on command line with -m/--message and -u/--user
3378 3378 override these.
3379 3379
3380 3380 If --exact is specified, import will set the working directory to
3381 3381 the parent of each patch before applying it, and will abort if the
3382 3382 resulting changeset has a different ID than the one recorded in
3383 3383 the patch. This will guard against various ways that portable
3384 3384 patch formats and mail systems might fail to transfer Mercurial
3385 3385 data or metadata. See :hg:`bundle` for lossless transmission.
3386 3386
3387 3387 Use --partial to ensure a changeset will be created from the patch
3388 3388 even if some hunks fail to apply. Hunks that fail to apply will be
3389 3389 written to a <target-file>.rej file. Conflicts can then be resolved
3390 3390 by hand before :hg:`commit --amend` is run to update the created
3391 3391 changeset. This flag exists to let people import patches that
3392 3392 partially apply without losing the associated metadata (author,
3393 3393 date, description, ...).
3394 3394
3395 3395 .. note::
3396 3396
3397 3397 When no hunks apply cleanly, :hg:`import --partial` will create
3398 3398 an empty changeset, importing only the patch metadata.
3399 3399
3400 3400 With -s/--similarity, hg will attempt to discover renames and
3401 3401 copies in the patch in the same way as :hg:`addremove`.
3402 3402
3403 3403 It is possible to use external patch programs to perform the patch
3404 3404 by setting the ``ui.patch`` configuration option. For the default
3405 3405 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3406 3406 See :hg:`help config` for more information about configuration
3407 3407 files and how to use these options.
3408 3408
3409 3409 See :hg:`help dates` for a list of formats valid for -d/--date.
3410 3410
3411 3411 .. container:: verbose
3412 3412
3413 3413 Examples:
3414 3414
3415 3415 - import a traditional patch from a website and detect renames::
3416 3416
3417 3417 hg import -s 80 http://example.com/bugfix.patch
3418 3418
3419 3419 - import a changeset from an hgweb server::
3420 3420
3421 3421 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3422 3422
3423 3423 - import all the patches in an Unix-style mbox::
3424 3424
3425 3425 hg import incoming-patches.mbox
3426 3426
3427 3427 - import patches from stdin::
3428 3428
3429 3429 hg import -
3430 3430
3431 3431 - attempt to exactly restore an exported changeset (not always
3432 3432 possible)::
3433 3433
3434 3434 hg import --exact proposed-fix.patch
3435 3435
3436 3436 - use an external tool to apply a patch which is too fuzzy for
3437 3437 the default internal tool.
3438 3438
3439 3439 hg import --config ui.patch="patch --merge" fuzzy.patch
3440 3440
3441 3441 - change the default fuzzing from 2 to a less strict 7
3442 3442
3443 3443 hg import --config ui.fuzz=7 fuzz.patch
3444 3444
3445 3445 Returns 0 on success, 1 on partial success (see --partial).
3446 3446 """
3447 3447
3448 3448 opts = pycompat.byteskwargs(opts)
3449 3449 if not patch1:
3450 3450 raise error.Abort(_('need at least one patch to import'))
3451 3451
3452 3452 patches = (patch1,) + patches
3453 3453
3454 3454 date = opts.get('date')
3455 3455 if date:
3456 3456 opts['date'] = dateutil.parsedate(date)
3457 3457
3458 3458 exact = opts.get('exact')
3459 3459 update = not opts.get('bypass')
3460 3460 if not update and opts.get('no_commit'):
3461 3461 raise error.Abort(_('cannot use --no-commit with --bypass'))
3462 3462 try:
3463 3463 sim = float(opts.get('similarity') or 0)
3464 3464 except ValueError:
3465 3465 raise error.Abort(_('similarity must be a number'))
3466 3466 if sim < 0 or sim > 100:
3467 3467 raise error.Abort(_('similarity must be between 0 and 100'))
3468 3468 if sim and not update:
3469 3469 raise error.Abort(_('cannot use --similarity with --bypass'))
3470 3470 if exact:
3471 3471 if opts.get('edit'):
3472 3472 raise error.Abort(_('cannot use --exact with --edit'))
3473 3473 if opts.get('prefix'):
3474 3474 raise error.Abort(_('cannot use --exact with --prefix'))
3475 3475
3476 3476 base = opts["base"]
3477 3477 msgs = []
3478 3478 ret = 0
3479 3479
3480 3480 with repo.wlock():
3481 3481 if update:
3482 3482 cmdutil.checkunfinished(repo)
3483 3483 if (exact or not opts.get('force')):
3484 3484 cmdutil.bailifchanged(repo)
3485 3485
3486 3486 if not opts.get('no_commit'):
3487 3487 lock = repo.lock
3488 3488 tr = lambda: repo.transaction('import')
3489 3489 dsguard = util.nullcontextmanager
3490 3490 else:
3491 3491 lock = util.nullcontextmanager
3492 3492 tr = util.nullcontextmanager
3493 3493 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3494 3494 with lock(), tr(), dsguard():
3495 3495 parents = repo[None].parents()
3496 3496 for patchurl in patches:
3497 3497 if patchurl == '-':
3498 3498 ui.status(_('applying patch from stdin\n'))
3499 3499 patchfile = ui.fin
3500 3500 patchurl = 'stdin' # for error message
3501 3501 else:
3502 3502 patchurl = os.path.join(base, patchurl)
3503 3503 ui.status(_('applying %s\n') % patchurl)
3504 3504 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
3505 3505
3506 3506 haspatch = False
3507 3507 for hunk in patch.split(patchfile):
3508 3508 with patch.extract(ui, hunk) as patchdata:
3509 3509 msg, node, rej = cmdutil.tryimportone(ui, repo,
3510 3510 patchdata,
3511 3511 parents, opts,
3512 3512 msgs, hg.clean)
3513 3513 if msg:
3514 3514 haspatch = True
3515 3515 ui.note(msg + '\n')
3516 3516 if update or exact:
3517 3517 parents = repo[None].parents()
3518 3518 else:
3519 3519 parents = [repo[node]]
3520 3520 if rej:
3521 3521 ui.write_err(_("patch applied partially\n"))
3522 3522 ui.write_err(_("(fix the .rej files and run "
3523 3523 "`hg commit --amend`)\n"))
3524 3524 ret = 1
3525 3525 break
3526 3526
3527 3527 if not haspatch:
3528 3528 raise error.Abort(_('%s: no diffs found') % patchurl)
3529 3529
3530 3530 if msgs:
3531 3531 repo.savecommitmessage('\n* * *\n'.join(msgs))
3532 3532 return ret
3533 3533
3534 3534 @command('incoming|in',
3535 3535 [('f', 'force', None,
3536 3536 _('run even if remote repository is unrelated')),
3537 3537 ('n', 'newest-first', None, _('show newest record first')),
3538 3538 ('', 'bundle', '',
3539 3539 _('file to store the bundles into'), _('FILE')),
3540 3540 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3541 3541 ('B', 'bookmarks', False, _("compare bookmarks")),
3542 3542 ('b', 'branch', [],
3543 3543 _('a specific branch you would like to pull'), _('BRANCH')),
3544 3544 ] + logopts + remoteopts + subrepoopts,
3545 3545 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
3546 3546 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
3547 3547 def incoming(ui, repo, source="default", **opts):
3548 3548 """show new changesets found in source
3549 3549
3550 3550 Show new changesets found in the specified path/URL or the default
3551 3551 pull location. These are the changesets that would have been pulled
3552 3552 by :hg:`pull` at the time you issued this command.
3553 3553
3554 3554 See pull for valid source format details.
3555 3555
3556 3556 .. container:: verbose
3557 3557
3558 3558 With -B/--bookmarks, the result of bookmark comparison between
3559 3559 local and remote repositories is displayed. With -v/--verbose,
3560 3560 status is also displayed for each bookmark like below::
3561 3561
3562 3562 BM1 01234567890a added
3563 3563 BM2 1234567890ab advanced
3564 3564 BM3 234567890abc diverged
3565 3565 BM4 34567890abcd changed
3566 3566
3567 3567 The action taken locally when pulling depends on the
3568 3568 status of each bookmark:
3569 3569
3570 3570 :``added``: pull will create it
3571 3571 :``advanced``: pull will update it
3572 3572 :``diverged``: pull will create a divergent bookmark
3573 3573 :``changed``: result depends on remote changesets
3574 3574
3575 3575 From the point of view of pulling behavior, bookmark
3576 3576 existing only in the remote repository are treated as ``added``,
3577 3577 even if it is in fact locally deleted.
3578 3578
3579 3579 .. container:: verbose
3580 3580
3581 3581 For remote repository, using --bundle avoids downloading the
3582 3582 changesets twice if the incoming is followed by a pull.
3583 3583
3584 3584 Examples:
3585 3585
3586 3586 - show incoming changes with patches and full description::
3587 3587
3588 3588 hg incoming -vp
3589 3589
3590 3590 - show incoming changes excluding merges, store a bundle::
3591 3591
3592 3592 hg in -vpM --bundle incoming.hg
3593 3593 hg pull incoming.hg
3594 3594
3595 3595 - briefly list changes inside a bundle::
3596 3596
3597 3597 hg in changes.hg -T "{desc|firstline}\\n"
3598 3598
3599 3599 Returns 0 if there are incoming changes, 1 otherwise.
3600 3600 """
3601 3601 opts = pycompat.byteskwargs(opts)
3602 3602 if opts.get('graph'):
3603 3603 logcmdutil.checkunsupportedgraphflags([], opts)
3604 3604 def display(other, chlist, displayer):
3605 3605 revdag = logcmdutil.graphrevs(other, chlist, opts)
3606 3606 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3607 3607 graphmod.asciiedges)
3608 3608
3609 3609 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3610 3610 return 0
3611 3611
3612 3612 if opts.get('bundle') and opts.get('subrepos'):
3613 3613 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3614 3614
3615 3615 if opts.get('bookmarks'):
3616 3616 source, branches = hg.parseurl(ui.expandpath(source),
3617 3617 opts.get('branch'))
3618 3618 other = hg.peer(repo, opts, source)
3619 3619 if 'bookmarks' not in other.listkeys('namespaces'):
3620 3620 ui.warn(_("remote doesn't support bookmarks\n"))
3621 3621 return 0
3622 3622 ui.pager('incoming')
3623 3623 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3624 3624 return bookmarks.incoming(ui, repo, other)
3625 3625
3626 3626 repo._subtoppath = ui.expandpath(source)
3627 3627 try:
3628 3628 return hg.incoming(ui, repo, source, opts)
3629 3629 finally:
3630 3630 del repo._subtoppath
3631 3631
3632 3632
3633 3633 @command('init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3634 3634 helpcategory=command.CATEGORY_REPO_CREATION,
3635 3635 helpbasic=True, norepo=True)
3636 3636 def init(ui, dest=".", **opts):
3637 3637 """create a new repository in the given directory
3638 3638
3639 3639 Initialize a new repository in the given directory. If the given
3640 3640 directory does not exist, it will be created.
3641 3641
3642 3642 If no directory is given, the current directory is used.
3643 3643
3644 3644 It is possible to specify an ``ssh://`` URL as the destination.
3645 3645 See :hg:`help urls` for more information.
3646 3646
3647 3647 Returns 0 on success.
3648 3648 """
3649 3649 opts = pycompat.byteskwargs(opts)
3650 3650 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3651 3651
3652 3652 @command('locate',
3653 3653 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3654 3654 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3655 3655 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3656 3656 ] + walkopts,
3657 3657 _('[OPTION]... [PATTERN]...'),
3658 3658 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
3659 3659 def locate(ui, repo, *pats, **opts):
3660 3660 """locate files matching specific patterns (DEPRECATED)
3661 3661
3662 3662 Print files under Mercurial control in the working directory whose
3663 3663 names match the given patterns.
3664 3664
3665 3665 By default, this command searches all directories in the working
3666 3666 directory. To search just the current directory and its
3667 3667 subdirectories, use "--include .".
3668 3668
3669 3669 If no patterns are given to match, this command prints the names
3670 3670 of all files under Mercurial control in the working directory.
3671 3671
3672 3672 If you want to feed the output of this command into the "xargs"
3673 3673 command, use the -0 option to both this command and "xargs". This
3674 3674 will avoid the problem of "xargs" treating single filenames that
3675 3675 contain whitespace as multiple filenames.
3676 3676
3677 3677 See :hg:`help files` for a more versatile command.
3678 3678
3679 3679 Returns 0 if a match is found, 1 otherwise.
3680 3680 """
3681 3681 opts = pycompat.byteskwargs(opts)
3682 3682 if opts.get('print0'):
3683 3683 end = '\0'
3684 3684 else:
3685 3685 end = '\n'
3686 3686 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3687 3687
3688 3688 ret = 1
3689 3689 m = scmutil.match(ctx, pats, opts, default='relglob',
3690 3690 badfn=lambda x, y: False)
3691 3691
3692 3692 ui.pager('locate')
3693 3693 if ctx.rev() is None:
3694 3694 # When run on the working copy, "locate" includes removed files, so
3695 3695 # we get the list of files from the dirstate.
3696 3696 filesgen = sorted(repo.dirstate.matches(m))
3697 3697 else:
3698 3698 filesgen = ctx.matches(m)
3699 3699 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
3700 3700 for abs in filesgen:
3701 3701 if opts.get('fullpath'):
3702 3702 ui.write(repo.wjoin(abs), end)
3703 3703 else:
3704 3704 ui.write(uipathfn(abs), end)
3705 3705 ret = 0
3706 3706
3707 3707 return ret
3708 3708
3709 3709 @command('log|history',
3710 3710 [('f', 'follow', None,
3711 3711 _('follow changeset history, or file history across copies and renames')),
3712 3712 ('', 'follow-first', None,
3713 3713 _('only follow the first parent of merge changesets (DEPRECATED)')),
3714 3714 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3715 3715 ('C', 'copies', None, _('show copied files')),
3716 3716 ('k', 'keyword', [],
3717 3717 _('do case-insensitive search for a given text'), _('TEXT')),
3718 3718 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3719 3719 ('L', 'line-range', [],
3720 3720 _('follow line range of specified file (EXPERIMENTAL)'),
3721 3721 _('FILE,RANGE')),
3722 3722 ('', 'removed', None, _('include revisions where files were removed')),
3723 3723 ('m', 'only-merges', None,
3724 3724 _('show only merges (DEPRECATED) (use -r "merge()" instead)')),
3725 3725 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3726 3726 ('', 'only-branch', [],
3727 3727 _('show only changesets within the given named branch (DEPRECATED)'),
3728 3728 _('BRANCH')),
3729 3729 ('b', 'branch', [],
3730 3730 _('show changesets within the given named branch'), _('BRANCH')),
3731 3731 ('P', 'prune', [],
3732 3732 _('do not display revision or any of its ancestors'), _('REV')),
3733 3733 ] + logopts + walkopts,
3734 3734 _('[OPTION]... [FILE]'),
3735 3735 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3736 3736 helpbasic=True, inferrepo=True,
3737 3737 intents={INTENT_READONLY})
3738 3738 def log(ui, repo, *pats, **opts):
3739 3739 """show revision history of entire repository or files
3740 3740
3741 3741 Print the revision history of the specified files or the entire
3742 3742 project.
3743 3743
3744 3744 If no revision range is specified, the default is ``tip:0`` unless
3745 3745 --follow is set, in which case the working directory parent is
3746 3746 used as the starting revision.
3747 3747
3748 3748 File history is shown without following rename or copy history of
3749 3749 files. Use -f/--follow with a filename to follow history across
3750 3750 renames and copies. --follow without a filename will only show
3751 3751 ancestors of the starting revision.
3752 3752
3753 3753 By default this command prints revision number and changeset id,
3754 3754 tags, non-trivial parents, user, date and time, and a summary for
3755 3755 each commit. When the -v/--verbose switch is used, the list of
3756 3756 changed files and full commit message are shown.
3757 3757
3758 3758 With --graph the revisions are shown as an ASCII art DAG with the most
3759 3759 recent changeset at the top.
3760 3760 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3761 3761 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3762 3762 changeset from the lines below is a parent of the 'o' merge on the same
3763 3763 line.
3764 3764 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3765 3765 of a '|' indicates one or more revisions in a path are omitted.
3766 3766
3767 3767 .. container:: verbose
3768 3768
3769 3769 Use -L/--line-range FILE,M:N options to follow the history of lines
3770 3770 from M to N in FILE. With -p/--patch only diff hunks affecting
3771 3771 specified line range will be shown. This option requires --follow;
3772 3772 it can be specified multiple times. Currently, this option is not
3773 3773 compatible with --graph. This option is experimental.
3774 3774
3775 3775 .. note::
3776 3776
3777 3777 :hg:`log --patch` may generate unexpected diff output for merge
3778 3778 changesets, as it will only compare the merge changeset against
3779 3779 its first parent. Also, only files different from BOTH parents
3780 3780 will appear in files:.
3781 3781
3782 3782 .. note::
3783 3783
3784 3784 For performance reasons, :hg:`log FILE` may omit duplicate changes
3785 3785 made on branches and will not show removals or mode changes. To
3786 3786 see all such changes, use the --removed switch.
3787 3787
3788 3788 .. container:: verbose
3789 3789
3790 3790 .. note::
3791 3791
3792 3792 The history resulting from -L/--line-range options depends on diff
3793 3793 options; for instance if white-spaces are ignored, respective changes
3794 3794 with only white-spaces in specified line range will not be listed.
3795 3795
3796 3796 .. container:: verbose
3797 3797
3798 3798 Some examples:
3799 3799
3800 3800 - changesets with full descriptions and file lists::
3801 3801
3802 3802 hg log -v
3803 3803
3804 3804 - changesets ancestral to the working directory::
3805 3805
3806 3806 hg log -f
3807 3807
3808 3808 - last 10 commits on the current branch::
3809 3809
3810 3810 hg log -l 10 -b .
3811 3811
3812 3812 - changesets showing all modifications of a file, including removals::
3813 3813
3814 3814 hg log --removed file.c
3815 3815
3816 3816 - all changesets that touch a directory, with diffs, excluding merges::
3817 3817
3818 3818 hg log -Mp lib/
3819 3819
3820 3820 - all revision numbers that match a keyword::
3821 3821
3822 3822 hg log -k bug --template "{rev}\\n"
3823 3823
3824 3824 - the full hash identifier of the working directory parent::
3825 3825
3826 3826 hg log -r . --template "{node}\\n"
3827 3827
3828 3828 - list available log templates::
3829 3829
3830 3830 hg log -T list
3831 3831
3832 3832 - check if a given changeset is included in a tagged release::
3833 3833
3834 3834 hg log -r "a21ccf and ancestor(1.9)"
3835 3835
3836 3836 - find all changesets by some user in a date range::
3837 3837
3838 3838 hg log -k alice -d "may 2008 to jul 2008"
3839 3839
3840 3840 - summary of all changesets after the last tag::
3841 3841
3842 3842 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3843 3843
3844 3844 - changesets touching lines 13 to 23 for file.c::
3845 3845
3846 3846 hg log -L file.c,13:23
3847 3847
3848 3848 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3849 3849 main.c with patch::
3850 3850
3851 3851 hg log -L file.c,13:23 -L main.c,2:6 -p
3852 3852
3853 3853 See :hg:`help dates` for a list of formats valid for -d/--date.
3854 3854
3855 3855 See :hg:`help revisions` for more about specifying and ordering
3856 3856 revisions.
3857 3857
3858 3858 See :hg:`help templates` for more about pre-packaged styles and
3859 3859 specifying custom templates. The default template used by the log
3860 3860 command can be customized via the ``ui.logtemplate`` configuration
3861 3861 setting.
3862 3862
3863 3863 Returns 0 on success.
3864 3864
3865 3865 """
3866 3866 opts = pycompat.byteskwargs(opts)
3867 3867 linerange = opts.get('line_range')
3868 3868
3869 3869 if linerange and not opts.get('follow'):
3870 3870 raise error.Abort(_('--line-range requires --follow'))
3871 3871
3872 3872 if linerange and pats:
3873 3873 # TODO: take pats as patterns with no line-range filter
3874 3874 raise error.Abort(
3875 3875 _('FILE arguments are not compatible with --line-range option')
3876 3876 )
3877 3877
3878 3878 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3879 3879 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3880 3880 if linerange:
3881 3881 # TODO: should follow file history from logcmdutil._initialrevs(),
3882 3882 # then filter the result by logcmdutil._makerevset() and --limit
3883 3883 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3884 3884
3885 3885 getcopies = None
3886 3886 if opts.get('copies'):
3887 3887 endrev = None
3888 3888 if revs:
3889 3889 endrev = revs.max() + 1
3890 3890 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
3891 3891
3892 3892 ui.pager('log')
3893 3893 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3894 3894 buffered=True)
3895 3895 if opts.get('graph'):
3896 3896 displayfn = logcmdutil.displaygraphrevs
3897 3897 else:
3898 3898 displayfn = logcmdutil.displayrevs
3899 3899 displayfn(ui, repo, revs, displayer, getcopies)
3900 3900
3901 3901 @command('manifest',
3902 3902 [('r', 'rev', '', _('revision to display'), _('REV')),
3903 3903 ('', 'all', False, _("list files from all revisions"))]
3904 3904 + formatteropts,
3905 3905 _('[-r REV]'),
3906 3906 helpcategory=command.CATEGORY_MAINTENANCE,
3907 3907 intents={INTENT_READONLY})
3908 3908 def manifest(ui, repo, node=None, rev=None, **opts):
3909 3909 """output the current or given revision of the project manifest
3910 3910
3911 3911 Print a list of version controlled files for the given revision.
3912 3912 If no revision is given, the first parent of the working directory
3913 3913 is used, or the null revision if no revision is checked out.
3914 3914
3915 3915 With -v, print file permissions, symlink and executable bits.
3916 3916 With --debug, print file revision hashes.
3917 3917
3918 3918 If option --all is specified, the list of all files from all revisions
3919 3919 is printed. This includes deleted and renamed files.
3920 3920
3921 3921 Returns 0 on success.
3922 3922 """
3923 3923 opts = pycompat.byteskwargs(opts)
3924 3924 fm = ui.formatter('manifest', opts)
3925 3925
3926 3926 if opts.get('all'):
3927 3927 if rev or node:
3928 3928 raise error.Abort(_("can't specify a revision with --all"))
3929 3929
3930 3930 res = set()
3931 3931 for rev in repo:
3932 3932 ctx = repo[rev]
3933 3933 res |= set(ctx.files())
3934 3934
3935 3935 ui.pager('manifest')
3936 3936 for f in sorted(res):
3937 3937 fm.startitem()
3938 3938 fm.write("path", '%s\n', f)
3939 3939 fm.end()
3940 3940 return
3941 3941
3942 3942 if rev and node:
3943 3943 raise error.Abort(_("please specify just one revision"))
3944 3944
3945 3945 if not node:
3946 3946 node = rev
3947 3947
3948 3948 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3949 3949 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3950 3950 if node:
3951 3951 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3952 3952 ctx = scmutil.revsingle(repo, node)
3953 3953 mf = ctx.manifest()
3954 3954 ui.pager('manifest')
3955 3955 for f in ctx:
3956 3956 fm.startitem()
3957 3957 fm.context(ctx=ctx)
3958 3958 fl = ctx[f].flags()
3959 3959 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3960 3960 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3961 3961 fm.write('path', '%s\n', f)
3962 3962 fm.end()
3963 3963
3964 3964 @command('merge',
3965 3965 [('f', 'force', None,
3966 3966 _('force a merge including outstanding changes (DEPRECATED)')),
3967 3967 ('r', 'rev', '', _('revision to merge'), _('REV')),
3968 3968 ('P', 'preview', None,
3969 3969 _('review revisions to merge (no merge is performed)')),
3970 3970 ('', 'abort', None, _('abort the ongoing merge')),
3971 3971 ] + mergetoolopts,
3972 3972 _('[-P] [[-r] REV]'),
3973 3973 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, helpbasic=True)
3974 3974 def merge(ui, repo, node=None, **opts):
3975 3975 """merge another revision into working directory
3976 3976
3977 3977 The current working directory is updated with all changes made in
3978 3978 the requested revision since the last common predecessor revision.
3979 3979
3980 3980 Files that changed between either parent are marked as changed for
3981 3981 the next commit and a commit must be performed before any further
3982 3982 updates to the repository are allowed. The next commit will have
3983 3983 two parents.
3984 3984
3985 3985 ``--tool`` can be used to specify the merge tool used for file
3986 3986 merges. It overrides the HGMERGE environment variable and your
3987 3987 configuration files. See :hg:`help merge-tools` for options.
3988 3988
3989 3989 If no revision is specified, the working directory's parent is a
3990 3990 head revision, and the current branch contains exactly one other
3991 3991 head, the other head is merged with by default. Otherwise, an
3992 3992 explicit revision with which to merge must be provided.
3993 3993
3994 3994 See :hg:`help resolve` for information on handling file conflicts.
3995 3995
3996 3996 To undo an uncommitted merge, use :hg:`merge --abort` which
3997 3997 will check out a clean copy of the original merge parent, losing
3998 3998 all changes.
3999 3999
4000 4000 Returns 0 on success, 1 if there are unresolved files.
4001 4001 """
4002 4002
4003 4003 opts = pycompat.byteskwargs(opts)
4004 4004 abort = opts.get('abort')
4005 4005 if abort and repo.dirstate.p2() == nullid:
4006 4006 cmdutil.wrongtooltocontinue(repo, _('merge'))
4007 4007 if abort:
4008 4008 state = cmdutil.getunfinishedstate(repo)
4009 4009 if state and state._opname != 'merge':
4010 4010 raise error.Abort(_('cannot abort merge with %s in progress') %
4011 4011 (state._opname), hint=state.hint())
4012 4012 if node:
4013 4013 raise error.Abort(_("cannot specify a node with --abort"))
4014 4014 if opts.get('rev'):
4015 4015 raise error.Abort(_("cannot specify both --rev and --abort"))
4016 4016 if opts.get('preview'):
4017 4017 raise error.Abort(_("cannot specify --preview with --abort"))
4018 4018 if opts.get('rev') and node:
4019 4019 raise error.Abort(_("please specify just one revision"))
4020 4020 if not node:
4021 4021 node = opts.get('rev')
4022 4022
4023 4023 if node:
4024 4024 node = scmutil.revsingle(repo, node).node()
4025 4025
4026 4026 if not node and not abort:
4027 4027 node = repo[destutil.destmerge(repo)].node()
4028 4028
4029 4029 if opts.get('preview'):
4030 4030 # find nodes that are ancestors of p2 but not of p1
4031 4031 p1 = repo.lookup('.')
4032 4032 p2 = node
4033 4033 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4034 4034
4035 4035 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4036 4036 for node in nodes:
4037 4037 displayer.show(repo[node])
4038 4038 displayer.close()
4039 4039 return 0
4040 4040
4041 4041 # ui.forcemerge is an internal variable, do not document
4042 4042 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4043 4043 with ui.configoverride(overrides, 'merge'):
4044 4044 force = opts.get('force')
4045 4045 labels = ['working copy', 'merge rev']
4046 4046 return hg.merge(repo, node, force=force, mergeforce=force,
4047 4047 labels=labels, abort=abort)
4048 4048
4049 4049 statemod.addunfinished(
4050 4050 'merge', fname=None, clearable=True, allowcommit=True,
4051 4051 cmdmsg=_('outstanding uncommitted merge'), abortfunc=hg.abortmerge,
4052 4052 statushint=_('To continue: hg commit\n'
4053 4053 'To abort: hg merge --abort'),
4054 4054 cmdhint=_("use 'hg commit' or 'hg merge --abort'")
4055 4055 )
4056 4056
4057 4057 @command('outgoing|out',
4058 4058 [('f', 'force', None, _('run even when the destination is unrelated')),
4059 4059 ('r', 'rev', [],
4060 4060 _('a changeset intended to be included in the destination'), _('REV')),
4061 4061 ('n', 'newest-first', None, _('show newest record first')),
4062 4062 ('B', 'bookmarks', False, _('compare bookmarks')),
4063 4063 ('b', 'branch', [], _('a specific branch you would like to push'),
4064 4064 _('BRANCH')),
4065 4065 ] + logopts + remoteopts + subrepoopts,
4066 4066 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4067 4067 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
4068 4068 def outgoing(ui, repo, dest=None, **opts):
4069 4069 """show changesets not found in the destination
4070 4070
4071 4071 Show changesets not found in the specified destination repository
4072 4072 or the default push location. These are the changesets that would
4073 4073 be pushed if a push was requested.
4074 4074
4075 4075 See pull for details of valid destination formats.
4076 4076
4077 4077 .. container:: verbose
4078 4078
4079 4079 With -B/--bookmarks, the result of bookmark comparison between
4080 4080 local and remote repositories is displayed. With -v/--verbose,
4081 4081 status is also displayed for each bookmark like below::
4082 4082
4083 4083 BM1 01234567890a added
4084 4084 BM2 deleted
4085 4085 BM3 234567890abc advanced
4086 4086 BM4 34567890abcd diverged
4087 4087 BM5 4567890abcde changed
4088 4088
4089 4089 The action taken when pushing depends on the
4090 4090 status of each bookmark:
4091 4091
4092 4092 :``added``: push with ``-B`` will create it
4093 4093 :``deleted``: push with ``-B`` will delete it
4094 4094 :``advanced``: push will update it
4095 4095 :``diverged``: push with ``-B`` will update it
4096 4096 :``changed``: push with ``-B`` will update it
4097 4097
4098 4098 From the point of view of pushing behavior, bookmarks
4099 4099 existing only in the remote repository are treated as
4100 4100 ``deleted``, even if it is in fact added remotely.
4101 4101
4102 4102 Returns 0 if there are outgoing changes, 1 otherwise.
4103 4103 """
4104 4104 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4105 4105 # style URLs, so don't overwrite dest.
4106 4106 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4107 4107 if not path:
4108 4108 raise error.Abort(_('default repository not configured!'),
4109 4109 hint=_("see 'hg help config.paths'"))
4110 4110
4111 4111 opts = pycompat.byteskwargs(opts)
4112 4112 if opts.get('graph'):
4113 4113 logcmdutil.checkunsupportedgraphflags([], opts)
4114 4114 o, other = hg._outgoing(ui, repo, dest, opts)
4115 4115 if not o:
4116 4116 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4117 4117 return
4118 4118
4119 4119 revdag = logcmdutil.graphrevs(repo, o, opts)
4120 4120 ui.pager('outgoing')
4121 4121 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4122 4122 logcmdutil.displaygraph(ui, repo, revdag, displayer,
4123 4123 graphmod.asciiedges)
4124 4124 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4125 4125 return 0
4126 4126
4127 4127 if opts.get('bookmarks'):
4128 4128 dest = path.pushloc or path.loc
4129 4129 other = hg.peer(repo, opts, dest)
4130 4130 if 'bookmarks' not in other.listkeys('namespaces'):
4131 4131 ui.warn(_("remote doesn't support bookmarks\n"))
4132 4132 return 0
4133 4133 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4134 4134 ui.pager('outgoing')
4135 4135 return bookmarks.outgoing(ui, repo, other)
4136 4136
4137 4137 repo._subtoppath = path.pushloc or path.loc
4138 4138 try:
4139 4139 return hg.outgoing(ui, repo, dest, opts)
4140 4140 finally:
4141 4141 del repo._subtoppath
4142 4142
4143 4143 @command('parents',
4144 4144 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4145 4145 ] + templateopts,
4146 4146 _('[-r REV] [FILE]'),
4147 4147 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4148 4148 inferrepo=True)
4149 4149 def parents(ui, repo, file_=None, **opts):
4150 4150 """show the parents of the working directory or revision (DEPRECATED)
4151 4151
4152 4152 Print the working directory's parent revisions. If a revision is
4153 4153 given via -r/--rev, the parent of that revision will be printed.
4154 4154 If a file argument is given, the revision in which the file was
4155 4155 last changed (before the working directory revision or the
4156 4156 argument to --rev if given) is printed.
4157 4157
4158 4158 This command is equivalent to::
4159 4159
4160 4160 hg log -r "p1()+p2()" or
4161 4161 hg log -r "p1(REV)+p2(REV)" or
4162 4162 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4163 4163 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4164 4164
4165 4165 See :hg:`summary` and :hg:`help revsets` for related information.
4166 4166
4167 4167 Returns 0 on success.
4168 4168 """
4169 4169
4170 4170 opts = pycompat.byteskwargs(opts)
4171 4171 rev = opts.get('rev')
4172 4172 if rev:
4173 4173 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4174 4174 ctx = scmutil.revsingle(repo, rev, None)
4175 4175
4176 4176 if file_:
4177 4177 m = scmutil.match(ctx, (file_,), opts)
4178 4178 if m.anypats() or len(m.files()) != 1:
4179 4179 raise error.Abort(_('can only specify an explicit filename'))
4180 4180 file_ = m.files()[0]
4181 4181 filenodes = []
4182 4182 for cp in ctx.parents():
4183 4183 if not cp:
4184 4184 continue
4185 4185 try:
4186 4186 filenodes.append(cp.filenode(file_))
4187 4187 except error.LookupError:
4188 4188 pass
4189 4189 if not filenodes:
4190 4190 raise error.Abort(_("'%s' not found in manifest!") % file_)
4191 4191 p = []
4192 4192 for fn in filenodes:
4193 4193 fctx = repo.filectx(file_, fileid=fn)
4194 4194 p.append(fctx.node())
4195 4195 else:
4196 4196 p = [cp.node() for cp in ctx.parents()]
4197 4197
4198 4198 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4199 4199 for n in p:
4200 4200 if n != nullid:
4201 4201 displayer.show(repo[n])
4202 4202 displayer.close()
4203 4203
4204 4204 @command('paths', formatteropts, _('[NAME]'),
4205 4205 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4206 4206 optionalrepo=True, intents={INTENT_READONLY})
4207 4207 def paths(ui, repo, search=None, **opts):
4208 4208 """show aliases for remote repositories
4209 4209
4210 4210 Show definition of symbolic path name NAME. If no name is given,
4211 4211 show definition of all available names.
4212 4212
4213 4213 Option -q/--quiet suppresses all output when searching for NAME
4214 4214 and shows only the path names when listing all definitions.
4215 4215
4216 4216 Path names are defined in the [paths] section of your
4217 4217 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4218 4218 repository, ``.hg/hgrc`` is used, too.
4219 4219
4220 4220 The path names ``default`` and ``default-push`` have a special
4221 4221 meaning. When performing a push or pull operation, they are used
4222 4222 as fallbacks if no location is specified on the command-line.
4223 4223 When ``default-push`` is set, it will be used for push and
4224 4224 ``default`` will be used for pull; otherwise ``default`` is used
4225 4225 as the fallback for both. When cloning a repository, the clone
4226 4226 source is written as ``default`` in ``.hg/hgrc``.
4227 4227
4228 4228 .. note::
4229 4229
4230 4230 ``default`` and ``default-push`` apply to all inbound (e.g.
4231 4231 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4232 4232 and :hg:`bundle`) operations.
4233 4233
4234 4234 See :hg:`help urls` for more information.
4235 4235
4236 4236 .. container:: verbose
4237 4237
4238 4238 Template:
4239 4239
4240 4240 The following keywords are supported. See also :hg:`help templates`.
4241 4241
4242 4242 :name: String. Symbolic name of the path alias.
4243 4243 :pushurl: String. URL for push operations.
4244 4244 :url: String. URL or directory path for the other operations.
4245 4245
4246 4246 Returns 0 on success.
4247 4247 """
4248 4248
4249 4249 opts = pycompat.byteskwargs(opts)
4250 4250 ui.pager('paths')
4251 4251 if search:
4252 4252 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4253 4253 if name == search]
4254 4254 else:
4255 4255 pathitems = sorted(ui.paths.iteritems())
4256 4256
4257 4257 fm = ui.formatter('paths', opts)
4258 4258 if fm.isplain():
4259 4259 hidepassword = util.hidepassword
4260 4260 else:
4261 4261 hidepassword = bytes
4262 4262 if ui.quiet:
4263 4263 namefmt = '%s\n'
4264 4264 else:
4265 4265 namefmt = '%s = '
4266 4266 showsubopts = not search and not ui.quiet
4267 4267
4268 4268 for name, path in pathitems:
4269 4269 fm.startitem()
4270 4270 fm.condwrite(not search, 'name', namefmt, name)
4271 4271 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4272 4272 for subopt, value in sorted(path.suboptions.items()):
4273 4273 assert subopt not in ('name', 'url')
4274 4274 if showsubopts:
4275 4275 fm.plain('%s:%s = ' % (name, subopt))
4276 4276 fm.condwrite(showsubopts, subopt, '%s\n', value)
4277 4277
4278 4278 fm.end()
4279 4279
4280 4280 if search and not pathitems:
4281 4281 if not ui.quiet:
4282 4282 ui.warn(_("not found!\n"))
4283 4283 return 1
4284 4284 else:
4285 4285 return 0
4286 4286
4287 4287 @command('phase',
4288 4288 [('p', 'public', False, _('set changeset phase to public')),
4289 4289 ('d', 'draft', False, _('set changeset phase to draft')),
4290 4290 ('s', 'secret', False, _('set changeset phase to secret')),
4291 4291 ('f', 'force', False, _('allow to move boundary backward')),
4292 4292 ('r', 'rev', [], _('target revision'), _('REV')),
4293 4293 ],
4294 4294 _('[-p|-d|-s] [-f] [-r] [REV...]'),
4295 4295 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
4296 4296 def phase(ui, repo, *revs, **opts):
4297 4297 """set or show the current phase name
4298 4298
4299 4299 With no argument, show the phase name of the current revision(s).
4300 4300
4301 4301 With one of -p/--public, -d/--draft or -s/--secret, change the
4302 4302 phase value of the specified revisions.
4303 4303
4304 4304 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4305 4305 lower phase to a higher phase. Phases are ordered as follows::
4306 4306
4307 4307 public < draft < secret
4308 4308
4309 4309 Returns 0 on success, 1 if some phases could not be changed.
4310 4310
4311 4311 (For more information about the phases concept, see :hg:`help phases`.)
4312 4312 """
4313 4313 opts = pycompat.byteskwargs(opts)
4314 4314 # search for a unique phase argument
4315 4315 targetphase = None
4316 4316 for idx, name in enumerate(phases.cmdphasenames):
4317 4317 if opts[name]:
4318 4318 if targetphase is not None:
4319 4319 raise error.Abort(_('only one phase can be specified'))
4320 4320 targetphase = idx
4321 4321
4322 4322 # look for specified revision
4323 4323 revs = list(revs)
4324 4324 revs.extend(opts['rev'])
4325 4325 if not revs:
4326 4326 # display both parents as the second parent phase can influence
4327 4327 # the phase of a merge commit
4328 4328 revs = [c.rev() for c in repo[None].parents()]
4329 4329
4330 4330 revs = scmutil.revrange(repo, revs)
4331 4331
4332 4332 ret = 0
4333 4333 if targetphase is None:
4334 4334 # display
4335 4335 for r in revs:
4336 4336 ctx = repo[r]
4337 4337 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4338 4338 else:
4339 4339 with repo.lock(), repo.transaction("phase") as tr:
4340 4340 # set phase
4341 4341 if not revs:
4342 4342 raise error.Abort(_('empty revision set'))
4343 4343 nodes = [repo[r].node() for r in revs]
4344 4344 # moving revision from public to draft may hide them
4345 4345 # We have to check result on an unfiltered repository
4346 4346 unfi = repo.unfiltered()
4347 4347 getphase = unfi._phasecache.phase
4348 4348 olddata = [getphase(unfi, r) for r in unfi]
4349 4349 phases.advanceboundary(repo, tr, targetphase, nodes)
4350 4350 if opts['force']:
4351 4351 phases.retractboundary(repo, tr, targetphase, nodes)
4352 4352 getphase = unfi._phasecache.phase
4353 4353 newdata = [getphase(unfi, r) for r in unfi]
4354 4354 changes = sum(newdata[r] != olddata[r] for r in unfi)
4355 4355 cl = unfi.changelog
4356 4356 rejected = [n for n in nodes
4357 4357 if newdata[cl.rev(n)] < targetphase]
4358 4358 if rejected:
4359 4359 ui.warn(_('cannot move %i changesets to a higher '
4360 4360 'phase, use --force\n') % len(rejected))
4361 4361 ret = 1
4362 4362 if changes:
4363 4363 msg = _('phase changed for %i changesets\n') % changes
4364 4364 if ret:
4365 4365 ui.status(msg)
4366 4366 else:
4367 4367 ui.note(msg)
4368 4368 else:
4369 4369 ui.warn(_('no phases changed\n'))
4370 4370 return ret
4371 4371
4372 4372 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4373 4373 """Run after a changegroup has been added via pull/unbundle
4374 4374
4375 4375 This takes arguments below:
4376 4376
4377 4377 :modheads: change of heads by pull/unbundle
4378 4378 :optupdate: updating working directory is needed or not
4379 4379 :checkout: update destination revision (or None to default destination)
4380 4380 :brev: a name, which might be a bookmark to be activated after updating
4381 4381 """
4382 4382 if modheads == 0:
4383 4383 return
4384 4384 if optupdate:
4385 4385 try:
4386 4386 return hg.updatetotally(ui, repo, checkout, brev)
4387 4387 except error.UpdateAbort as inst:
4388 4388 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4389 4389 hint = inst.hint
4390 4390 raise error.UpdateAbort(msg, hint=hint)
4391 4391 if modheads is not None and modheads > 1:
4392 4392 currentbranchheads = len(repo.branchheads())
4393 4393 if currentbranchheads == modheads:
4394 4394 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4395 4395 elif currentbranchheads > 1:
4396 4396 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4397 4397 "merge)\n"))
4398 4398 else:
4399 4399 ui.status(_("(run 'hg heads' to see heads)\n"))
4400 4400 elif not ui.configbool('commands', 'update.requiredest'):
4401 4401 ui.status(_("(run 'hg update' to get a working copy)\n"))
4402 4402
4403 4403 @command('pull',
4404 4404 [('u', 'update', None,
4405 4405 _('update to new branch head if new descendants were pulled')),
4406 4406 ('f', 'force', None, _('run even when remote repository is unrelated')),
4407 4407 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4408 4408 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4409 4409 ('b', 'branch', [], _('a specific branch you would like to pull'),
4410 4410 _('BRANCH')),
4411 4411 ] + remoteopts,
4412 4412 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
4413 4413 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4414 4414 helpbasic=True)
4415 4415 def pull(ui, repo, source="default", **opts):
4416 4416 """pull changes from the specified source
4417 4417
4418 4418 Pull changes from a remote repository to a local one.
4419 4419
4420 4420 This finds all changes from the repository at the specified path
4421 4421 or URL and adds them to a local repository (the current one unless
4422 4422 -R is specified). By default, this does not update the copy of the
4423 4423 project in the working directory.
4424 4424
4425 4425 When cloning from servers that support it, Mercurial may fetch
4426 4426 pre-generated data. When this is done, hooks operating on incoming
4427 4427 changesets and changegroups may fire more than once, once for each
4428 4428 pre-generated bundle and as well as for any additional remaining
4429 4429 data. See :hg:`help -e clonebundles` for more.
4430 4430
4431 4431 Use :hg:`incoming` if you want to see what would have been added
4432 4432 by a pull at the time you issued this command. If you then decide
4433 4433 to add those changes to the repository, you should use :hg:`pull
4434 4434 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4435 4435
4436 4436 If SOURCE is omitted, the 'default' path will be used.
4437 4437 See :hg:`help urls` for more information.
4438 4438
4439 4439 Specifying bookmark as ``.`` is equivalent to specifying the active
4440 4440 bookmark's name.
4441 4441
4442 4442 Returns 0 on success, 1 if an update had unresolved files.
4443 4443 """
4444 4444
4445 4445 opts = pycompat.byteskwargs(opts)
4446 4446 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4447 4447 msg = _('update destination required by configuration')
4448 4448 hint = _('use hg pull followed by hg update DEST')
4449 4449 raise error.Abort(msg, hint=hint)
4450 4450
4451 4451 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4452 4452 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4453 4453 other = hg.peer(repo, opts, source)
4454 4454 try:
4455 4455 revs, checkout = hg.addbranchrevs(repo, other, branches,
4456 4456 opts.get('rev'))
4457 4457
4458 4458 pullopargs = {}
4459 4459
4460 4460 nodes = None
4461 4461 if opts.get('bookmark') or revs:
4462 4462 # The list of bookmark used here is the same used to actually update
4463 4463 # the bookmark names, to avoid the race from issue 4689 and we do
4464 4464 # all lookup and bookmark queries in one go so they see the same
4465 4465 # version of the server state (issue 4700).
4466 4466 nodes = []
4467 4467 fnodes = []
4468 4468 revs = revs or []
4469 4469 if revs and not other.capable('lookup'):
4470 4470 err = _("other repository doesn't support revision lookup, "
4471 4471 "so a rev cannot be specified.")
4472 4472 raise error.Abort(err)
4473 4473 with other.commandexecutor() as e:
4474 4474 fremotebookmarks = e.callcommand('listkeys', {
4475 4475 'namespace': 'bookmarks'
4476 4476 })
4477 4477 for r in revs:
4478 4478 fnodes.append(e.callcommand('lookup', {'key': r}))
4479 4479 remotebookmarks = fremotebookmarks.result()
4480 4480 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4481 4481 pullopargs['remotebookmarks'] = remotebookmarks
4482 4482 for b in opts.get('bookmark', []):
4483 4483 b = repo._bookmarks.expandname(b)
4484 4484 if b not in remotebookmarks:
4485 4485 raise error.Abort(_('remote bookmark %s not found!') % b)
4486 4486 nodes.append(remotebookmarks[b])
4487 4487 for i, rev in enumerate(revs):
4488 4488 node = fnodes[i].result()
4489 4489 nodes.append(node)
4490 4490 if rev == checkout:
4491 4491 checkout = node
4492 4492
4493 4493 wlock = util.nullcontextmanager()
4494 4494 if opts.get('update'):
4495 4495 wlock = repo.wlock()
4496 4496 with wlock:
4497 4497 pullopargs.update(opts.get('opargs', {}))
4498 4498 modheads = exchange.pull(repo, other, heads=nodes,
4499 4499 force=opts.get('force'),
4500 4500 bookmarks=opts.get('bookmark', ()),
4501 4501 opargs=pullopargs).cgresult
4502 4502
4503 4503 # brev is a name, which might be a bookmark to be activated at
4504 4504 # the end of the update. In other words, it is an explicit
4505 4505 # destination of the update
4506 4506 brev = None
4507 4507
4508 4508 if checkout:
4509 4509 checkout = repo.unfiltered().changelog.rev(checkout)
4510 4510
4511 4511 # order below depends on implementation of
4512 4512 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4513 4513 # because 'checkout' is determined without it.
4514 4514 if opts.get('rev'):
4515 4515 brev = opts['rev'][0]
4516 4516 elif opts.get('branch'):
4517 4517 brev = opts['branch'][0]
4518 4518 else:
4519 4519 brev = branches[0]
4520 4520 repo._subtoppath = source
4521 4521 try:
4522 4522 ret = postincoming(ui, repo, modheads, opts.get('update'),
4523 4523 checkout, brev)
4524 4524 except error.FilteredRepoLookupError as exc:
4525 4525 msg = _('cannot update to target: %s') % exc.args[0]
4526 4526 exc.args = (msg,) + exc.args[1:]
4527 4527 raise
4528 4528 finally:
4529 4529 del repo._subtoppath
4530 4530
4531 4531 finally:
4532 4532 other.close()
4533 4533 return ret
4534 4534
4535 4535 @command('push',
4536 4536 [('f', 'force', None, _('force push')),
4537 4537 ('r', 'rev', [],
4538 4538 _('a changeset intended to be included in the destination'),
4539 4539 _('REV')),
4540 4540 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4541 4541 ('b', 'branch', [],
4542 4542 _('a specific branch you would like to push'), _('BRANCH')),
4543 4543 ('', 'new-branch', False, _('allow pushing a new branch')),
4544 4544 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4545 4545 ('', 'publish', False, _('push the changeset as public (EXPERIMENTAL)')),
4546 4546 ] + remoteopts,
4547 4547 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
4548 4548 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4549 4549 helpbasic=True)
4550 4550 def push(ui, repo, dest=None, **opts):
4551 4551 """push changes to the specified destination
4552 4552
4553 4553 Push changesets from the local repository to the specified
4554 4554 destination.
4555 4555
4556 4556 This operation is symmetrical to pull: it is identical to a pull
4557 4557 in the destination repository from the current one.
4558 4558
4559 4559 By default, push will not allow creation of new heads at the
4560 4560 destination, since multiple heads would make it unclear which head
4561 4561 to use. In this situation, it is recommended to pull and merge
4562 4562 before pushing.
4563 4563
4564 4564 Use --new-branch if you want to allow push to create a new named
4565 4565 branch that is not present at the destination. This allows you to
4566 4566 only create a new branch without forcing other changes.
4567 4567
4568 4568 .. note::
4569 4569
4570 4570 Extra care should be taken with the -f/--force option,
4571 4571 which will push all new heads on all branches, an action which will
4572 4572 almost always cause confusion for collaborators.
4573 4573
4574 4574 If -r/--rev is used, the specified revision and all its ancestors
4575 4575 will be pushed to the remote repository.
4576 4576
4577 4577 If -B/--bookmark is used, the specified bookmarked revision, its
4578 4578 ancestors, and the bookmark will be pushed to the remote
4579 4579 repository. Specifying ``.`` is equivalent to specifying the active
4580 4580 bookmark's name.
4581 4581
4582 4582 Please see :hg:`help urls` for important details about ``ssh://``
4583 4583 URLs. If DESTINATION is omitted, a default path will be used.
4584 4584
4585 4585 .. container:: verbose
4586 4586
4587 4587 The --pushvars option sends strings to the server that become
4588 4588 environment variables prepended with ``HG_USERVAR_``. For example,
4589 4589 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4590 4590 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4591 4591
4592 4592 pushvars can provide for user-overridable hooks as well as set debug
4593 4593 levels. One example is having a hook that blocks commits containing
4594 4594 conflict markers, but enables the user to override the hook if the file
4595 4595 is using conflict markers for testing purposes or the file format has
4596 4596 strings that look like conflict markers.
4597 4597
4598 4598 By default, servers will ignore `--pushvars`. To enable it add the
4599 4599 following to your configuration file::
4600 4600
4601 4601 [push]
4602 4602 pushvars.server = true
4603 4603
4604 4604 Returns 0 if push was successful, 1 if nothing to push.
4605 4605 """
4606 4606
4607 4607 opts = pycompat.byteskwargs(opts)
4608 4608 if opts.get('bookmark'):
4609 4609 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4610 4610 for b in opts['bookmark']:
4611 4611 # translate -B options to -r so changesets get pushed
4612 4612 b = repo._bookmarks.expandname(b)
4613 4613 if b in repo._bookmarks:
4614 4614 opts.setdefault('rev', []).append(b)
4615 4615 else:
4616 4616 # if we try to push a deleted bookmark, translate it to null
4617 4617 # this lets simultaneous -r, -b options continue working
4618 4618 opts.setdefault('rev', []).append("null")
4619 4619
4620 4620 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4621 4621 if not path:
4622 4622 raise error.Abort(_('default repository not configured!'),
4623 4623 hint=_("see 'hg help config.paths'"))
4624 4624 dest = path.pushloc or path.loc
4625 4625 branches = (path.branch, opts.get('branch') or [])
4626 4626 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4627 4627 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4628 4628 other = hg.peer(repo, opts, dest)
4629 4629
4630 4630 if revs:
4631 4631 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4632 4632 if not revs:
4633 4633 raise error.Abort(_("specified revisions evaluate to an empty set"),
4634 4634 hint=_("use different revision arguments"))
4635 4635 elif path.pushrev:
4636 4636 # It doesn't make any sense to specify ancestor revisions. So limit
4637 4637 # to DAG heads to make discovery simpler.
4638 4638 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4639 4639 revs = scmutil.revrange(repo, [expr])
4640 4640 revs = [repo[rev].node() for rev in revs]
4641 4641 if not revs:
4642 4642 raise error.Abort(_('default push revset for path evaluates to an '
4643 4643 'empty set'))
4644 4644
4645 4645 repo._subtoppath = dest
4646 4646 try:
4647 4647 # push subrepos depth-first for coherent ordering
4648 4648 c = repo['.']
4649 4649 subs = c.substate # only repos that are committed
4650 4650 for s in sorted(subs):
4651 4651 result = c.sub(s).push(opts)
4652 4652 if result == 0:
4653 4653 return not result
4654 4654 finally:
4655 4655 del repo._subtoppath
4656 4656
4657 4657 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4658 4658 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4659 4659
4660 4660 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4661 4661 newbranch=opts.get('new_branch'),
4662 4662 bookmarks=opts.get('bookmark', ()),
4663 4663 publish=opts.get('publish'),
4664 4664 opargs=opargs)
4665 4665
4666 4666 result = not pushop.cgresult
4667 4667
4668 4668 if pushop.bkresult is not None:
4669 4669 if pushop.bkresult == 2:
4670 4670 result = 2
4671 4671 elif not result and pushop.bkresult:
4672 4672 result = 2
4673 4673
4674 4674 return result
4675 4675
4676 4676 @command('recover',
4677 4677 [('','verify', True, "run `hg verify` after succesful recover"),
4678 4678 ],
4679 4679 helpcategory=command.CATEGORY_MAINTENANCE)
4680 4680 def recover(ui, repo, **opts):
4681 4681 """roll back an interrupted transaction
4682 4682
4683 4683 Recover from an interrupted commit or pull.
4684 4684
4685 4685 This command tries to fix the repository status after an
4686 4686 interrupted operation. It should only be necessary when Mercurial
4687 4687 suggests it.
4688 4688
4689 4689 Returns 0 if successful, 1 if nothing to recover or verify fails.
4690 4690 """
4691 4691 ret = repo.recover()
4692 4692 if ret:
4693 4693 if opts[r'verify']:
4694 4694 return hg.verify(repo)
4695 4695 else:
4696 4696 msg = _("(verify step skipped, run `hg verify` to check your "
4697 4697 "repository content)\n")
4698 4698 ui.warn(msg)
4699 4699 return 0
4700 4700 return 1
4701 4701
4702 4702 @command('remove|rm',
4703 4703 [('A', 'after', None, _('record delete for missing files')),
4704 4704 ('f', 'force', None,
4705 4705 _('forget added files, delete modified files')),
4706 4706 ] + subrepoopts + walkopts + dryrunopts,
4707 4707 _('[OPTION]... FILE...'),
4708 4708 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4709 4709 helpbasic=True, inferrepo=True)
4710 4710 def remove(ui, repo, *pats, **opts):
4711 4711 """remove the specified files on the next commit
4712 4712
4713 4713 Schedule the indicated files for removal from the current branch.
4714 4714
4715 4715 This command schedules the files to be removed at the next commit.
4716 4716 To undo a remove before that, see :hg:`revert`. To undo added
4717 4717 files, see :hg:`forget`.
4718 4718
4719 4719 .. container:: verbose
4720 4720
4721 4721 -A/--after can be used to remove only files that have already
4722 4722 been deleted, -f/--force can be used to force deletion, and -Af
4723 4723 can be used to remove files from the next revision without
4724 4724 deleting them from the working directory.
4725 4725
4726 4726 The following table details the behavior of remove for different
4727 4727 file states (columns) and option combinations (rows). The file
4728 4728 states are Added [A], Clean [C], Modified [M] and Missing [!]
4729 4729 (as reported by :hg:`status`). The actions are Warn, Remove
4730 4730 (from branch) and Delete (from disk):
4731 4731
4732 4732 ========= == == == ==
4733 4733 opt/state A C M !
4734 4734 ========= == == == ==
4735 4735 none W RD W R
4736 4736 -f R RD RD R
4737 4737 -A W W W R
4738 4738 -Af R R R R
4739 4739 ========= == == == ==
4740 4740
4741 4741 .. note::
4742 4742
4743 4743 :hg:`remove` never deletes files in Added [A] state from the
4744 4744 working directory, not even if ``--force`` is specified.
4745 4745
4746 4746 Returns 0 on success, 1 if any warnings encountered.
4747 4747 """
4748 4748
4749 4749 opts = pycompat.byteskwargs(opts)
4750 4750 after, force = opts.get('after'), opts.get('force')
4751 4751 dryrun = opts.get('dry_run')
4752 4752 if not pats and not after:
4753 4753 raise error.Abort(_('no files specified'))
4754 4754
4755 4755 m = scmutil.match(repo[None], pats, opts)
4756 4756 subrepos = opts.get('subrepos')
4757 4757 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
4758 4758 return cmdutil.remove(ui, repo, m, "", uipathfn, after, force, subrepos,
4759 4759 dryrun=dryrun)
4760 4760
4761 4761 @command('rename|move|mv',
4762 4762 [('A', 'after', None, _('record a rename that has already occurred')),
4763 4763 ('f', 'force', None, _('forcibly move over an existing managed file')),
4764 4764 ] + walkopts + dryrunopts,
4765 4765 _('[OPTION]... SOURCE... DEST'),
4766 4766 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
4767 4767 def rename(ui, repo, *pats, **opts):
4768 4768 """rename files; equivalent of copy + remove
4769 4769
4770 4770 Mark dest as copies of sources; mark sources for deletion. If dest
4771 4771 is a directory, copies are put in that directory. If dest is a
4772 4772 file, there can only be one source.
4773 4773
4774 4774 By default, this command copies the contents of files as they
4775 4775 exist in the working directory. If invoked with -A/--after, the
4776 4776 operation is recorded, but no copying is performed.
4777 4777
4778 4778 This command takes effect at the next commit. To undo a rename
4779 4779 before that, see :hg:`revert`.
4780 4780
4781 4781 Returns 0 on success, 1 if errors are encountered.
4782 4782 """
4783 4783 opts = pycompat.byteskwargs(opts)
4784 4784 with repo.wlock(False):
4785 4785 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4786 4786
4787 4787 @command('resolve',
4788 4788 [('a', 'all', None, _('select all unresolved files')),
4789 4789 ('l', 'list', None, _('list state of files needing merge')),
4790 4790 ('m', 'mark', None, _('mark files as resolved')),
4791 4791 ('u', 'unmark', None, _('mark files as unresolved')),
4792 4792 ('n', 'no-status', None, _('hide status prefix')),
4793 4793 ('', 're-merge', None, _('re-merge files'))]
4794 4794 + mergetoolopts + walkopts + formatteropts,
4795 4795 _('[OPTION]... [FILE]...'),
4796 4796 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4797 4797 inferrepo=True)
4798 4798 def resolve(ui, repo, *pats, **opts):
4799 4799 """redo merges or set/view the merge status of files
4800 4800
4801 4801 Merges with unresolved conflicts are often the result of
4802 4802 non-interactive merging using the ``internal:merge`` configuration
4803 4803 setting, or a command-line merge tool like ``diff3``. The resolve
4804 4804 command is used to manage the files involved in a merge, after
4805 4805 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4806 4806 working directory must have two parents). See :hg:`help
4807 4807 merge-tools` for information on configuring merge tools.
4808 4808
4809 4809 The resolve command can be used in the following ways:
4810 4810
4811 4811 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
4812 4812 the specified files, discarding any previous merge attempts. Re-merging
4813 4813 is not performed for files already marked as resolved. Use ``--all/-a``
4814 4814 to select all unresolved files. ``--tool`` can be used to specify
4815 4815 the merge tool used for the given files. It overrides the HGMERGE
4816 4816 environment variable and your configuration files. Previous file
4817 4817 contents are saved with a ``.orig`` suffix.
4818 4818
4819 4819 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4820 4820 (e.g. after having manually fixed-up the files). The default is
4821 4821 to mark all unresolved files.
4822 4822
4823 4823 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4824 4824 default is to mark all resolved files.
4825 4825
4826 4826 - :hg:`resolve -l`: list files which had or still have conflicts.
4827 4827 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4828 4828 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4829 4829 the list. See :hg:`help filesets` for details.
4830 4830
4831 4831 .. note::
4832 4832
4833 4833 Mercurial will not let you commit files with unresolved merge
4834 4834 conflicts. You must use :hg:`resolve -m ...` before you can
4835 4835 commit after a conflicting merge.
4836 4836
4837 4837 .. container:: verbose
4838 4838
4839 4839 Template:
4840 4840
4841 4841 The following keywords are supported in addition to the common template
4842 4842 keywords and functions. See also :hg:`help templates`.
4843 4843
4844 4844 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
4845 4845 :path: String. Repository-absolute path of the file.
4846 4846
4847 4847 Returns 0 on success, 1 if any files fail a resolve attempt.
4848 4848 """
4849 4849
4850 4850 opts = pycompat.byteskwargs(opts)
4851 4851 confirm = ui.configbool('commands', 'resolve.confirm')
4852 4852 flaglist = 'all mark unmark list no_status re_merge'.split()
4853 4853 all, mark, unmark, show, nostatus, remerge = [
4854 4854 opts.get(o) for o in flaglist]
4855 4855
4856 4856 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
4857 4857 if actioncount > 1:
4858 4858 raise error.Abort(_("too many actions specified"))
4859 4859 elif (actioncount == 0
4860 4860 and ui.configbool('commands', 'resolve.explicit-re-merge')):
4861 4861 hint = _('use --mark, --unmark, --list or --re-merge')
4862 4862 raise error.Abort(_('no action specified'), hint=hint)
4863 4863 if pats and all:
4864 4864 raise error.Abort(_("can't specify --all and patterns"))
4865 4865 if not (all or pats or show or mark or unmark):
4866 4866 raise error.Abort(_('no files or directories specified'),
4867 4867 hint=('use --all to re-merge all unresolved files'))
4868 4868
4869 4869 if confirm:
4870 4870 if all:
4871 4871 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4872 4872 b'$$ &Yes $$ &No')):
4873 4873 raise error.Abort(_('user quit'))
4874 4874 if mark and not pats:
4875 4875 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4876 4876 b'$$ &Yes $$ &No')):
4877 4877 raise error.Abort(_('user quit'))
4878 4878 if unmark and not pats:
4879 4879 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4880 4880 b'$$ &Yes $$ &No')):
4881 4881 raise error.Abort(_('user quit'))
4882 4882
4883 4883 uipathfn = scmutil.getuipathfn(repo)
4884 4884
4885 4885 if show:
4886 4886 ui.pager('resolve')
4887 4887 fm = ui.formatter('resolve', opts)
4888 4888 ms = mergemod.mergestate.read(repo)
4889 4889 wctx = repo[None]
4890 4890 m = scmutil.match(wctx, pats, opts)
4891 4891
4892 4892 # Labels and keys based on merge state. Unresolved path conflicts show
4893 4893 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4894 4894 # resolved conflicts.
4895 4895 mergestateinfo = {
4896 4896 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4897 4897 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4898 4898 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4899 4899 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4900 4900 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4901 4901 'D'),
4902 4902 }
4903 4903
4904 4904 for f in ms:
4905 4905 if not m(f):
4906 4906 continue
4907 4907
4908 4908 label, key = mergestateinfo[ms[f]]
4909 4909 fm.startitem()
4910 4910 fm.context(ctx=wctx)
4911 4911 fm.condwrite(not nostatus, 'mergestatus', '%s ', key, label=label)
4912 4912 fm.data(path=f)
4913 4913 fm.plain('%s\n' % uipathfn(f), label=label)
4914 4914 fm.end()
4915 4915 return 0
4916 4916
4917 4917 with repo.wlock():
4918 4918 ms = mergemod.mergestate.read(repo)
4919 4919
4920 4920 if not (ms.active() or repo.dirstate.p2() != nullid):
4921 4921 raise error.Abort(
4922 4922 _('resolve command not applicable when not merging'))
4923 4923
4924 4924 wctx = repo[None]
4925 4925
4926 4926 if (ms.mergedriver
4927 4927 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4928 4928 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4929 4929 ms.commit()
4930 4930 # allow mark and unmark to go through
4931 4931 if not mark and not unmark and not proceed:
4932 4932 return 1
4933 4933
4934 4934 m = scmutil.match(wctx, pats, opts)
4935 4935 ret = 0
4936 4936 didwork = False
4937 4937 runconclude = False
4938 4938
4939 4939 tocomplete = []
4940 4940 hasconflictmarkers = []
4941 4941 if mark:
4942 4942 markcheck = ui.config('commands', 'resolve.mark-check')
4943 4943 if markcheck not in ['warn', 'abort']:
4944 4944 # Treat all invalid / unrecognized values as 'none'.
4945 4945 markcheck = False
4946 4946 for f in ms:
4947 4947 if not m(f):
4948 4948 continue
4949 4949
4950 4950 didwork = True
4951 4951
4952 4952 # don't let driver-resolved files be marked, and run the conclude
4953 4953 # step if asked to resolve
4954 4954 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4955 4955 exact = m.exact(f)
4956 4956 if mark:
4957 4957 if exact:
4958 4958 ui.warn(_('not marking %s as it is driver-resolved\n')
4959 4959 % uipathfn(f))
4960 4960 elif unmark:
4961 4961 if exact:
4962 4962 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4963 4963 % uipathfn(f))
4964 4964 else:
4965 4965 runconclude = True
4966 4966 continue
4967 4967
4968 4968 # path conflicts must be resolved manually
4969 4969 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4970 4970 mergemod.MERGE_RECORD_RESOLVED_PATH):
4971 4971 if mark:
4972 4972 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4973 4973 elif unmark:
4974 4974 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4975 4975 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4976 4976 ui.warn(_('%s: path conflict must be resolved manually\n')
4977 4977 % uipathfn(f))
4978 4978 continue
4979 4979
4980 4980 if mark:
4981 4981 if markcheck:
4982 4982 fdata = repo.wvfs.tryread(f)
4983 4983 if (filemerge.hasconflictmarkers(fdata) and
4984 4984 ms[f] != mergemod.MERGE_RECORD_RESOLVED):
4985 4985 hasconflictmarkers.append(f)
4986 4986 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4987 4987 elif unmark:
4988 4988 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4989 4989 else:
4990 4990 # backup pre-resolve (merge uses .orig for its own purposes)
4991 4991 a = repo.wjoin(f)
4992 4992 try:
4993 4993 util.copyfile(a, a + ".resolve")
4994 4994 except (IOError, OSError) as inst:
4995 4995 if inst.errno != errno.ENOENT:
4996 4996 raise
4997 4997
4998 4998 try:
4999 4999 # preresolve file
5000 5000 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
5001 5001 with ui.configoverride(overrides, 'resolve'):
5002 5002 complete, r = ms.preresolve(f, wctx)
5003 5003 if not complete:
5004 5004 tocomplete.append(f)
5005 5005 elif r:
5006 5006 ret = 1
5007 5007 finally:
5008 5008 ms.commit()
5009 5009
5010 5010 # replace filemerge's .orig file with our resolve file, but only
5011 5011 # for merges that are complete
5012 5012 if complete:
5013 5013 try:
5014 5014 util.rename(a + ".resolve",
5015 5015 scmutil.backuppath(ui, repo, f))
5016 5016 except OSError as inst:
5017 5017 if inst.errno != errno.ENOENT:
5018 5018 raise
5019 5019
5020 5020 if hasconflictmarkers:
5021 5021 ui.warn(_('warning: the following files still have conflict '
5022 5022 'markers:\n') + ''.join(' ' + uipathfn(f) + '\n'
5023 5023 for f in hasconflictmarkers))
5024 5024 if markcheck == 'abort' and not all and not pats:
5025 5025 raise error.Abort(_('conflict markers detected'),
5026 5026 hint=_('use --all to mark anyway'))
5027 5027
5028 5028 for f in tocomplete:
5029 5029 try:
5030 5030 # resolve file
5031 5031 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
5032 5032 with ui.configoverride(overrides, 'resolve'):
5033 5033 r = ms.resolve(f, wctx)
5034 5034 if r:
5035 5035 ret = 1
5036 5036 finally:
5037 5037 ms.commit()
5038 5038
5039 5039 # replace filemerge's .orig file with our resolve file
5040 5040 a = repo.wjoin(f)
5041 5041 try:
5042 5042 util.rename(a + ".resolve", scmutil.backuppath(ui, repo, f))
5043 5043 except OSError as inst:
5044 5044 if inst.errno != errno.ENOENT:
5045 5045 raise
5046 5046
5047 5047 ms.commit()
5048 5048 ms.recordactions()
5049 5049
5050 5050 if not didwork and pats:
5051 5051 hint = None
5052 5052 if not any([p for p in pats if p.find(':') >= 0]):
5053 5053 pats = ['path:%s' % p for p in pats]
5054 5054 m = scmutil.match(wctx, pats, opts)
5055 5055 for f in ms:
5056 5056 if not m(f):
5057 5057 continue
5058 5058 def flag(o):
5059 5059 if o == 're_merge':
5060 5060 return '--re-merge '
5061 5061 return '-%s ' % o[0:1]
5062 5062 flags = ''.join([flag(o) for o in flaglist if opts.get(o)])
5063 5063 hint = _("(try: hg resolve %s%s)\n") % (
5064 5064 flags,
5065 5065 ' '.join(pats))
5066 5066 break
5067 5067 ui.warn(_("arguments do not match paths that need resolving\n"))
5068 5068 if hint:
5069 5069 ui.warn(hint)
5070 5070 elif ms.mergedriver and ms.mdstate() != 's':
5071 5071 # run conclude step when either a driver-resolved file is requested
5072 5072 # or there are no driver-resolved files
5073 5073 # we can't use 'ret' to determine whether any files are unresolved
5074 5074 # because we might not have tried to resolve some
5075 5075 if ((runconclude or not list(ms.driverresolved()))
5076 5076 and not list(ms.unresolved())):
5077 5077 proceed = mergemod.driverconclude(repo, ms, wctx)
5078 5078 ms.commit()
5079 5079 if not proceed:
5080 5080 return 1
5081 5081
5082 5082 # Nudge users into finishing an unfinished operation
5083 5083 unresolvedf = list(ms.unresolved())
5084 5084 driverresolvedf = list(ms.driverresolved())
5085 5085 if not unresolvedf and not driverresolvedf:
5086 5086 ui.status(_('(no more unresolved files)\n'))
5087 5087 cmdutil.checkafterresolved(repo)
5088 5088 elif not unresolvedf:
5089 5089 ui.status(_('(no more unresolved files -- '
5090 5090 'run "hg resolve --all" to conclude)\n'))
5091 5091
5092 5092 return ret
5093 5093
5094 5094 @command('revert',
5095 5095 [('a', 'all', None, _('revert all changes when no arguments given')),
5096 5096 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5097 5097 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5098 5098 ('C', 'no-backup', None, _('do not save backup copies of files')),
5099 5099 ('i', 'interactive', None, _('interactively select the changes')),
5100 5100 ] + walkopts + dryrunopts,
5101 5101 _('[OPTION]... [-r REV] [NAME]...'),
5102 5102 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5103 5103 def revert(ui, repo, *pats, **opts):
5104 5104 """restore files to their checkout state
5105 5105
5106 5106 .. note::
5107 5107
5108 5108 To check out earlier revisions, you should use :hg:`update REV`.
5109 5109 To cancel an uncommitted merge (and lose your changes),
5110 5110 use :hg:`merge --abort`.
5111 5111
5112 5112 With no revision specified, revert the specified files or directories
5113 5113 to the contents they had in the parent of the working directory.
5114 5114 This restores the contents of files to an unmodified
5115 5115 state and unschedules adds, removes, copies, and renames. If the
5116 5116 working directory has two parents, you must explicitly specify a
5117 5117 revision.
5118 5118
5119 5119 Using the -r/--rev or -d/--date options, revert the given files or
5120 5120 directories to their states as of a specific revision. Because
5121 5121 revert does not change the working directory parents, this will
5122 5122 cause these files to appear modified. This can be helpful to "back
5123 5123 out" some or all of an earlier change. See :hg:`backout` for a
5124 5124 related method.
5125 5125
5126 5126 Modified files are saved with a .orig suffix before reverting.
5127 5127 To disable these backups, use --no-backup. It is possible to store
5128 5128 the backup files in a custom directory relative to the root of the
5129 5129 repository by setting the ``ui.origbackuppath`` configuration
5130 5130 option.
5131 5131
5132 5132 See :hg:`help dates` for a list of formats valid for -d/--date.
5133 5133
5134 5134 See :hg:`help backout` for a way to reverse the effect of an
5135 5135 earlier changeset.
5136 5136
5137 5137 Returns 0 on success.
5138 5138 """
5139 5139
5140 5140 opts = pycompat.byteskwargs(opts)
5141 5141 if opts.get("date"):
5142 5142 if opts.get("rev"):
5143 5143 raise error.Abort(_("you can't specify a revision and a date"))
5144 5144 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5145 5145
5146 5146 parent, p2 = repo.dirstate.parents()
5147 5147 if not opts.get('rev') and p2 != nullid:
5148 5148 # revert after merge is a trap for new users (issue2915)
5149 5149 raise error.Abort(_('uncommitted merge with no revision specified'),
5150 5150 hint=_("use 'hg update' or see 'hg help revert'"))
5151 5151
5152 5152 rev = opts.get('rev')
5153 5153 if rev:
5154 5154 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5155 5155 ctx = scmutil.revsingle(repo, rev)
5156 5156
5157 5157 if (not (pats or opts.get('include') or opts.get('exclude') or
5158 5158 opts.get('all') or opts.get('interactive'))):
5159 5159 msg = _("no files or directories specified")
5160 5160 if p2 != nullid:
5161 5161 hint = _("uncommitted merge, use --all to discard all changes,"
5162 5162 " or 'hg update -C .' to abort the merge")
5163 5163 raise error.Abort(msg, hint=hint)
5164 5164 dirty = any(repo.status())
5165 5165 node = ctx.node()
5166 5166 if node != parent:
5167 5167 if dirty:
5168 5168 hint = _("uncommitted changes, use --all to discard all"
5169 5169 " changes, or 'hg update %d' to update") % ctx.rev()
5170 5170 else:
5171 5171 hint = _("use --all to revert all files,"
5172 5172 " or 'hg update %d' to update") % ctx.rev()
5173 5173 elif dirty:
5174 5174 hint = _("uncommitted changes, use --all to discard all changes")
5175 5175 else:
5176 5176 hint = _("use --all to revert all files")
5177 5177 raise error.Abort(msg, hint=hint)
5178 5178
5179 5179 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
5180 5180 **pycompat.strkwargs(opts))
5181 5181
5182 5182 @command(
5183 5183 'rollback',
5184 5184 dryrunopts + [('f', 'force', False, _('ignore safety measures'))],
5185 5185 helpcategory=command.CATEGORY_MAINTENANCE)
5186 5186 def rollback(ui, repo, **opts):
5187 5187 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5188 5188
5189 5189 Please use :hg:`commit --amend` instead of rollback to correct
5190 5190 mistakes in the last commit.
5191 5191
5192 5192 This command should be used with care. There is only one level of
5193 5193 rollback, and there is no way to undo a rollback. It will also
5194 5194 restore the dirstate at the time of the last transaction, losing
5195 5195 any dirstate changes since that time. This command does not alter
5196 5196 the working directory.
5197 5197
5198 5198 Transactions are used to encapsulate the effects of all commands
5199 5199 that create new changesets or propagate existing changesets into a
5200 5200 repository.
5201 5201
5202 5202 .. container:: verbose
5203 5203
5204 5204 For example, the following commands are transactional, and their
5205 5205 effects can be rolled back:
5206 5206
5207 5207 - commit
5208 5208 - import
5209 5209 - pull
5210 5210 - push (with this repository as the destination)
5211 5211 - unbundle
5212 5212
5213 5213 To avoid permanent data loss, rollback will refuse to rollback a
5214 5214 commit transaction if it isn't checked out. Use --force to
5215 5215 override this protection.
5216 5216
5217 5217 The rollback command can be entirely disabled by setting the
5218 5218 ``ui.rollback`` configuration setting to false. If you're here
5219 5219 because you want to use rollback and it's disabled, you can
5220 5220 re-enable the command by setting ``ui.rollback`` to true.
5221 5221
5222 5222 This command is not intended for use on public repositories. Once
5223 5223 changes are visible for pull by other users, rolling a transaction
5224 5224 back locally is ineffective (someone else may already have pulled
5225 5225 the changes). Furthermore, a race is possible with readers of the
5226 5226 repository; for example an in-progress pull from the repository
5227 5227 may fail if a rollback is performed.
5228 5228
5229 5229 Returns 0 on success, 1 if no rollback data is available.
5230 5230 """
5231 5231 if not ui.configbool('ui', 'rollback'):
5232 5232 raise error.Abort(_('rollback is disabled because it is unsafe'),
5233 5233 hint=('see `hg help -v rollback` for information'))
5234 5234 return repo.rollback(dryrun=opts.get(r'dry_run'),
5235 5235 force=opts.get(r'force'))
5236 5236
5237 5237 @command(
5238 5238 'root', [] + formatteropts, intents={INTENT_READONLY},
5239 5239 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5240 5240 def root(ui, repo, **opts):
5241 5241 """print the root (top) of the current working directory
5242 5242
5243 5243 Print the root directory of the current repository.
5244 5244
5245 5245 .. container:: verbose
5246 5246
5247 5247 Template:
5248 5248
5249 5249 The following keywords are supported in addition to the common template
5250 5250 keywords and functions. See also :hg:`help templates`.
5251 5251
5252 5252 :hgpath: String. Path to the .hg directory.
5253 5253 :storepath: String. Path to the directory holding versioned data.
5254 5254
5255 5255 Returns 0 on success.
5256 5256 """
5257 5257 opts = pycompat.byteskwargs(opts)
5258 5258 with ui.formatter('root', opts) as fm:
5259 5259 fm.startitem()
5260 5260 fm.write('reporoot', '%s\n', repo.root)
5261 5261 fm.data(hgpath=repo.path, storepath=repo.spath)
5262 5262
5263 5263 @command('serve',
5264 5264 [('A', 'accesslog', '', _('name of access log file to write to'),
5265 5265 _('FILE')),
5266 5266 ('d', 'daemon', None, _('run server in background')),
5267 5267 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5268 5268 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5269 5269 # use string type, then we can check if something was passed
5270 5270 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5271 5271 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5272 5272 _('ADDR')),
5273 5273 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5274 5274 _('PREFIX')),
5275 5275 ('n', 'name', '',
5276 5276 _('name to show in web pages (default: working directory)'), _('NAME')),
5277 5277 ('', 'web-conf', '',
5278 5278 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5279 5279 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5280 5280 _('FILE')),
5281 5281 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5282 5282 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
5283 5283 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
5284 5284 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5285 5285 ('', 'style', '', _('template style to use'), _('STYLE')),
5286 5286 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5287 5287 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
5288 5288 ('', 'print-url', None, _('start and print only the URL'))]
5289 5289 + subrepoopts,
5290 5290 _('[OPTION]...'),
5291 5291 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5292 5292 helpbasic=True, optionalrepo=True)
5293 5293 def serve(ui, repo, **opts):
5294 5294 """start stand-alone webserver
5295 5295
5296 5296 Start a local HTTP repository browser and pull server. You can use
5297 5297 this for ad-hoc sharing and browsing of repositories. It is
5298 5298 recommended to use a real web server to serve a repository for
5299 5299 longer periods of time.
5300 5300
5301 5301 Please note that the server does not implement access control.
5302 5302 This means that, by default, anybody can read from the server and
5303 5303 nobody can write to it by default. Set the ``web.allow-push``
5304 5304 option to ``*`` to allow everybody to push to the server. You
5305 5305 should use a real web server if you need to authenticate users.
5306 5306
5307 5307 By default, the server logs accesses to stdout and errors to
5308 5308 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5309 5309 files.
5310 5310
5311 5311 To have the server choose a free port number to listen on, specify
5312 5312 a port number of 0; in this case, the server will print the port
5313 5313 number it uses.
5314 5314
5315 5315 Returns 0 on success.
5316 5316 """
5317 5317
5318 5318 opts = pycompat.byteskwargs(opts)
5319 5319 if opts["stdio"] and opts["cmdserver"]:
5320 5320 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5321 5321 if opts["print_url"] and ui.verbose:
5322 5322 raise error.Abort(_("cannot use --print-url with --verbose"))
5323 5323
5324 5324 if opts["stdio"]:
5325 5325 if repo is None:
5326 5326 raise error.RepoError(_("there is no Mercurial repository here"
5327 5327 " (.hg not found)"))
5328 5328 s = wireprotoserver.sshserver(ui, repo)
5329 5329 s.serve_forever()
5330 5330
5331 5331 service = server.createservice(ui, repo, opts)
5332 5332 return server.runservice(opts, initfn=service.init, runfn=service.run)
5333 5333
5334 5334 @command('shelve',
5335 5335 [('A', 'addremove', None,
5336 5336 _('mark new/missing files as added/removed before shelving')),
5337 5337 ('u', 'unknown', None,
5338 5338 _('store unknown files in the shelve')),
5339 5339 ('', 'cleanup', None,
5340 5340 _('delete all shelved changes')),
5341 5341 ('', 'date', '',
5342 5342 _('shelve with the specified commit date'), _('DATE')),
5343 5343 ('d', 'delete', None,
5344 5344 _('delete the named shelved change(s)')),
5345 5345 ('e', 'edit', False,
5346 5346 _('invoke editor on commit messages')),
5347 5347 ('k', 'keep', False,
5348 5348 _('shelve, but keep changes in the working directory')),
5349 5349 ('l', 'list', None,
5350 5350 _('list current shelves')),
5351 5351 ('m', 'message', '',
5352 5352 _('use text as shelve message'), _('TEXT')),
5353 5353 ('n', 'name', '',
5354 5354 _('use the given name for the shelved commit'), _('NAME')),
5355 5355 ('p', 'patch', None,
5356 5356 _('output patches for changes (provide the names of the shelved '
5357 5357 'changes as positional arguments)')),
5358 5358 ('i', 'interactive', None,
5359 5359 _('interactive mode')),
5360 5360 ('', 'stat', None,
5361 5361 _('output diffstat-style summary of changes (provide the names of '
5362 5362 'the shelved changes as positional arguments)')
5363 5363 )] + cmdutil.walkopts,
5364 5364 _('hg shelve [OPTION]... [FILE]...'),
5365 5365 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5366 5366 def shelve(ui, repo, *pats, **opts):
5367 5367 '''save and set aside changes from the working directory
5368 5368
5369 5369 Shelving takes files that "hg status" reports as not clean, saves
5370 5370 the modifications to a bundle (a shelved change), and reverts the
5371 5371 files so that their state in the working directory becomes clean.
5372 5372
5373 5373 To restore these changes to the working directory, using "hg
5374 5374 unshelve"; this will work even if you switch to a different
5375 5375 commit.
5376 5376
5377 5377 When no files are specified, "hg shelve" saves all not-clean
5378 5378 files. If specific files or directories are named, only changes to
5379 5379 those files are shelved.
5380 5380
5381 5381 In bare shelve (when no files are specified, without interactive,
5382 5382 include and exclude option), shelving remembers information if the
5383 5383 working directory was on newly created branch, in other words working
5384 5384 directory was on different branch than its first parent. In this
5385 5385 situation unshelving restores branch information to the working directory.
5386 5386
5387 5387 Each shelved change has a name that makes it easier to find later.
5388 5388 The name of a shelved change defaults to being based on the active
5389 5389 bookmark, or if there is no active bookmark, the current named
5390 5390 branch. To specify a different name, use ``--name``.
5391 5391
5392 5392 To see a list of existing shelved changes, use the ``--list``
5393 5393 option. For each shelved change, this will print its name, age,
5394 5394 and description; use ``--patch`` or ``--stat`` for more details.
5395 5395
5396 5396 To delete specific shelved changes, use ``--delete``. To delete
5397 5397 all shelved changes, use ``--cleanup``.
5398 5398 '''
5399 5399 opts = pycompat.byteskwargs(opts)
5400 5400 allowables = [
5401 5401 ('addremove', {'create'}), # 'create' is pseudo action
5402 5402 ('unknown', {'create'}),
5403 5403 ('cleanup', {'cleanup'}),
5404 5404 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
5405 5405 ('delete', {'delete'}),
5406 5406 ('edit', {'create'}),
5407 5407 ('keep', {'create'}),
5408 5408 ('list', {'list'}),
5409 5409 ('message', {'create'}),
5410 5410 ('name', {'create'}),
5411 5411 ('patch', {'patch', 'list'}),
5412 5412 ('stat', {'stat', 'list'}),
5413 5413 ]
5414 5414 def checkopt(opt):
5415 5415 if opts.get(opt):
5416 5416 for i, allowable in allowables:
5417 5417 if opts[i] and opt not in allowable:
5418 5418 raise error.Abort(_("options '--%s' and '--%s' may not be "
5419 5419 "used together") % (opt, i))
5420 5420 return True
5421 5421 if checkopt('cleanup'):
5422 5422 if pats:
5423 5423 raise error.Abort(_("cannot specify names when using '--cleanup'"))
5424 5424 return shelvemod.cleanupcmd(ui, repo)
5425 5425 elif checkopt('delete'):
5426 5426 return shelvemod.deletecmd(ui, repo, pats)
5427 5427 elif checkopt('list'):
5428 5428 return shelvemod.listcmd(ui, repo, pats, opts)
5429 5429 elif checkopt('patch') or checkopt('stat'):
5430 5430 return shelvemod.patchcmds(ui, repo, pats, opts)
5431 5431 else:
5432 5432 return shelvemod.createcmd(ui, repo, pats, opts)
5433 5433
5434 5434 _NOTTERSE = 'nothing'
5435 5435
5436 5436 @command('status|st',
5437 5437 [('A', 'all', None, _('show status of all files')),
5438 5438 ('m', 'modified', None, _('show only modified files')),
5439 5439 ('a', 'added', None, _('show only added files')),
5440 5440 ('r', 'removed', None, _('show only removed files')),
5441 5441 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5442 5442 ('c', 'clean', None, _('show only files without changes')),
5443 5443 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5444 5444 ('i', 'ignored', None, _('show only ignored files')),
5445 5445 ('n', 'no-status', None, _('hide status prefix')),
5446 5446 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
5447 5447 ('C', 'copies', None, _('show source of copied files')),
5448 5448 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5449 5449 ('', 'rev', [], _('show difference from revision'), _('REV')),
5450 5450 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5451 5451 ] + walkopts + subrepoopts + formatteropts,
5452 5452 _('[OPTION]... [FILE]...'),
5453 5453 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5454 5454 helpbasic=True, inferrepo=True,
5455 5455 intents={INTENT_READONLY})
5456 5456 def status(ui, repo, *pats, **opts):
5457 5457 """show changed files in the working directory
5458 5458
5459 5459 Show status of files in the repository. If names are given, only
5460 5460 files that match are shown. Files that are clean or ignored or
5461 5461 the source of a copy/move operation, are not listed unless
5462 5462 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5463 5463 Unless options described with "show only ..." are given, the
5464 5464 options -mardu are used.
5465 5465
5466 5466 Option -q/--quiet hides untracked (unknown and ignored) files
5467 5467 unless explicitly requested with -u/--unknown or -i/--ignored.
5468 5468
5469 5469 .. note::
5470 5470
5471 5471 :hg:`status` may appear to disagree with diff if permissions have
5472 5472 changed or a merge has occurred. The standard diff format does
5473 5473 not report permission changes and diff only reports changes
5474 5474 relative to one merge parent.
5475 5475
5476 5476 If one revision is given, it is used as the base revision.
5477 5477 If two revisions are given, the differences between them are
5478 5478 shown. The --change option can also be used as a shortcut to list
5479 5479 the changed files of a revision from its first parent.
5480 5480
5481 5481 The codes used to show the status of files are::
5482 5482
5483 5483 M = modified
5484 5484 A = added
5485 5485 R = removed
5486 5486 C = clean
5487 5487 ! = missing (deleted by non-hg command, but still tracked)
5488 5488 ? = not tracked
5489 5489 I = ignored
5490 5490 = origin of the previous file (with --copies)
5491 5491
5492 5492 .. container:: verbose
5493 5493
5494 5494 The -t/--terse option abbreviates the output by showing only the directory
5495 5495 name if all the files in it share the same status. The option takes an
5496 5496 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5497 5497 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5498 5498 for 'ignored' and 'c' for clean.
5499 5499
5500 5500 It abbreviates only those statuses which are passed. Note that clean and
5501 5501 ignored files are not displayed with '--terse ic' unless the -c/--clean
5502 5502 and -i/--ignored options are also used.
5503 5503
5504 5504 The -v/--verbose option shows information when the repository is in an
5505 5505 unfinished merge, shelve, rebase state etc. You can have this behavior
5506 5506 turned on by default by enabling the ``commands.status.verbose`` option.
5507 5507
5508 5508 You can skip displaying some of these states by setting
5509 5509 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5510 5510 'histedit', 'merge', 'rebase', or 'unshelve'.
5511 5511
5512 5512 Template:
5513 5513
5514 5514 The following keywords are supported in addition to the common template
5515 5515 keywords and functions. See also :hg:`help templates`.
5516 5516
5517 5517 :path: String. Repository-absolute path of the file.
5518 5518 :source: String. Repository-absolute path of the file originated from.
5519 5519 Available if ``--copies`` is specified.
5520 5520 :status: String. Character denoting file's status.
5521 5521
5522 5522 Examples:
5523 5523
5524 5524 - show changes in the working directory relative to a
5525 5525 changeset::
5526 5526
5527 5527 hg status --rev 9353
5528 5528
5529 5529 - show changes in the working directory relative to the
5530 5530 current directory (see :hg:`help patterns` for more information)::
5531 5531
5532 5532 hg status re:
5533 5533
5534 5534 - show all changes including copies in an existing changeset::
5535 5535
5536 5536 hg status --copies --change 9353
5537 5537
5538 5538 - get a NUL separated list of added files, suitable for xargs::
5539 5539
5540 5540 hg status -an0
5541 5541
5542 5542 - show more information about the repository status, abbreviating
5543 5543 added, removed, modified, deleted, and untracked paths::
5544 5544
5545 5545 hg status -v -t mardu
5546 5546
5547 5547 Returns 0 on success.
5548 5548
5549 5549 """
5550 5550
5551 5551 opts = pycompat.byteskwargs(opts)
5552 5552 revs = opts.get('rev')
5553 5553 change = opts.get('change')
5554 5554 terse = opts.get('terse')
5555 5555 if terse is _NOTTERSE:
5556 5556 if revs:
5557 5557 terse = ''
5558 5558 else:
5559 5559 terse = ui.config('commands', 'status.terse')
5560 5560
5561 5561 if revs and change:
5562 5562 msg = _('cannot specify --rev and --change at the same time')
5563 5563 raise error.Abort(msg)
5564 5564 elif revs and terse:
5565 5565 msg = _('cannot use --terse with --rev')
5566 5566 raise error.Abort(msg)
5567 5567 elif change:
5568 5568 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5569 5569 ctx2 = scmutil.revsingle(repo, change, None)
5570 5570 ctx1 = ctx2.p1()
5571 5571 else:
5572 5572 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5573 5573 ctx1, ctx2 = scmutil.revpair(repo, revs)
5574 5574
5575 5575 forcerelativevalue = None
5576 5576 if ui.hasconfig('commands', 'status.relative'):
5577 5577 forcerelativevalue = ui.configbool('commands', 'status.relative')
5578 5578 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats),
5579 5579 forcerelativevalue=forcerelativevalue)
5580 5580
5581 5581 if opts.get('print0'):
5582 5582 end = '\0'
5583 5583 else:
5584 5584 end = '\n'
5585 5585 copy = {}
5586 5586 states = 'modified added removed deleted unknown ignored clean'.split()
5587 5587 show = [k for k in states if opts.get(k)]
5588 5588 if opts.get('all'):
5589 5589 show += ui.quiet and (states[:4] + ['clean']) or states
5590 5590
5591 5591 if not show:
5592 5592 if ui.quiet:
5593 5593 show = states[:4]
5594 5594 else:
5595 5595 show = states[:5]
5596 5596
5597 5597 m = scmutil.match(ctx2, pats, opts)
5598 5598 if terse:
5599 5599 # we need to compute clean and unknown to terse
5600 5600 stat = repo.status(ctx1.node(), ctx2.node(), m,
5601 5601 'ignored' in show or 'i' in terse,
5602 5602 clean=True, unknown=True,
5603 5603 listsubrepos=opts.get('subrepos'))
5604 5604
5605 5605 stat = cmdutil.tersedir(stat, terse)
5606 5606 else:
5607 5607 stat = repo.status(ctx1.node(), ctx2.node(), m,
5608 5608 'ignored' in show, 'clean' in show,
5609 5609 'unknown' in show, opts.get('subrepos'))
5610 5610
5611 5611 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5612 5612
5613 5613 if (opts.get('all') or opts.get('copies')
5614 5614 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5615 5615 copy = copies.pathcopies(ctx1, ctx2, m)
5616 5616
5617 5617 ui.pager('status')
5618 5618 fm = ui.formatter('status', opts)
5619 5619 fmt = '%s' + end
5620 5620 showchar = not opts.get('no_status')
5621 5621
5622 5622 for state, char, files in changestates:
5623 5623 if state in show:
5624 5624 label = 'status.' + state
5625 5625 for f in files:
5626 5626 fm.startitem()
5627 5627 fm.context(ctx=ctx2)
5628 5628 fm.data(path=f)
5629 5629 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5630 5630 fm.plain(fmt % uipathfn(f), label=label)
5631 5631 if f in copy:
5632 5632 fm.data(source=copy[f])
5633 5633 fm.plain((' %s' + end) % uipathfn(copy[f]),
5634 5634 label='status.copied')
5635 5635
5636 5636 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5637 5637 and not ui.plain()):
5638 5638 cmdutil.morestatus(repo, fm)
5639 5639 fm.end()
5640 5640
5641 5641 @command('summary|sum',
5642 5642 [('', 'remote', None, _('check for push and pull'))],
5643 5643 '[--remote]',
5644 5644 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5645 5645 helpbasic=True,
5646 5646 intents={INTENT_READONLY})
5647 5647 def summary(ui, repo, **opts):
5648 5648 """summarize working directory state
5649 5649
5650 5650 This generates a brief summary of the working directory state,
5651 5651 including parents, branch, commit status, phase and available updates.
5652 5652
5653 5653 With the --remote option, this will check the default paths for
5654 5654 incoming and outgoing changes. This can be time-consuming.
5655 5655
5656 5656 Returns 0 on success.
5657 5657 """
5658 5658
5659 5659 opts = pycompat.byteskwargs(opts)
5660 5660 ui.pager('summary')
5661 5661 ctx = repo[None]
5662 5662 parents = ctx.parents()
5663 5663 pnode = parents[0].node()
5664 5664 marks = []
5665 5665
5666 5666 try:
5667 5667 ms = mergemod.mergestate.read(repo)
5668 5668 except error.UnsupportedMergeRecords as e:
5669 5669 s = ' '.join(e.recordtypes)
5670 5670 ui.warn(
5671 5671 _('warning: merge state has unsupported record types: %s\n') % s)
5672 5672 unresolved = []
5673 5673 else:
5674 5674 unresolved = list(ms.unresolved())
5675 5675
5676 5676 for p in parents:
5677 5677 # label with log.changeset (instead of log.parent) since this
5678 5678 # shows a working directory parent *changeset*:
5679 5679 # i18n: column positioning for "hg summary"
5680 5680 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5681 5681 label=logcmdutil.changesetlabels(p))
5682 5682 ui.write(' '.join(p.tags()), label='log.tag')
5683 5683 if p.bookmarks():
5684 5684 marks.extend(p.bookmarks())
5685 5685 if p.rev() == -1:
5686 5686 if not len(repo):
5687 5687 ui.write(_(' (empty repository)'))
5688 5688 else:
5689 5689 ui.write(_(' (no revision checked out)'))
5690 5690 if p.obsolete():
5691 5691 ui.write(_(' (obsolete)'))
5692 5692 if p.isunstable():
5693 5693 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5694 5694 for instability in p.instabilities())
5695 5695 ui.write(' ('
5696 5696 + ', '.join(instabilities)
5697 5697 + ')')
5698 5698 ui.write('\n')
5699 5699 if p.description():
5700 5700 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5701 5701 label='log.summary')
5702 5702
5703 5703 branch = ctx.branch()
5704 5704 bheads = repo.branchheads(branch)
5705 5705 # i18n: column positioning for "hg summary"
5706 5706 m = _('branch: %s\n') % branch
5707 5707 if branch != 'default':
5708 5708 ui.write(m, label='log.branch')
5709 5709 else:
5710 5710 ui.status(m, label='log.branch')
5711 5711
5712 5712 if marks:
5713 5713 active = repo._activebookmark
5714 5714 # i18n: column positioning for "hg summary"
5715 5715 ui.write(_('bookmarks:'), label='log.bookmark')
5716 5716 if active is not None:
5717 5717 if active in marks:
5718 5718 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5719 5719 marks.remove(active)
5720 5720 else:
5721 5721 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5722 5722 for m in marks:
5723 5723 ui.write(' ' + m, label='log.bookmark')
5724 5724 ui.write('\n', label='log.bookmark')
5725 5725
5726 5726 status = repo.status(unknown=True)
5727 5727
5728 5728 c = repo.dirstate.copies()
5729 5729 copied, renamed = [], []
5730 5730 for d, s in c.iteritems():
5731 5731 if s in status.removed:
5732 5732 status.removed.remove(s)
5733 5733 renamed.append(d)
5734 5734 else:
5735 5735 copied.append(d)
5736 5736 if d in status.added:
5737 5737 status.added.remove(d)
5738 5738
5739 5739 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5740 5740
5741 5741 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5742 5742 (ui.label(_('%d added'), 'status.added'), status.added),
5743 5743 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5744 5744 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5745 5745 (ui.label(_('%d copied'), 'status.copied'), copied),
5746 5746 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5747 5747 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5748 5748 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5749 5749 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5750 5750 t = []
5751 5751 for l, s in labels:
5752 5752 if s:
5753 5753 t.append(l % len(s))
5754 5754
5755 5755 t = ', '.join(t)
5756 5756 cleanworkdir = False
5757 5757
5758 5758 if repo.vfs.exists('graftstate'):
5759 5759 t += _(' (graft in progress)')
5760 5760 if repo.vfs.exists('updatestate'):
5761 5761 t += _(' (interrupted update)')
5762 5762 elif len(parents) > 1:
5763 5763 t += _(' (merge)')
5764 5764 elif branch != parents[0].branch():
5765 5765 t += _(' (new branch)')
5766 5766 elif (parents[0].closesbranch() and
5767 5767 pnode in repo.branchheads(branch, closed=True)):
5768 5768 t += _(' (head closed)')
5769 5769 elif not (status.modified or status.added or status.removed or renamed or
5770 5770 copied or subs):
5771 5771 t += _(' (clean)')
5772 5772 cleanworkdir = True
5773 5773 elif pnode not in bheads:
5774 5774 t += _(' (new branch head)')
5775 5775
5776 5776 if parents:
5777 5777 pendingphase = max(p.phase() for p in parents)
5778 5778 else:
5779 5779 pendingphase = phases.public
5780 5780
5781 5781 if pendingphase > phases.newcommitphase(ui):
5782 5782 t += ' (%s)' % phases.phasenames[pendingphase]
5783 5783
5784 5784 if cleanworkdir:
5785 5785 # i18n: column positioning for "hg summary"
5786 5786 ui.status(_('commit: %s\n') % t.strip())
5787 5787 else:
5788 5788 # i18n: column positioning for "hg summary"
5789 5789 ui.write(_('commit: %s\n') % t.strip())
5790 5790
5791 5791 # all ancestors of branch heads - all ancestors of parent = new csets
5792 5792 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5793 5793 bheads))
5794 5794
5795 5795 if new == 0:
5796 5796 # i18n: column positioning for "hg summary"
5797 5797 ui.status(_('update: (current)\n'))
5798 5798 elif pnode not in bheads:
5799 5799 # i18n: column positioning for "hg summary"
5800 5800 ui.write(_('update: %d new changesets (update)\n') % new)
5801 5801 else:
5802 5802 # i18n: column positioning for "hg summary"
5803 5803 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5804 5804 (new, len(bheads)))
5805 5805
5806 5806 t = []
5807 5807 draft = len(repo.revs('draft()'))
5808 5808 if draft:
5809 5809 t.append(_('%d draft') % draft)
5810 5810 secret = len(repo.revs('secret()'))
5811 5811 if secret:
5812 5812 t.append(_('%d secret') % secret)
5813 5813
5814 5814 if draft or secret:
5815 5815 ui.status(_('phases: %s\n') % ', '.join(t))
5816 5816
5817 5817 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5818 5818 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5819 5819 numtrouble = len(repo.revs(trouble + "()"))
5820 5820 # We write all the possibilities to ease translation
5821 5821 troublemsg = {
5822 5822 "orphan": _("orphan: %d changesets"),
5823 5823 "contentdivergent": _("content-divergent: %d changesets"),
5824 5824 "phasedivergent": _("phase-divergent: %d changesets"),
5825 5825 }
5826 5826 if numtrouble > 0:
5827 5827 ui.status(troublemsg[trouble] % numtrouble + "\n")
5828 5828
5829 5829 cmdutil.summaryhooks(ui, repo)
5830 5830
5831 5831 if opts.get('remote'):
5832 5832 needsincoming, needsoutgoing = True, True
5833 5833 else:
5834 5834 needsincoming, needsoutgoing = False, False
5835 5835 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5836 5836 if i:
5837 5837 needsincoming = True
5838 5838 if o:
5839 5839 needsoutgoing = True
5840 5840 if not needsincoming and not needsoutgoing:
5841 5841 return
5842 5842
5843 5843 def getincoming():
5844 5844 source, branches = hg.parseurl(ui.expandpath('default'))
5845 5845 sbranch = branches[0]
5846 5846 try:
5847 5847 other = hg.peer(repo, {}, source)
5848 5848 except error.RepoError:
5849 5849 if opts.get('remote'):
5850 5850 raise
5851 5851 return source, sbranch, None, None, None
5852 5852 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5853 5853 if revs:
5854 5854 revs = [other.lookup(rev) for rev in revs]
5855 5855 ui.debug('comparing with %s\n' % util.hidepassword(source))
5856 5856 repo.ui.pushbuffer()
5857 5857 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5858 5858 repo.ui.popbuffer()
5859 5859 return source, sbranch, other, commoninc, commoninc[1]
5860 5860
5861 5861 if needsincoming:
5862 5862 source, sbranch, sother, commoninc, incoming = getincoming()
5863 5863 else:
5864 5864 source = sbranch = sother = commoninc = incoming = None
5865 5865
5866 5866 def getoutgoing():
5867 5867 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5868 5868 dbranch = branches[0]
5869 5869 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5870 5870 if source != dest:
5871 5871 try:
5872 5872 dother = hg.peer(repo, {}, dest)
5873 5873 except error.RepoError:
5874 5874 if opts.get('remote'):
5875 5875 raise
5876 5876 return dest, dbranch, None, None
5877 5877 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5878 5878 elif sother is None:
5879 5879 # there is no explicit destination peer, but source one is invalid
5880 5880 return dest, dbranch, None, None
5881 5881 else:
5882 5882 dother = sother
5883 5883 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5884 5884 common = None
5885 5885 else:
5886 5886 common = commoninc
5887 5887 if revs:
5888 5888 revs = [repo.lookup(rev) for rev in revs]
5889 5889 repo.ui.pushbuffer()
5890 5890 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5891 5891 commoninc=common)
5892 5892 repo.ui.popbuffer()
5893 5893 return dest, dbranch, dother, outgoing
5894 5894
5895 5895 if needsoutgoing:
5896 5896 dest, dbranch, dother, outgoing = getoutgoing()
5897 5897 else:
5898 5898 dest = dbranch = dother = outgoing = None
5899 5899
5900 5900 if opts.get('remote'):
5901 5901 t = []
5902 5902 if incoming:
5903 5903 t.append(_('1 or more incoming'))
5904 5904 o = outgoing.missing
5905 5905 if o:
5906 5906 t.append(_('%d outgoing') % len(o))
5907 5907 other = dother or sother
5908 5908 if 'bookmarks' in other.listkeys('namespaces'):
5909 5909 counts = bookmarks.summary(repo, other)
5910 5910 if counts[0] > 0:
5911 5911 t.append(_('%d incoming bookmarks') % counts[0])
5912 5912 if counts[1] > 0:
5913 5913 t.append(_('%d outgoing bookmarks') % counts[1])
5914 5914
5915 5915 if t:
5916 5916 # i18n: column positioning for "hg summary"
5917 5917 ui.write(_('remote: %s\n') % (', '.join(t)))
5918 5918 else:
5919 5919 # i18n: column positioning for "hg summary"
5920 5920 ui.status(_('remote: (synced)\n'))
5921 5921
5922 5922 cmdutil.summaryremotehooks(ui, repo, opts,
5923 5923 ((source, sbranch, sother, commoninc),
5924 5924 (dest, dbranch, dother, outgoing)))
5925 5925
5926 5926 @command('tag',
5927 5927 [('f', 'force', None, _('force tag')),
5928 5928 ('l', 'local', None, _('make the tag local')),
5929 5929 ('r', 'rev', '', _('revision to tag'), _('REV')),
5930 5930 ('', 'remove', None, _('remove a tag')),
5931 5931 # -l/--local is already there, commitopts cannot be used
5932 5932 ('e', 'edit', None, _('invoke editor on commit messages')),
5933 5933 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5934 5934 ] + commitopts2,
5935 5935 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
5936 5936 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
5937 5937 def tag(ui, repo, name1, *names, **opts):
5938 5938 """add one or more tags for the current or given revision
5939 5939
5940 5940 Name a particular revision using <name>.
5941 5941
5942 5942 Tags are used to name particular revisions of the repository and are
5943 5943 very useful to compare different revisions, to go back to significant
5944 5944 earlier versions or to mark branch points as releases, etc. Changing
5945 5945 an existing tag is normally disallowed; use -f/--force to override.
5946 5946
5947 5947 If no revision is given, the parent of the working directory is
5948 5948 used.
5949 5949
5950 5950 To facilitate version control, distribution, and merging of tags,
5951 5951 they are stored as a file named ".hgtags" which is managed similarly
5952 5952 to other project files and can be hand-edited if necessary. This
5953 5953 also means that tagging creates a new commit. The file
5954 5954 ".hg/localtags" is used for local tags (not shared among
5955 5955 repositories).
5956 5956
5957 5957 Tag commits are usually made at the head of a branch. If the parent
5958 5958 of the working directory is not a branch head, :hg:`tag` aborts; use
5959 5959 -f/--force to force the tag commit to be based on a non-head
5960 5960 changeset.
5961 5961
5962 5962 See :hg:`help dates` for a list of formats valid for -d/--date.
5963 5963
5964 5964 Since tag names have priority over branch names during revision
5965 5965 lookup, using an existing branch name as a tag name is discouraged.
5966 5966
5967 5967 Returns 0 on success.
5968 5968 """
5969 5969 opts = pycompat.byteskwargs(opts)
5970 5970 with repo.wlock(), repo.lock():
5971 5971 rev_ = "."
5972 5972 names = [t.strip() for t in (name1,) + names]
5973 5973 if len(names) != len(set(names)):
5974 5974 raise error.Abort(_('tag names must be unique'))
5975 5975 for n in names:
5976 5976 scmutil.checknewlabel(repo, n, 'tag')
5977 5977 if not n:
5978 5978 raise error.Abort(_('tag names cannot consist entirely of '
5979 5979 'whitespace'))
5980 5980 if opts.get('rev') and opts.get('remove'):
5981 5981 raise error.Abort(_("--rev and --remove are incompatible"))
5982 5982 if opts.get('rev'):
5983 5983 rev_ = opts['rev']
5984 5984 message = opts.get('message')
5985 5985 if opts.get('remove'):
5986 5986 if opts.get('local'):
5987 5987 expectedtype = 'local'
5988 5988 else:
5989 5989 expectedtype = 'global'
5990 5990
5991 5991 for n in names:
5992 5992 if repo.tagtype(n) == 'global':
5993 5993 alltags = tagsmod.findglobaltags(ui, repo)
5994 5994 if alltags[n][0] == nullid:
5995 5995 raise error.Abort(_("tag '%s' is already removed") % n)
5996 5996 if not repo.tagtype(n):
5997 5997 raise error.Abort(_("tag '%s' does not exist") % n)
5998 5998 if repo.tagtype(n) != expectedtype:
5999 5999 if expectedtype == 'global':
6000 6000 raise error.Abort(_("tag '%s' is not a global tag") % n)
6001 6001 else:
6002 6002 raise error.Abort(_("tag '%s' is not a local tag") % n)
6003 6003 rev_ = 'null'
6004 6004 if not message:
6005 6005 # we don't translate commit messages
6006 6006 message = 'Removed tag %s' % ', '.join(names)
6007 6007 elif not opts.get('force'):
6008 6008 for n in names:
6009 6009 if n in repo.tags():
6010 6010 raise error.Abort(_("tag '%s' already exists "
6011 6011 "(use -f to force)") % n)
6012 6012 if not opts.get('local'):
6013 6013 p1, p2 = repo.dirstate.parents()
6014 6014 if p2 != nullid:
6015 6015 raise error.Abort(_('uncommitted merge'))
6016 6016 bheads = repo.branchheads()
6017 6017 if not opts.get('force') and bheads and p1 not in bheads:
6018 6018 raise error.Abort(_('working directory is not at a branch head '
6019 6019 '(use -f to force)'))
6020 6020 node = scmutil.revsingle(repo, rev_).node()
6021 6021
6022 6022 if not message:
6023 6023 # we don't translate commit messages
6024 6024 message = ('Added tag %s for changeset %s' %
6025 6025 (', '.join(names), short(node)))
6026 6026
6027 6027 date = opts.get('date')
6028 6028 if date:
6029 6029 date = dateutil.parsedate(date)
6030 6030
6031 6031 if opts.get('remove'):
6032 6032 editform = 'tag.remove'
6033 6033 else:
6034 6034 editform = 'tag.add'
6035 6035 editor = cmdutil.getcommiteditor(editform=editform,
6036 6036 **pycompat.strkwargs(opts))
6037 6037
6038 6038 # don't allow tagging the null rev
6039 6039 if (not opts.get('remove') and
6040 6040 scmutil.revsingle(repo, rev_).rev() == nullrev):
6041 6041 raise error.Abort(_("cannot tag null revision"))
6042 6042
6043 6043 tagsmod.tag(repo, names, node, message, opts.get('local'),
6044 6044 opts.get('user'), date, editor=editor)
6045 6045
6046 6046 @command(
6047 6047 'tags', formatteropts, '',
6048 6048 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
6049 6049 intents={INTENT_READONLY})
6050 6050 def tags(ui, repo, **opts):
6051 6051 """list repository tags
6052 6052
6053 6053 This lists both regular and local tags. When the -v/--verbose
6054 6054 switch is used, a third column "local" is printed for local tags.
6055 6055 When the -q/--quiet switch is used, only the tag name is printed.
6056 6056
6057 6057 .. container:: verbose
6058 6058
6059 6059 Template:
6060 6060
6061 6061 The following keywords are supported in addition to the common template
6062 6062 keywords and functions such as ``{tag}``. See also
6063 6063 :hg:`help templates`.
6064 6064
6065 6065 :type: String. ``local`` for local tags.
6066 6066
6067 6067 Returns 0 on success.
6068 6068 """
6069 6069
6070 6070 opts = pycompat.byteskwargs(opts)
6071 6071 ui.pager('tags')
6072 6072 fm = ui.formatter('tags', opts)
6073 6073 hexfunc = fm.hexfunc
6074 6074
6075 6075 for t, n in reversed(repo.tagslist()):
6076 6076 hn = hexfunc(n)
6077 6077 label = 'tags.normal'
6078 6078 tagtype = ''
6079 6079 if repo.tagtype(t) == 'local':
6080 6080 label = 'tags.local'
6081 6081 tagtype = 'local'
6082 6082
6083 6083 fm.startitem()
6084 6084 fm.context(repo=repo)
6085 6085 fm.write('tag', '%s', t, label=label)
6086 6086 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6087 6087 fm.condwrite(not ui.quiet, 'rev node', fmt,
6088 6088 repo.changelog.rev(n), hn, label=label)
6089 6089 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6090 6090 tagtype, label=label)
6091 6091 fm.plain('\n')
6092 6092 fm.end()
6093 6093
6094 6094 @command('tip',
6095 6095 [('p', 'patch', None, _('show patch')),
6096 6096 ('g', 'git', None, _('use git extended diff format')),
6097 6097 ] + templateopts,
6098 6098 _('[-p] [-g]'),
6099 6099 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
6100 6100 def tip(ui, repo, **opts):
6101 6101 """show the tip revision (DEPRECATED)
6102 6102
6103 6103 The tip revision (usually just called the tip) is the changeset
6104 6104 most recently added to the repository (and therefore the most
6105 6105 recently changed head).
6106 6106
6107 6107 If you have just made a commit, that commit will be the tip. If
6108 6108 you have just pulled changes from another repository, the tip of
6109 6109 that repository becomes the current tip. The "tip" tag is special
6110 6110 and cannot be renamed or assigned to a different changeset.
6111 6111
6112 6112 This command is deprecated, please use :hg:`heads` instead.
6113 6113
6114 6114 Returns 0 on success.
6115 6115 """
6116 6116 opts = pycompat.byteskwargs(opts)
6117 6117 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
6118 6118 displayer.show(repo['tip'])
6119 6119 displayer.close()
6120 6120
6121 6121 @command('unbundle',
6122 6122 [('u', 'update', None,
6123 6123 _('update to new branch head if changesets were unbundled'))],
6124 6124 _('[-u] FILE...'),
6125 6125 helpcategory=command.CATEGORY_IMPORT_EXPORT)
6126 6126 def unbundle(ui, repo, fname1, *fnames, **opts):
6127 6127 """apply one or more bundle files
6128 6128
6129 6129 Apply one or more bundle files generated by :hg:`bundle`.
6130 6130
6131 6131 Returns 0 on success, 1 if an update has unresolved files.
6132 6132 """
6133 6133 fnames = (fname1,) + fnames
6134 6134
6135 6135 with repo.lock():
6136 6136 for fname in fnames:
6137 6137 f = hg.openpath(ui, fname)
6138 6138 gen = exchange.readbundle(ui, f, fname)
6139 6139 if isinstance(gen, streamclone.streamcloneapplier):
6140 6140 raise error.Abort(
6141 6141 _('packed bundles cannot be applied with '
6142 6142 '"hg unbundle"'),
6143 6143 hint=_('use "hg debugapplystreamclonebundle"'))
6144 6144 url = 'bundle:' + fname
6145 6145 try:
6146 6146 txnname = 'unbundle'
6147 6147 if not isinstance(gen, bundle2.unbundle20):
6148 6148 txnname = 'unbundle\n%s' % util.hidepassword(url)
6149 6149 with repo.transaction(txnname) as tr:
6150 6150 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
6151 6151 url=url)
6152 6152 except error.BundleUnknownFeatureError as exc:
6153 6153 raise error.Abort(
6154 6154 _('%s: unknown bundle feature, %s') % (fname, exc),
6155 6155 hint=_("see https://mercurial-scm.org/"
6156 6156 "wiki/BundleFeature for more "
6157 6157 "information"))
6158 6158 modheads = bundle2.combinechangegroupresults(op)
6159 6159
6160 6160 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
6161 6161
6162 6162 @command('unshelve',
6163 6163 [('a', 'abort', None,
6164 6164 _('abort an incomplete unshelve operation')),
6165 6165 ('c', 'continue', None,
6166 6166 _('continue an incomplete unshelve operation')),
6167 6167 ('i', 'interactive', None,
6168 6168 _('use interactive mode (EXPERIMENTAL)')),
6169 6169 ('k', 'keep', None,
6170 6170 _('keep shelve after unshelving')),
6171 6171 ('n', 'name', '',
6172 6172 _('restore shelved change with given name'), _('NAME')),
6173 6173 ('t', 'tool', '', _('specify merge tool')),
6174 6174 ('', 'date', '',
6175 6175 _('set date for temporary commits (DEPRECATED)'), _('DATE'))],
6176 6176 _('hg unshelve [OPTION]... [FILE]... [-n SHELVED]'),
6177 6177 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
6178 6178 def unshelve(ui, repo, *shelved, **opts):
6179 6179 """restore a shelved change to the working directory
6180 6180
6181 6181 This command accepts an optional name of a shelved change to
6182 6182 restore. If none is given, the most recent shelved change is used.
6183 6183
6184 6184 If a shelved change is applied successfully, the bundle that
6185 6185 contains the shelved changes is moved to a backup location
6186 6186 (.hg/shelve-backup).
6187 6187
6188 6188 Since you can restore a shelved change on top of an arbitrary
6189 6189 commit, it is possible that unshelving will result in a conflict
6190 6190 between your changes and the commits you are unshelving onto. If
6191 6191 this occurs, you must resolve the conflict, then use
6192 6192 ``--continue`` to complete the unshelve operation. (The bundle
6193 6193 will not be moved until you successfully complete the unshelve.)
6194 6194
6195 6195 (Alternatively, you can use ``--abort`` to abandon an unshelve
6196 6196 that causes a conflict. This reverts the unshelved changes, and
6197 6197 leaves the bundle in place.)
6198 6198
6199 6199 If bare shelved change (when no files are specified, without interactive,
6200 6200 include and exclude option) was done on newly created branch it would
6201 6201 restore branch information to the working directory.
6202 6202
6203 6203 After a successful unshelve, the shelved changes are stored in a
6204 6204 backup directory. Only the N most recent backups are kept. N
6205 6205 defaults to 10 but can be overridden using the ``shelve.maxbackups``
6206 6206 configuration option.
6207 6207
6208 6208 .. container:: verbose
6209 6209
6210 6210 Timestamp in seconds is used to decide order of backups. More
6211 6211 than ``maxbackups`` backups are kept, if same timestamp
6212 6212 prevents from deciding exact order of them, for safety.
6213
6214 Selected changes can be unshelved with ``--interactive`` flag.
6215 The working directory is updated with the selected changes, and
6216 only the unselected changes remain shelved.
6217 Note: The whole shelve is applied to working directory first before
6218 running interactively. So, this will bring up all the conflicts between
6219 working directory and the shelve, irrespective of which changes will be
6220 unshelved.
6213 6221 """
6214 6222 with repo.wlock():
6215 6223 return shelvemod.dounshelve(ui, repo, *shelved, **opts)
6216 6224
6217 6225 statemod.addunfinished(
6218 6226 'unshelve', fname='shelvedstate', continueflag=True,
6219 6227 abortfunc=shelvemod.hgabortunshelve,
6220 6228 continuefunc=shelvemod.hgcontinueunshelve,
6221 6229 cmdmsg=_('unshelve already in progress'),
6222 6230 )
6223 6231
6224 6232 @command('update|up|checkout|co',
6225 6233 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6226 6234 ('c', 'check', None, _('require clean working directory')),
6227 6235 ('m', 'merge', None, _('merge uncommitted changes')),
6228 6236 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6229 6237 ('r', 'rev', '', _('revision'), _('REV'))
6230 6238 ] + mergetoolopts,
6231 6239 _('[-C|-c|-m] [-d DATE] [[-r] REV]'),
6232 6240 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6233 6241 helpbasic=True)
6234 6242 def update(ui, repo, node=None, **opts):
6235 6243 """update working directory (or switch revisions)
6236 6244
6237 6245 Update the repository's working directory to the specified
6238 6246 changeset. If no changeset is specified, update to the tip of the
6239 6247 current named branch and move the active bookmark (see :hg:`help
6240 6248 bookmarks`).
6241 6249
6242 6250 Update sets the working directory's parent revision to the specified
6243 6251 changeset (see :hg:`help parents`).
6244 6252
6245 6253 If the changeset is not a descendant or ancestor of the working
6246 6254 directory's parent and there are uncommitted changes, the update is
6247 6255 aborted. With the -c/--check option, the working directory is checked
6248 6256 for uncommitted changes; if none are found, the working directory is
6249 6257 updated to the specified changeset.
6250 6258
6251 6259 .. container:: verbose
6252 6260
6253 6261 The -C/--clean, -c/--check, and -m/--merge options control what
6254 6262 happens if the working directory contains uncommitted changes.
6255 6263 At most of one of them can be specified.
6256 6264
6257 6265 1. If no option is specified, and if
6258 6266 the requested changeset is an ancestor or descendant of
6259 6267 the working directory's parent, the uncommitted changes
6260 6268 are merged into the requested changeset and the merged
6261 6269 result is left uncommitted. If the requested changeset is
6262 6270 not an ancestor or descendant (that is, it is on another
6263 6271 branch), the update is aborted and the uncommitted changes
6264 6272 are preserved.
6265 6273
6266 6274 2. With the -m/--merge option, the update is allowed even if the
6267 6275 requested changeset is not an ancestor or descendant of
6268 6276 the working directory's parent.
6269 6277
6270 6278 3. With the -c/--check option, the update is aborted and the
6271 6279 uncommitted changes are preserved.
6272 6280
6273 6281 4. With the -C/--clean option, uncommitted changes are discarded and
6274 6282 the working directory is updated to the requested changeset.
6275 6283
6276 6284 To cancel an uncommitted merge (and lose your changes), use
6277 6285 :hg:`merge --abort`.
6278 6286
6279 6287 Use null as the changeset to remove the working directory (like
6280 6288 :hg:`clone -U`).
6281 6289
6282 6290 If you want to revert just one file to an older revision, use
6283 6291 :hg:`revert [-r REV] NAME`.
6284 6292
6285 6293 See :hg:`help dates` for a list of formats valid for -d/--date.
6286 6294
6287 6295 Returns 0 on success, 1 if there are unresolved files.
6288 6296 """
6289 6297 rev = opts.get(r'rev')
6290 6298 date = opts.get(r'date')
6291 6299 clean = opts.get(r'clean')
6292 6300 check = opts.get(r'check')
6293 6301 merge = opts.get(r'merge')
6294 6302 if rev and node:
6295 6303 raise error.Abort(_("please specify just one revision"))
6296 6304
6297 6305 if ui.configbool('commands', 'update.requiredest'):
6298 6306 if not node and not rev and not date:
6299 6307 raise error.Abort(_('you must specify a destination'),
6300 6308 hint=_('for example: hg update ".::"'))
6301 6309
6302 6310 if rev is None or rev == '':
6303 6311 rev = node
6304 6312
6305 6313 if date and rev is not None:
6306 6314 raise error.Abort(_("you can't specify a revision and a date"))
6307 6315
6308 6316 if len([x for x in (clean, check, merge) if x]) > 1:
6309 6317 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
6310 6318 "or -m/--merge"))
6311 6319
6312 6320 updatecheck = None
6313 6321 if check:
6314 6322 updatecheck = 'abort'
6315 6323 elif merge:
6316 6324 updatecheck = 'none'
6317 6325
6318 6326 with repo.wlock():
6319 6327 cmdutil.clearunfinished(repo)
6320 6328 if date:
6321 6329 rev = cmdutil.finddate(ui, repo, date)
6322 6330
6323 6331 # if we defined a bookmark, we have to remember the original name
6324 6332 brev = rev
6325 6333 if rev:
6326 6334 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
6327 6335 ctx = scmutil.revsingle(repo, rev, default=None)
6328 6336 rev = ctx.rev()
6329 6337 hidden = ctx.hidden()
6330 6338 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
6331 6339 with ui.configoverride(overrides, 'update'):
6332 6340 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
6333 6341 updatecheck=updatecheck)
6334 6342 if hidden:
6335 6343 ctxstr = ctx.hex()[:12]
6336 6344 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
6337 6345
6338 6346 if ctx.obsolete():
6339 6347 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
6340 6348 ui.warn("(%s)\n" % obsfatemsg)
6341 6349 return ret
6342 6350
6343 6351 @command('verify',
6344 6352 [('', 'full', False, 'perform more checks (EXPERIMENTAL)')],
6345 6353 helpcategory=command.CATEGORY_MAINTENANCE)
6346 6354 def verify(ui, repo, **opts):
6347 6355 """verify the integrity of the repository
6348 6356
6349 6357 Verify the integrity of the current repository.
6350 6358
6351 6359 This will perform an extensive check of the repository's
6352 6360 integrity, validating the hashes and checksums of each entry in
6353 6361 the changelog, manifest, and tracked files, as well as the
6354 6362 integrity of their crosslinks and indices.
6355 6363
6356 6364 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6357 6365 for more information about recovery from corruption of the
6358 6366 repository.
6359 6367
6360 6368 Returns 0 on success, 1 if errors are encountered.
6361 6369 """
6362 6370 opts = pycompat.byteskwargs(opts)
6363 6371
6364 6372 level = None
6365 6373 if opts['full']:
6366 6374 level = verifymod.VERIFY_FULL
6367 6375 return hg.verify(repo, level)
6368 6376
6369 6377 @command(
6370 6378 'version', [] + formatteropts, helpcategory=command.CATEGORY_HELP,
6371 6379 norepo=True, intents={INTENT_READONLY})
6372 6380 def version_(ui, **opts):
6373 6381 """output version and copyright information
6374 6382
6375 6383 .. container:: verbose
6376 6384
6377 6385 Template:
6378 6386
6379 6387 The following keywords are supported. See also :hg:`help templates`.
6380 6388
6381 6389 :extensions: List of extensions.
6382 6390 :ver: String. Version number.
6383 6391
6384 6392 And each entry of ``{extensions}`` provides the following sub-keywords
6385 6393 in addition to ``{ver}``.
6386 6394
6387 6395 :bundled: Boolean. True if included in the release.
6388 6396 :name: String. Extension name.
6389 6397 """
6390 6398 opts = pycompat.byteskwargs(opts)
6391 6399 if ui.verbose:
6392 6400 ui.pager('version')
6393 6401 fm = ui.formatter("version", opts)
6394 6402 fm.startitem()
6395 6403 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6396 6404 util.version())
6397 6405 license = _(
6398 6406 "(see https://mercurial-scm.org for more information)\n"
6399 6407 "\nCopyright (C) 2005-2019 Matt Mackall and others\n"
6400 6408 "This is free software; see the source for copying conditions. "
6401 6409 "There is NO\nwarranty; "
6402 6410 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6403 6411 )
6404 6412 if not ui.quiet:
6405 6413 fm.plain(license)
6406 6414
6407 6415 if ui.verbose:
6408 6416 fm.plain(_("\nEnabled extensions:\n\n"))
6409 6417 # format names and versions into columns
6410 6418 names = []
6411 6419 vers = []
6412 6420 isinternals = []
6413 6421 for name, module in extensions.extensions():
6414 6422 names.append(name)
6415 6423 vers.append(extensions.moduleversion(module) or None)
6416 6424 isinternals.append(extensions.ismoduleinternal(module))
6417 6425 fn = fm.nested("extensions", tmpl='{name}\n')
6418 6426 if names:
6419 6427 namefmt = " %%-%ds " % max(len(n) for n in names)
6420 6428 places = [_("external"), _("internal")]
6421 6429 for n, v, p in zip(names, vers, isinternals):
6422 6430 fn.startitem()
6423 6431 fn.condwrite(ui.verbose, "name", namefmt, n)
6424 6432 if ui.verbose:
6425 6433 fn.plain("%s " % places[p])
6426 6434 fn.data(bundled=p)
6427 6435 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6428 6436 if ui.verbose:
6429 6437 fn.plain("\n")
6430 6438 fn.end()
6431 6439 fm.end()
6432 6440
6433 6441 def loadcmdtable(ui, name, cmdtable):
6434 6442 """Load command functions from specified cmdtable
6435 6443 """
6436 6444 overrides = [cmd for cmd in cmdtable if cmd in table]
6437 6445 if overrides:
6438 6446 ui.warn(_("extension '%s' overrides commands: %s\n")
6439 6447 % (name, " ".join(overrides)))
6440 6448 table.update(cmdtable)
General Comments 0
You need to be logged in to leave comments. Login now