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