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