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