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