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