##// END OF EJS Templates
commit: add a check if it is trying to close an already closed branch head...
Sushil khanchi -
r42693:9f7cb777 default
parent child Browse files
Show More
@@ -1,6269 +1,6272 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 if not bheads:
1676 if repo['.'].closesbranch():
1677 raise error.Abort(_('current revision is already a branch closing'
1678 ' head'))
1679 elif not bheads:
1677 1680 raise error.Abort(_('can only close branch heads'))
1678 1681 elif branch == repo['.'].branch() and repo['.'].node() not in bheads:
1679 1682 raise error.Abort(_('can only close branch heads'))
1680 1683 elif opts.get('amend'):
1681 1684 if (repo['.'].p1().branch() != branch and
1682 1685 repo['.'].p2().branch() != branch):
1683 1686 raise error.Abort(_('can only close branch heads'))
1684 1687
1685 1688 if opts.get('amend'):
1686 1689 if ui.configbool('ui', 'commitsubrepos'):
1687 1690 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1688 1691
1689 1692 old = repo['.']
1690 1693 rewriteutil.precheck(repo, [old.rev()], 'amend')
1691 1694
1692 1695 # Currently histedit gets confused if an amend happens while histedit
1693 1696 # is in progress. Since we have a checkunfinished command, we are
1694 1697 # temporarily honoring it.
1695 1698 #
1696 1699 # Note: eventually this guard will be removed. Please do not expect
1697 1700 # this behavior to remain.
1698 1701 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1699 1702 cmdutil.checkunfinished(repo)
1700 1703
1701 1704 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1702 1705 if node == old.node():
1703 1706 ui.status(_("nothing changed\n"))
1704 1707 return 1
1705 1708 else:
1706 1709 def commitfunc(ui, repo, message, match, opts):
1707 1710 overrides = {}
1708 1711 if opts.get('secret'):
1709 1712 overrides[('phases', 'new-commit')] = 'secret'
1710 1713
1711 1714 baseui = repo.baseui
1712 1715 with baseui.configoverride(overrides, 'commit'):
1713 1716 with ui.configoverride(overrides, 'commit'):
1714 1717 editform = cmdutil.mergeeditform(repo[None],
1715 1718 'commit.normal')
1716 1719 editor = cmdutil.getcommiteditor(
1717 1720 editform=editform, **pycompat.strkwargs(opts))
1718 1721 return repo.commit(message,
1719 1722 opts.get('user'),
1720 1723 opts.get('date'),
1721 1724 match,
1722 1725 editor=editor,
1723 1726 extra=extra)
1724 1727
1725 1728 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1726 1729
1727 1730 if not node:
1728 1731 stat = cmdutil.postcommitstatus(repo, pats, opts)
1729 1732 if stat[3]:
1730 1733 ui.status(_("nothing changed (%d missing files, see "
1731 1734 "'hg status')\n") % len(stat[3]))
1732 1735 else:
1733 1736 ui.status(_("nothing changed\n"))
1734 1737 return 1
1735 1738
1736 1739 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1737 1740
1738 1741 if not ui.quiet and ui.configbool('commands', 'commit.post-status'):
1739 1742 status(ui, repo, modified=True, added=True, removed=True, deleted=True,
1740 1743 unknown=True, subrepos=opts.get('subrepos'))
1741 1744
1742 1745 @command('config|showconfig|debugconfig',
1743 1746 [('u', 'untrusted', None, _('show untrusted configuration options')),
1744 1747 ('e', 'edit', None, _('edit user config')),
1745 1748 ('l', 'local', None, _('edit repository config')),
1746 1749 ('g', 'global', None, _('edit global config'))] + formatteropts,
1747 1750 _('[-u] [NAME]...'),
1748 1751 helpcategory=command.CATEGORY_HELP,
1749 1752 optionalrepo=True,
1750 1753 intents={INTENT_READONLY})
1751 1754 def config(ui, repo, *values, **opts):
1752 1755 """show combined config settings from all hgrc files
1753 1756
1754 1757 With no arguments, print names and values of all config items.
1755 1758
1756 1759 With one argument of the form section.name, print just the value
1757 1760 of that config item.
1758 1761
1759 1762 With multiple arguments, print names and values of all config
1760 1763 items with matching section names or section.names.
1761 1764
1762 1765 With --edit, start an editor on the user-level config file. With
1763 1766 --global, edit the system-wide config file. With --local, edit the
1764 1767 repository-level config file.
1765 1768
1766 1769 With --debug, the source (filename and line number) is printed
1767 1770 for each config item.
1768 1771
1769 1772 See :hg:`help config` for more information about config files.
1770 1773
1771 1774 .. container:: verbose
1772 1775
1773 1776 Template:
1774 1777
1775 1778 The following keywords are supported. See also :hg:`help templates`.
1776 1779
1777 1780 :name: String. Config name.
1778 1781 :source: String. Filename and line number where the item is defined.
1779 1782 :value: String. Config value.
1780 1783
1781 1784 Returns 0 on success, 1 if NAME does not exist.
1782 1785
1783 1786 """
1784 1787
1785 1788 opts = pycompat.byteskwargs(opts)
1786 1789 if opts.get('edit') or opts.get('local') or opts.get('global'):
1787 1790 if opts.get('local') and opts.get('global'):
1788 1791 raise error.Abort(_("can't use --local and --global together"))
1789 1792
1790 1793 if opts.get('local'):
1791 1794 if not repo:
1792 1795 raise error.Abort(_("can't use --local outside a repository"))
1793 1796 paths = [repo.vfs.join('hgrc')]
1794 1797 elif opts.get('global'):
1795 1798 paths = rcutil.systemrcpath()
1796 1799 else:
1797 1800 paths = rcutil.userrcpath()
1798 1801
1799 1802 for f in paths:
1800 1803 if os.path.exists(f):
1801 1804 break
1802 1805 else:
1803 1806 if opts.get('global'):
1804 1807 samplehgrc = uimod.samplehgrcs['global']
1805 1808 elif opts.get('local'):
1806 1809 samplehgrc = uimod.samplehgrcs['local']
1807 1810 else:
1808 1811 samplehgrc = uimod.samplehgrcs['user']
1809 1812
1810 1813 f = paths[0]
1811 1814 fp = open(f, "wb")
1812 1815 fp.write(util.tonativeeol(samplehgrc))
1813 1816 fp.close()
1814 1817
1815 1818 editor = ui.geteditor()
1816 1819 ui.system("%s \"%s\"" % (editor, f),
1817 1820 onerr=error.Abort, errprefix=_("edit failed"),
1818 1821 blockedtag='config_edit')
1819 1822 return
1820 1823 ui.pager('config')
1821 1824 fm = ui.formatter('config', opts)
1822 1825 for t, f in rcutil.rccomponents():
1823 1826 if t == 'path':
1824 1827 ui.debug('read config from: %s\n' % f)
1825 1828 elif t == 'items':
1826 1829 for section, name, value, source in f:
1827 1830 ui.debug('set config by: %s\n' % source)
1828 1831 else:
1829 1832 raise error.ProgrammingError('unknown rctype: %s' % t)
1830 1833 untrusted = bool(opts.get('untrusted'))
1831 1834
1832 1835 selsections = selentries = []
1833 1836 if values:
1834 1837 selsections = [v for v in values if '.' not in v]
1835 1838 selentries = [v for v in values if '.' in v]
1836 1839 uniquesel = (len(selentries) == 1 and not selsections)
1837 1840 selsections = set(selsections)
1838 1841 selentries = set(selentries)
1839 1842
1840 1843 matched = False
1841 1844 for section, name, value in ui.walkconfig(untrusted=untrusted):
1842 1845 source = ui.configsource(section, name, untrusted)
1843 1846 value = pycompat.bytestr(value)
1844 1847 if fm.isplain():
1845 1848 source = source or 'none'
1846 1849 value = value.replace('\n', '\\n')
1847 1850 entryname = section + '.' + name
1848 1851 if values and not (section in selsections or entryname in selentries):
1849 1852 continue
1850 1853 fm.startitem()
1851 1854 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1852 1855 if uniquesel:
1853 1856 fm.data(name=entryname)
1854 1857 fm.write('value', '%s\n', value)
1855 1858 else:
1856 1859 fm.write('name value', '%s=%s\n', entryname, value)
1857 1860 matched = True
1858 1861 fm.end()
1859 1862 if matched:
1860 1863 return 0
1861 1864 return 1
1862 1865
1863 1866 @command('copy|cp',
1864 1867 [('A', 'after', None, _('record a copy that has already occurred')),
1865 1868 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1866 1869 ] + walkopts + dryrunopts,
1867 1870 _('[OPTION]... [SOURCE]... DEST'),
1868 1871 helpcategory=command.CATEGORY_FILE_CONTENTS)
1869 1872 def copy(ui, repo, *pats, **opts):
1870 1873 """mark files as copied for the next commit
1871 1874
1872 1875 Mark dest as having copies of source files. If dest is a
1873 1876 directory, copies are put in that directory. If dest is a file,
1874 1877 the source must be a single file.
1875 1878
1876 1879 By default, this command copies the contents of files as they
1877 1880 exist in the working directory. If invoked with -A/--after, the
1878 1881 operation is recorded, but no copying is performed.
1879 1882
1880 1883 This command takes effect with the next commit. To undo a copy
1881 1884 before that, see :hg:`revert`.
1882 1885
1883 1886 Returns 0 on success, 1 if errors are encountered.
1884 1887 """
1885 1888 opts = pycompat.byteskwargs(opts)
1886 1889 with repo.wlock(False):
1887 1890 return cmdutil.copy(ui, repo, pats, opts)
1888 1891
1889 1892 @command(
1890 1893 'debugcommands', [], _('[COMMAND]'),
1891 1894 helpcategory=command.CATEGORY_HELP,
1892 1895 norepo=True)
1893 1896 def debugcommands(ui, cmd='', *args):
1894 1897 """list all available commands and options"""
1895 1898 for cmd, vals in sorted(table.iteritems()):
1896 1899 cmd = cmd.split('|')[0]
1897 1900 opts = ', '.join([i[1] for i in vals[1]])
1898 1901 ui.write('%s: %s\n' % (cmd, opts))
1899 1902
1900 1903 @command('debugcomplete',
1901 1904 [('o', 'options', None, _('show the command options'))],
1902 1905 _('[-o] CMD'),
1903 1906 helpcategory=command.CATEGORY_HELP,
1904 1907 norepo=True)
1905 1908 def debugcomplete(ui, cmd='', **opts):
1906 1909 """returns the completion list associated with the given command"""
1907 1910
1908 1911 if opts.get(r'options'):
1909 1912 options = []
1910 1913 otables = [globalopts]
1911 1914 if cmd:
1912 1915 aliases, entry = cmdutil.findcmd(cmd, table, False)
1913 1916 otables.append(entry[1])
1914 1917 for t in otables:
1915 1918 for o in t:
1916 1919 if "(DEPRECATED)" in o[3]:
1917 1920 continue
1918 1921 if o[0]:
1919 1922 options.append('-%s' % o[0])
1920 1923 options.append('--%s' % o[1])
1921 1924 ui.write("%s\n" % "\n".join(options))
1922 1925 return
1923 1926
1924 1927 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1925 1928 if ui.verbose:
1926 1929 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1927 1930 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1928 1931
1929 1932 @command('diff',
1930 1933 [('r', 'rev', [], _('revision'), _('REV')),
1931 1934 ('c', 'change', '', _('change made by revision'), _('REV'))
1932 1935 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1933 1936 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1934 1937 helpcategory=command.CATEGORY_FILE_CONTENTS,
1935 1938 helpbasic=True, inferrepo=True, intents={INTENT_READONLY})
1936 1939 def diff(ui, repo, *pats, **opts):
1937 1940 """diff repository (or selected files)
1938 1941
1939 1942 Show differences between revisions for the specified files.
1940 1943
1941 1944 Differences between files are shown using the unified diff format.
1942 1945
1943 1946 .. note::
1944 1947
1945 1948 :hg:`diff` may generate unexpected results for merges, as it will
1946 1949 default to comparing against the working directory's first
1947 1950 parent changeset if no revisions are specified.
1948 1951
1949 1952 When two revision arguments are given, then changes are shown
1950 1953 between those revisions. If only one revision is specified then
1951 1954 that revision is compared to the working directory, and, when no
1952 1955 revisions are specified, the working directory files are compared
1953 1956 to its first parent.
1954 1957
1955 1958 Alternatively you can specify -c/--change with a revision to see
1956 1959 the changes in that changeset relative to its first parent.
1957 1960
1958 1961 Without the -a/--text option, diff will avoid generating diffs of
1959 1962 files it detects as binary. With -a, diff will generate a diff
1960 1963 anyway, probably with undesirable results.
1961 1964
1962 1965 Use the -g/--git option to generate diffs in the git extended diff
1963 1966 format. For more information, read :hg:`help diffs`.
1964 1967
1965 1968 .. container:: verbose
1966 1969
1967 1970 Examples:
1968 1971
1969 1972 - compare a file in the current working directory to its parent::
1970 1973
1971 1974 hg diff foo.c
1972 1975
1973 1976 - compare two historical versions of a directory, with rename info::
1974 1977
1975 1978 hg diff --git -r 1.0:1.2 lib/
1976 1979
1977 1980 - get change stats relative to the last change on some date::
1978 1981
1979 1982 hg diff --stat -r "date('may 2')"
1980 1983
1981 1984 - diff all newly-added files that contain a keyword::
1982 1985
1983 1986 hg diff "set:added() and grep(GNU)"
1984 1987
1985 1988 - compare a revision and its parents::
1986 1989
1987 1990 hg diff -c 9353 # compare against first parent
1988 1991 hg diff -r 9353^:9353 # same using revset syntax
1989 1992 hg diff -r 9353^2:9353 # compare against the second parent
1990 1993
1991 1994 Returns 0 on success.
1992 1995 """
1993 1996
1994 1997 opts = pycompat.byteskwargs(opts)
1995 1998 revs = opts.get('rev')
1996 1999 change = opts.get('change')
1997 2000 stat = opts.get('stat')
1998 2001 reverse = opts.get('reverse')
1999 2002
2000 2003 if revs and change:
2001 2004 msg = _('cannot specify --rev and --change at the same time')
2002 2005 raise error.Abort(msg)
2003 2006 elif change:
2004 2007 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
2005 2008 ctx2 = scmutil.revsingle(repo, change, None)
2006 2009 ctx1 = ctx2.p1()
2007 2010 else:
2008 2011 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
2009 2012 ctx1, ctx2 = scmutil.revpair(repo, revs)
2010 2013 node1, node2 = ctx1.node(), ctx2.node()
2011 2014
2012 2015 if reverse:
2013 2016 node1, node2 = node2, node1
2014 2017
2015 2018 diffopts = patch.diffallopts(ui, opts)
2016 2019 m = scmutil.match(ctx2, pats, opts)
2017 2020 m = repo.narrowmatch(m)
2018 2021 ui.pager('diff')
2019 2022 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2020 2023 listsubrepos=opts.get('subrepos'),
2021 2024 root=opts.get('root'))
2022 2025
2023 2026 @command('export',
2024 2027 [('B', 'bookmark', '',
2025 2028 _('export changes only reachable by given bookmark'), _('BOOKMARK')),
2026 2029 ('o', 'output', '',
2027 2030 _('print output to file with formatted name'), _('FORMAT')),
2028 2031 ('', 'switch-parent', None, _('diff against the second parent')),
2029 2032 ('r', 'rev', [], _('revisions to export'), _('REV')),
2030 2033 ] + diffopts + formatteropts,
2031 2034 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2032 2035 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2033 2036 helpbasic=True, intents={INTENT_READONLY})
2034 2037 def export(ui, repo, *changesets, **opts):
2035 2038 """dump the header and diffs for one or more changesets
2036 2039
2037 2040 Print the changeset header and diffs for one or more revisions.
2038 2041 If no revision is given, the parent of the working directory is used.
2039 2042
2040 2043 The information shown in the changeset header is: author, date,
2041 2044 branch name (if non-default), changeset hash, parent(s) and commit
2042 2045 comment.
2043 2046
2044 2047 .. note::
2045 2048
2046 2049 :hg:`export` may generate unexpected diff output for merge
2047 2050 changesets, as it will compare the merge changeset against its
2048 2051 first parent only.
2049 2052
2050 2053 Output may be to a file, in which case the name of the file is
2051 2054 given using a template string. See :hg:`help templates`. In addition
2052 2055 to the common template keywords, the following formatting rules are
2053 2056 supported:
2054 2057
2055 2058 :``%%``: literal "%" character
2056 2059 :``%H``: changeset hash (40 hexadecimal digits)
2057 2060 :``%N``: number of patches being generated
2058 2061 :``%R``: changeset revision number
2059 2062 :``%b``: basename of the exporting repository
2060 2063 :``%h``: short-form changeset hash (12 hexadecimal digits)
2061 2064 :``%m``: first line of the commit message (only alphanumeric characters)
2062 2065 :``%n``: zero-padded sequence number, starting at 1
2063 2066 :``%r``: zero-padded changeset revision number
2064 2067 :``\\``: literal "\\" character
2065 2068
2066 2069 Without the -a/--text option, export will avoid generating diffs
2067 2070 of files it detects as binary. With -a, export will generate a
2068 2071 diff anyway, probably with undesirable results.
2069 2072
2070 2073 With -B/--bookmark changesets reachable by the given bookmark are
2071 2074 selected.
2072 2075
2073 2076 Use the -g/--git option to generate diffs in the git extended diff
2074 2077 format. See :hg:`help diffs` for more information.
2075 2078
2076 2079 With the --switch-parent option, the diff will be against the
2077 2080 second parent. It can be useful to review a merge.
2078 2081
2079 2082 .. container:: verbose
2080 2083
2081 2084 Template:
2082 2085
2083 2086 The following keywords are supported in addition to the common template
2084 2087 keywords and functions. See also :hg:`help templates`.
2085 2088
2086 2089 :diff: String. Diff content.
2087 2090 :parents: List of strings. Parent nodes of the changeset.
2088 2091
2089 2092 Examples:
2090 2093
2091 2094 - use export and import to transplant a bugfix to the current
2092 2095 branch::
2093 2096
2094 2097 hg export -r 9353 | hg import -
2095 2098
2096 2099 - export all the changesets between two revisions to a file with
2097 2100 rename information::
2098 2101
2099 2102 hg export --git -r 123:150 > changes.txt
2100 2103
2101 2104 - split outgoing changes into a series of patches with
2102 2105 descriptive names::
2103 2106
2104 2107 hg export -r "outgoing()" -o "%n-%m.patch"
2105 2108
2106 2109 Returns 0 on success.
2107 2110 """
2108 2111 opts = pycompat.byteskwargs(opts)
2109 2112 bookmark = opts.get('bookmark')
2110 2113 changesets += tuple(opts.get('rev', []))
2111 2114
2112 2115 if bookmark and changesets:
2113 2116 raise error.Abort(_("-r and -B are mutually exclusive"))
2114 2117
2115 2118 if bookmark:
2116 2119 if bookmark not in repo._bookmarks:
2117 2120 raise error.Abort(_("bookmark '%s' not found") % bookmark)
2118 2121
2119 2122 revs = scmutil.bookmarkrevs(repo, bookmark)
2120 2123 else:
2121 2124 if not changesets:
2122 2125 changesets = ['.']
2123 2126
2124 2127 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
2125 2128 revs = scmutil.revrange(repo, changesets)
2126 2129
2127 2130 if not revs:
2128 2131 raise error.Abort(_("export requires at least one changeset"))
2129 2132 if len(revs) > 1:
2130 2133 ui.note(_('exporting patches:\n'))
2131 2134 else:
2132 2135 ui.note(_('exporting patch:\n'))
2133 2136
2134 2137 fntemplate = opts.get('output')
2135 2138 if cmdutil.isstdiofilename(fntemplate):
2136 2139 fntemplate = ''
2137 2140
2138 2141 if fntemplate:
2139 2142 fm = formatter.nullformatter(ui, 'export', opts)
2140 2143 else:
2141 2144 ui.pager('export')
2142 2145 fm = ui.formatter('export', opts)
2143 2146 with fm:
2144 2147 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
2145 2148 switch_parent=opts.get('switch_parent'),
2146 2149 opts=patch.diffallopts(ui, opts))
2147 2150
2148 2151 @command('files',
2149 2152 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2150 2153 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2151 2154 ] + walkopts + formatteropts + subrepoopts,
2152 2155 _('[OPTION]... [FILE]...'),
2153 2156 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2154 2157 intents={INTENT_READONLY})
2155 2158 def files(ui, repo, *pats, **opts):
2156 2159 """list tracked files
2157 2160
2158 2161 Print files under Mercurial control in the working directory or
2159 2162 specified revision for given files (excluding removed files).
2160 2163 Files can be specified as filenames or filesets.
2161 2164
2162 2165 If no files are given to match, this command prints the names
2163 2166 of all files under Mercurial control.
2164 2167
2165 2168 .. container:: verbose
2166 2169
2167 2170 Template:
2168 2171
2169 2172 The following keywords are supported in addition to the common template
2170 2173 keywords and functions. See also :hg:`help templates`.
2171 2174
2172 2175 :flags: String. Character denoting file's symlink and executable bits.
2173 2176 :path: String. Repository-absolute path of the file.
2174 2177 :size: Integer. Size of the file in bytes.
2175 2178
2176 2179 Examples:
2177 2180
2178 2181 - list all files under the current directory::
2179 2182
2180 2183 hg files .
2181 2184
2182 2185 - shows sizes and flags for current revision::
2183 2186
2184 2187 hg files -vr .
2185 2188
2186 2189 - list all files named README::
2187 2190
2188 2191 hg files -I "**/README"
2189 2192
2190 2193 - list all binary files::
2191 2194
2192 2195 hg files "set:binary()"
2193 2196
2194 2197 - find files containing a regular expression::
2195 2198
2196 2199 hg files "set:grep('bob')"
2197 2200
2198 2201 - search tracked file contents with xargs and grep::
2199 2202
2200 2203 hg files -0 | xargs -0 grep foo
2201 2204
2202 2205 See :hg:`help patterns` and :hg:`help filesets` for more information
2203 2206 on specifying file patterns.
2204 2207
2205 2208 Returns 0 if a match is found, 1 otherwise.
2206 2209
2207 2210 """
2208 2211
2209 2212 opts = pycompat.byteskwargs(opts)
2210 2213 rev = opts.get('rev')
2211 2214 if rev:
2212 2215 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2213 2216 ctx = scmutil.revsingle(repo, rev, None)
2214 2217
2215 2218 end = '\n'
2216 2219 if opts.get('print0'):
2217 2220 end = '\0'
2218 2221 fmt = '%s' + end
2219 2222
2220 2223 m = scmutil.match(ctx, pats, opts)
2221 2224 ui.pager('files')
2222 2225 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2223 2226 with ui.formatter('files', opts) as fm:
2224 2227 return cmdutil.files(ui, ctx, m, uipathfn, fm, fmt,
2225 2228 opts.get('subrepos'))
2226 2229
2227 2230 @command(
2228 2231 'forget',
2229 2232 [('i', 'interactive', None, _('use interactive mode')),
2230 2233 ] + walkopts + dryrunopts,
2231 2234 _('[OPTION]... FILE...'),
2232 2235 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2233 2236 helpbasic=True, inferrepo=True)
2234 2237 def forget(ui, repo, *pats, **opts):
2235 2238 """forget the specified files on the next commit
2236 2239
2237 2240 Mark the specified files so they will no longer be tracked
2238 2241 after the next commit.
2239 2242
2240 2243 This only removes files from the current branch, not from the
2241 2244 entire project history, and it does not delete them from the
2242 2245 working directory.
2243 2246
2244 2247 To delete the file from the working directory, see :hg:`remove`.
2245 2248
2246 2249 To undo a forget before the next commit, see :hg:`add`.
2247 2250
2248 2251 .. container:: verbose
2249 2252
2250 2253 Examples:
2251 2254
2252 2255 - forget newly-added binary files::
2253 2256
2254 2257 hg forget "set:added() and binary()"
2255 2258
2256 2259 - forget files that would be excluded by .hgignore::
2257 2260
2258 2261 hg forget "set:hgignore()"
2259 2262
2260 2263 Returns 0 on success.
2261 2264 """
2262 2265
2263 2266 opts = pycompat.byteskwargs(opts)
2264 2267 if not pats:
2265 2268 raise error.Abort(_('no files specified'))
2266 2269
2267 2270 m = scmutil.match(repo[None], pats, opts)
2268 2271 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2269 2272 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2270 2273 rejected = cmdutil.forget(ui, repo, m, prefix="", uipathfn=uipathfn,
2271 2274 explicitonly=False, dryrun=dryrun,
2272 2275 interactive=interactive)[0]
2273 2276 return rejected and 1 or 0
2274 2277
2275 2278 @command(
2276 2279 'graft',
2277 2280 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2278 2281 ('', 'base', '',
2279 2282 _('base revision when doing the graft merge (ADVANCED)'), _('REV')),
2280 2283 ('c', 'continue', False, _('resume interrupted graft')),
2281 2284 ('', 'stop', False, _('stop interrupted graft')),
2282 2285 ('', 'abort', False, _('abort interrupted graft')),
2283 2286 ('e', 'edit', False, _('invoke editor on commit messages')),
2284 2287 ('', 'log', None, _('append graft info to log message')),
2285 2288 ('', 'no-commit', None,
2286 2289 _("don't commit, just apply the changes in working directory")),
2287 2290 ('f', 'force', False, _('force graft')),
2288 2291 ('D', 'currentdate', False,
2289 2292 _('record the current date as commit date')),
2290 2293 ('U', 'currentuser', False,
2291 2294 _('record the current user as committer'))]
2292 2295 + commitopts2 + mergetoolopts + dryrunopts,
2293 2296 _('[OPTION]... [-r REV]... REV...'),
2294 2297 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
2295 2298 def graft(ui, repo, *revs, **opts):
2296 2299 '''copy changes from other branches onto the current branch
2297 2300
2298 2301 This command uses Mercurial's merge logic to copy individual
2299 2302 changes from other branches without merging branches in the
2300 2303 history graph. This is sometimes known as 'backporting' or
2301 2304 'cherry-picking'. By default, graft will copy user, date, and
2302 2305 description from the source changesets.
2303 2306
2304 2307 Changesets that are ancestors of the current revision, that have
2305 2308 already been grafted, or that are merges will be skipped.
2306 2309
2307 2310 If --log is specified, log messages will have a comment appended
2308 2311 of the form::
2309 2312
2310 2313 (grafted from CHANGESETHASH)
2311 2314
2312 2315 If --force is specified, revisions will be grafted even if they
2313 2316 are already ancestors of, or have been grafted to, the destination.
2314 2317 This is useful when the revisions have since been backed out.
2315 2318
2316 2319 If a graft merge results in conflicts, the graft process is
2317 2320 interrupted so that the current merge can be manually resolved.
2318 2321 Once all conflicts are addressed, the graft process can be
2319 2322 continued with the -c/--continue option.
2320 2323
2321 2324 The -c/--continue option reapplies all the earlier options.
2322 2325
2323 2326 .. container:: verbose
2324 2327
2325 2328 The --base option exposes more of how graft internally uses merge with a
2326 2329 custom base revision. --base can be used to specify another ancestor than
2327 2330 the first and only parent.
2328 2331
2329 2332 The command::
2330 2333
2331 2334 hg graft -r 345 --base 234
2332 2335
2333 2336 is thus pretty much the same as::
2334 2337
2335 2338 hg diff -r 234 -r 345 | hg import
2336 2339
2337 2340 but using merge to resolve conflicts and track moved files.
2338 2341
2339 2342 The result of a merge can thus be backported as a single commit by
2340 2343 specifying one of the merge parents as base, and thus effectively
2341 2344 grafting the changes from the other side.
2342 2345
2343 2346 It is also possible to collapse multiple changesets and clean up history
2344 2347 by specifying another ancestor as base, much like rebase --collapse
2345 2348 --keep.
2346 2349
2347 2350 The commit message can be tweaked after the fact using commit --amend .
2348 2351
2349 2352 For using non-ancestors as the base to backout changes, see the backout
2350 2353 command and the hidden --parent option.
2351 2354
2352 2355 .. container:: verbose
2353 2356
2354 2357 Examples:
2355 2358
2356 2359 - copy a single change to the stable branch and edit its description::
2357 2360
2358 2361 hg update stable
2359 2362 hg graft --edit 9393
2360 2363
2361 2364 - graft a range of changesets with one exception, updating dates::
2362 2365
2363 2366 hg graft -D "2085::2093 and not 2091"
2364 2367
2365 2368 - continue a graft after resolving conflicts::
2366 2369
2367 2370 hg graft -c
2368 2371
2369 2372 - show the source of a grafted changeset::
2370 2373
2371 2374 hg log --debug -r .
2372 2375
2373 2376 - show revisions sorted by date::
2374 2377
2375 2378 hg log -r "sort(all(), date)"
2376 2379
2377 2380 - backport the result of a merge as a single commit::
2378 2381
2379 2382 hg graft -r 123 --base 123^
2380 2383
2381 2384 - land a feature branch as one changeset::
2382 2385
2383 2386 hg up -cr default
2384 2387 hg graft -r featureX --base "ancestor('featureX', 'default')"
2385 2388
2386 2389 See :hg:`help revisions` for more about specifying revisions.
2387 2390
2388 2391 Returns 0 on successful completion.
2389 2392 '''
2390 2393 with repo.wlock():
2391 2394 return _dograft(ui, repo, *revs, **opts)
2392 2395
2393 2396 def _dograft(ui, repo, *revs, **opts):
2394 2397 opts = pycompat.byteskwargs(opts)
2395 2398 if revs and opts.get('rev'):
2396 2399 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2397 2400 'revision ordering!\n'))
2398 2401
2399 2402 revs = list(revs)
2400 2403 revs.extend(opts.get('rev'))
2401 2404 basectx = None
2402 2405 if opts.get('base'):
2403 2406 basectx = scmutil.revsingle(repo, opts['base'], None)
2404 2407 # a dict of data to be stored in state file
2405 2408 statedata = {}
2406 2409 # list of new nodes created by ongoing graft
2407 2410 statedata['newnodes'] = []
2408 2411
2409 2412 if opts.get('user') and opts.get('currentuser'):
2410 2413 raise error.Abort(_('--user and --currentuser are mutually exclusive'))
2411 2414 if opts.get('date') and opts.get('currentdate'):
2412 2415 raise error.Abort(_('--date and --currentdate are mutually exclusive'))
2413 2416 if not opts.get('user') and opts.get('currentuser'):
2414 2417 opts['user'] = ui.username()
2415 2418 if not opts.get('date') and opts.get('currentdate'):
2416 2419 opts['date'] = "%d %d" % dateutil.makedate()
2417 2420
2418 2421 editor = cmdutil.getcommiteditor(editform='graft',
2419 2422 **pycompat.strkwargs(opts))
2420 2423
2421 2424 cont = False
2422 2425 if opts.get('no_commit'):
2423 2426 if opts.get('edit'):
2424 2427 raise error.Abort(_("cannot specify --no-commit and "
2425 2428 "--edit together"))
2426 2429 if opts.get('currentuser'):
2427 2430 raise error.Abort(_("cannot specify --no-commit and "
2428 2431 "--currentuser together"))
2429 2432 if opts.get('currentdate'):
2430 2433 raise error.Abort(_("cannot specify --no-commit and "
2431 2434 "--currentdate together"))
2432 2435 if opts.get('log'):
2433 2436 raise error.Abort(_("cannot specify --no-commit and "
2434 2437 "--log together"))
2435 2438
2436 2439 graftstate = statemod.cmdstate(repo, 'graftstate')
2437 2440
2438 2441 if opts.get('stop'):
2439 2442 if opts.get('continue'):
2440 2443 raise error.Abort(_("cannot use '--continue' and "
2441 2444 "'--stop' together"))
2442 2445 if opts.get('abort'):
2443 2446 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2444 2447
2445 2448 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2446 2449 opts.get('date'), opts.get('currentdate'),
2447 2450 opts.get('currentuser'), opts.get('rev'))):
2448 2451 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2449 2452 return _stopgraft(ui, repo, graftstate)
2450 2453 elif opts.get('abort'):
2451 2454 if opts.get('continue'):
2452 2455 raise error.Abort(_("cannot use '--continue' and "
2453 2456 "'--abort' together"))
2454 2457 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2455 2458 opts.get('date'), opts.get('currentdate'),
2456 2459 opts.get('currentuser'), opts.get('rev'))):
2457 2460 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2458 2461
2459 2462 return _abortgraft(ui, repo, graftstate)
2460 2463 elif opts.get('continue'):
2461 2464 cont = True
2462 2465 if revs:
2463 2466 raise error.Abort(_("can't specify --continue and revisions"))
2464 2467 # read in unfinished revisions
2465 2468 if graftstate.exists():
2466 2469 statedata = _readgraftstate(repo, graftstate)
2467 2470 if statedata.get('date'):
2468 2471 opts['date'] = statedata['date']
2469 2472 if statedata.get('user'):
2470 2473 opts['user'] = statedata['user']
2471 2474 if statedata.get('log'):
2472 2475 opts['log'] = True
2473 2476 if statedata.get('no_commit'):
2474 2477 opts['no_commit'] = statedata.get('no_commit')
2475 2478 nodes = statedata['nodes']
2476 2479 revs = [repo[node].rev() for node in nodes]
2477 2480 else:
2478 2481 cmdutil.wrongtooltocontinue(repo, _('graft'))
2479 2482 else:
2480 2483 if not revs:
2481 2484 raise error.Abort(_('no revisions specified'))
2482 2485 cmdutil.checkunfinished(repo)
2483 2486 cmdutil.bailifchanged(repo)
2484 2487 revs = scmutil.revrange(repo, revs)
2485 2488
2486 2489 skipped = set()
2487 2490 if basectx is None:
2488 2491 # check for merges
2489 2492 for rev in repo.revs('%ld and merge()', revs):
2490 2493 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2491 2494 skipped.add(rev)
2492 2495 revs = [r for r in revs if r not in skipped]
2493 2496 if not revs:
2494 2497 return -1
2495 2498 if basectx is not None and len(revs) != 1:
2496 2499 raise error.Abort(_('only one revision allowed with --base '))
2497 2500
2498 2501 # Don't check in the --continue case, in effect retaining --force across
2499 2502 # --continues. That's because without --force, any revisions we decided to
2500 2503 # skip would have been filtered out here, so they wouldn't have made their
2501 2504 # way to the graftstate. With --force, any revisions we would have otherwise
2502 2505 # skipped would not have been filtered out, and if they hadn't been applied
2503 2506 # already, they'd have been in the graftstate.
2504 2507 if not (cont or opts.get('force')) and basectx is None:
2505 2508 # check for ancestors of dest branch
2506 2509 crev = repo['.'].rev()
2507 2510 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2508 2511 # XXX make this lazy in the future
2509 2512 # don't mutate while iterating, create a copy
2510 2513 for rev in list(revs):
2511 2514 if rev in ancestors:
2512 2515 ui.warn(_('skipping ancestor revision %d:%s\n') %
2513 2516 (rev, repo[rev]))
2514 2517 # XXX remove on list is slow
2515 2518 revs.remove(rev)
2516 2519 if not revs:
2517 2520 return -1
2518 2521
2519 2522 # analyze revs for earlier grafts
2520 2523 ids = {}
2521 2524 for ctx in repo.set("%ld", revs):
2522 2525 ids[ctx.hex()] = ctx.rev()
2523 2526 n = ctx.extra().get('source')
2524 2527 if n:
2525 2528 ids[n] = ctx.rev()
2526 2529
2527 2530 # check ancestors for earlier grafts
2528 2531 ui.debug('scanning for duplicate grafts\n')
2529 2532
2530 2533 # The only changesets we can be sure doesn't contain grafts of any
2531 2534 # revs, are the ones that are common ancestors of *all* revs:
2532 2535 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2533 2536 ctx = repo[rev]
2534 2537 n = ctx.extra().get('source')
2535 2538 if n in ids:
2536 2539 try:
2537 2540 r = repo[n].rev()
2538 2541 except error.RepoLookupError:
2539 2542 r = None
2540 2543 if r in revs:
2541 2544 ui.warn(_('skipping revision %d:%s '
2542 2545 '(already grafted to %d:%s)\n')
2543 2546 % (r, repo[r], rev, ctx))
2544 2547 revs.remove(r)
2545 2548 elif ids[n] in revs:
2546 2549 if r is None:
2547 2550 ui.warn(_('skipping already grafted revision %d:%s '
2548 2551 '(%d:%s also has unknown origin %s)\n')
2549 2552 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2550 2553 else:
2551 2554 ui.warn(_('skipping already grafted revision %d:%s '
2552 2555 '(%d:%s also has origin %d:%s)\n')
2553 2556 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2554 2557 revs.remove(ids[n])
2555 2558 elif ctx.hex() in ids:
2556 2559 r = ids[ctx.hex()]
2557 2560 if r in revs:
2558 2561 ui.warn(_('skipping already grafted revision %d:%s '
2559 2562 '(was grafted from %d:%s)\n') %
2560 2563 (r, repo[r], rev, ctx))
2561 2564 revs.remove(r)
2562 2565 if not revs:
2563 2566 return -1
2564 2567
2565 2568 if opts.get('no_commit'):
2566 2569 statedata['no_commit'] = True
2567 2570 for pos, ctx in enumerate(repo.set("%ld", revs)):
2568 2571 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2569 2572 ctx.description().split('\n', 1)[0])
2570 2573 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2571 2574 if names:
2572 2575 desc += ' (%s)' % ' '.join(names)
2573 2576 ui.status(_('grafting %s\n') % desc)
2574 2577 if opts.get('dry_run'):
2575 2578 continue
2576 2579
2577 2580 source = ctx.extra().get('source')
2578 2581 extra = {}
2579 2582 if source:
2580 2583 extra['source'] = source
2581 2584 extra['intermediate-source'] = ctx.hex()
2582 2585 else:
2583 2586 extra['source'] = ctx.hex()
2584 2587 user = ctx.user()
2585 2588 if opts.get('user'):
2586 2589 user = opts['user']
2587 2590 statedata['user'] = user
2588 2591 date = ctx.date()
2589 2592 if opts.get('date'):
2590 2593 date = opts['date']
2591 2594 statedata['date'] = date
2592 2595 message = ctx.description()
2593 2596 if opts.get('log'):
2594 2597 message += '\n(grafted from %s)' % ctx.hex()
2595 2598 statedata['log'] = True
2596 2599
2597 2600 # we don't merge the first commit when continuing
2598 2601 if not cont:
2599 2602 # perform the graft merge with p1(rev) as 'ancestor'
2600 2603 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2601 2604 base = ctx.p1() if basectx is None else basectx
2602 2605 with ui.configoverride(overrides, 'graft'):
2603 2606 stats = mergemod.graft(repo, ctx, base, ['local', 'graft'])
2604 2607 # report any conflicts
2605 2608 if stats.unresolvedcount > 0:
2606 2609 # write out state for --continue
2607 2610 nodes = [repo[rev].hex() for rev in revs[pos:]]
2608 2611 statedata['nodes'] = nodes
2609 2612 stateversion = 1
2610 2613 graftstate.save(stateversion, statedata)
2611 2614 hint = _("use 'hg resolve' and 'hg graft --continue'")
2612 2615 raise error.Abort(
2613 2616 _("unresolved conflicts, can't continue"),
2614 2617 hint=hint)
2615 2618 else:
2616 2619 cont = False
2617 2620
2618 2621 # commit if --no-commit is false
2619 2622 if not opts.get('no_commit'):
2620 2623 node = repo.commit(text=message, user=user, date=date, extra=extra,
2621 2624 editor=editor)
2622 2625 if node is None:
2623 2626 ui.warn(
2624 2627 _('note: graft of %d:%s created no changes to commit\n') %
2625 2628 (ctx.rev(), ctx))
2626 2629 # checking that newnodes exist because old state files won't have it
2627 2630 elif statedata.get('newnodes') is not None:
2628 2631 statedata['newnodes'].append(node)
2629 2632
2630 2633 # remove state when we complete successfully
2631 2634 if not opts.get('dry_run'):
2632 2635 graftstate.delete()
2633 2636
2634 2637 return 0
2635 2638
2636 2639 def _abortgraft(ui, repo, graftstate):
2637 2640 """abort the interrupted graft and rollbacks to the state before interrupted
2638 2641 graft"""
2639 2642 if not graftstate.exists():
2640 2643 raise error.Abort(_("no interrupted graft to abort"))
2641 2644 statedata = _readgraftstate(repo, graftstate)
2642 2645 newnodes = statedata.get('newnodes')
2643 2646 if newnodes is None:
2644 2647 # and old graft state which does not have all the data required to abort
2645 2648 # the graft
2646 2649 raise error.Abort(_("cannot abort using an old graftstate"))
2647 2650
2648 2651 # changeset from which graft operation was started
2649 2652 if len(newnodes) > 0:
2650 2653 startctx = repo[newnodes[0]].p1()
2651 2654 else:
2652 2655 startctx = repo['.']
2653 2656 # whether to strip or not
2654 2657 cleanup = False
2655 2658 if newnodes:
2656 2659 newnodes = [repo[r].rev() for r in newnodes]
2657 2660 cleanup = True
2658 2661 # checking that none of the newnodes turned public or is public
2659 2662 immutable = [c for c in newnodes if not repo[c].mutable()]
2660 2663 if immutable:
2661 2664 repo.ui.warn(_("cannot clean up public changesets %s\n")
2662 2665 % ', '.join(bytes(repo[r]) for r in immutable),
2663 2666 hint=_("see 'hg help phases' for details"))
2664 2667 cleanup = False
2665 2668
2666 2669 # checking that no new nodes are created on top of grafted revs
2667 2670 desc = set(repo.changelog.descendants(newnodes))
2668 2671 if desc - set(newnodes):
2669 2672 repo.ui.warn(_("new changesets detected on destination "
2670 2673 "branch, can't strip\n"))
2671 2674 cleanup = False
2672 2675
2673 2676 if cleanup:
2674 2677 with repo.wlock(), repo.lock():
2675 2678 hg.updaterepo(repo, startctx.node(), overwrite=True)
2676 2679 # stripping the new nodes created
2677 2680 strippoints = [c.node() for c in repo.set("roots(%ld)",
2678 2681 newnodes)]
2679 2682 repair.strip(repo.ui, repo, strippoints, backup=False)
2680 2683
2681 2684 if not cleanup:
2682 2685 # we don't update to the startnode if we can't strip
2683 2686 startctx = repo['.']
2684 2687 hg.updaterepo(repo, startctx.node(), overwrite=True)
2685 2688
2686 2689 ui.status(_("graft aborted\n"))
2687 2690 ui.status(_("working directory is now at %s\n") % startctx.hex()[:12])
2688 2691 graftstate.delete()
2689 2692 return 0
2690 2693
2691 2694 def _readgraftstate(repo, graftstate):
2692 2695 """read the graft state file and return a dict of the data stored in it"""
2693 2696 try:
2694 2697 return graftstate.read()
2695 2698 except error.CorruptedState:
2696 2699 nodes = repo.vfs.read('graftstate').splitlines()
2697 2700 return {'nodes': nodes}
2698 2701
2699 2702 def _stopgraft(ui, repo, graftstate):
2700 2703 """stop the interrupted graft"""
2701 2704 if not graftstate.exists():
2702 2705 raise error.Abort(_("no interrupted graft found"))
2703 2706 pctx = repo['.']
2704 2707 hg.updaterepo(repo, pctx.node(), overwrite=True)
2705 2708 graftstate.delete()
2706 2709 ui.status(_("stopped the interrupted graft\n"))
2707 2710 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2708 2711 return 0
2709 2712
2710 2713 @command('grep',
2711 2714 [('0', 'print0', None, _('end fields with NUL')),
2712 2715 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2713 2716 ('', 'diff', None, _('print all revisions when the term was introduced '
2714 2717 'or removed')),
2715 2718 ('a', 'text', None, _('treat all files as text')),
2716 2719 ('f', 'follow', None,
2717 2720 _('follow changeset history,'
2718 2721 ' or file history across copies and renames')),
2719 2722 ('i', 'ignore-case', None, _('ignore case when matching')),
2720 2723 ('l', 'files-with-matches', None,
2721 2724 _('print only filenames and revisions that match')),
2722 2725 ('n', 'line-number', None, _('print matching line numbers')),
2723 2726 ('r', 'rev', [],
2724 2727 _('only search files changed within revision range'), _('REV')),
2725 2728 ('', 'all-files', None,
2726 2729 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2727 2730 ('u', 'user', None, _('list the author (long with -v)')),
2728 2731 ('d', 'date', None, _('list the date (short with -q)')),
2729 2732 ] + formatteropts + walkopts,
2730 2733 _('[OPTION]... PATTERN [FILE]...'),
2731 2734 helpcategory=command.CATEGORY_FILE_CONTENTS,
2732 2735 inferrepo=True,
2733 2736 intents={INTENT_READONLY})
2734 2737 def grep(ui, repo, pattern, *pats, **opts):
2735 2738 """search revision history for a pattern in specified files
2736 2739
2737 2740 Search revision history for a regular expression in the specified
2738 2741 files or the entire project.
2739 2742
2740 2743 By default, grep prints the most recent revision number for each
2741 2744 file in which it finds a match. To get it to print every revision
2742 2745 that contains a change in match status ("-" for a match that becomes
2743 2746 a non-match, or "+" for a non-match that becomes a match), use the
2744 2747 --diff flag.
2745 2748
2746 2749 PATTERN can be any Python (roughly Perl-compatible) regular
2747 2750 expression.
2748 2751
2749 2752 If no FILEs are specified (and -f/--follow isn't set), all files in
2750 2753 the repository are searched, including those that don't exist in the
2751 2754 current branch or have been deleted in a prior changeset.
2752 2755
2753 2756 .. container:: verbose
2754 2757
2755 2758 Template:
2756 2759
2757 2760 The following keywords are supported in addition to the common template
2758 2761 keywords and functions. See also :hg:`help templates`.
2759 2762
2760 2763 :change: String. Character denoting insertion ``+`` or removal ``-``.
2761 2764 Available if ``--diff`` is specified.
2762 2765 :lineno: Integer. Line number of the match.
2763 2766 :path: String. Repository-absolute path of the file.
2764 2767 :texts: List of text chunks.
2765 2768
2766 2769 And each entry of ``{texts}`` provides the following sub-keywords.
2767 2770
2768 2771 :matched: Boolean. True if the chunk matches the specified pattern.
2769 2772 :text: String. Chunk content.
2770 2773
2771 2774 See :hg:`help templates.operators` for the list expansion syntax.
2772 2775
2773 2776 Returns 0 if a match is found, 1 otherwise.
2774 2777 """
2775 2778 opts = pycompat.byteskwargs(opts)
2776 2779 diff = opts.get('all') or opts.get('diff')
2777 2780 all_files = opts.get('all_files')
2778 2781 if diff and opts.get('all_files'):
2779 2782 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2780 2783 # TODO: remove "not opts.get('rev')" if --all-files -rMULTIREV gets working
2781 2784 if opts.get('all_files') is None and not opts.get('rev') and not diff:
2782 2785 # experimental config: commands.grep.all-files
2783 2786 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2784 2787 plaingrep = opts.get('all_files') and not opts.get('rev')
2785 2788 if plaingrep:
2786 2789 opts['rev'] = ['wdir()']
2787 2790
2788 2791 reflags = re.M
2789 2792 if opts.get('ignore_case'):
2790 2793 reflags |= re.I
2791 2794 try:
2792 2795 regexp = util.re.compile(pattern, reflags)
2793 2796 except re.error as inst:
2794 2797 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2795 2798 return 1
2796 2799 sep, eol = ':', '\n'
2797 2800 if opts.get('print0'):
2798 2801 sep = eol = '\0'
2799 2802
2800 2803 getfile = util.lrucachefunc(repo.file)
2801 2804
2802 2805 def matchlines(body):
2803 2806 begin = 0
2804 2807 linenum = 0
2805 2808 while begin < len(body):
2806 2809 match = regexp.search(body, begin)
2807 2810 if not match:
2808 2811 break
2809 2812 mstart, mend = match.span()
2810 2813 linenum += body.count('\n', begin, mstart) + 1
2811 2814 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2812 2815 begin = body.find('\n', mend) + 1 or len(body) + 1
2813 2816 lend = begin - 1
2814 2817 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2815 2818
2816 2819 class linestate(object):
2817 2820 def __init__(self, line, linenum, colstart, colend):
2818 2821 self.line = line
2819 2822 self.linenum = linenum
2820 2823 self.colstart = colstart
2821 2824 self.colend = colend
2822 2825
2823 2826 def __hash__(self):
2824 2827 return hash((self.linenum, self.line))
2825 2828
2826 2829 def __eq__(self, other):
2827 2830 return self.line == other.line
2828 2831
2829 2832 def findpos(self):
2830 2833 """Iterate all (start, end) indices of matches"""
2831 2834 yield self.colstart, self.colend
2832 2835 p = self.colend
2833 2836 while p < len(self.line):
2834 2837 m = regexp.search(self.line, p)
2835 2838 if not m:
2836 2839 break
2837 2840 yield m.span()
2838 2841 p = m.end()
2839 2842
2840 2843 matches = {}
2841 2844 copies = {}
2842 2845 def grepbody(fn, rev, body):
2843 2846 matches[rev].setdefault(fn, [])
2844 2847 m = matches[rev][fn]
2845 2848 for lnum, cstart, cend, line in matchlines(body):
2846 2849 s = linestate(line, lnum, cstart, cend)
2847 2850 m.append(s)
2848 2851
2849 2852 def difflinestates(a, b):
2850 2853 sm = difflib.SequenceMatcher(None, a, b)
2851 2854 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2852 2855 if tag == r'insert':
2853 2856 for i in pycompat.xrange(blo, bhi):
2854 2857 yield ('+', b[i])
2855 2858 elif tag == r'delete':
2856 2859 for i in pycompat.xrange(alo, ahi):
2857 2860 yield ('-', a[i])
2858 2861 elif tag == r'replace':
2859 2862 for i in pycompat.xrange(alo, ahi):
2860 2863 yield ('-', a[i])
2861 2864 for i in pycompat.xrange(blo, bhi):
2862 2865 yield ('+', b[i])
2863 2866
2864 2867 uipathfn = scmutil.getuipathfn(repo)
2865 2868 def display(fm, fn, ctx, pstates, states):
2866 2869 rev = scmutil.intrev(ctx)
2867 2870 if fm.isplain():
2868 2871 formatuser = ui.shortuser
2869 2872 else:
2870 2873 formatuser = pycompat.bytestr
2871 2874 if ui.quiet:
2872 2875 datefmt = '%Y-%m-%d'
2873 2876 else:
2874 2877 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2875 2878 found = False
2876 2879 @util.cachefunc
2877 2880 def binary():
2878 2881 flog = getfile(fn)
2879 2882 try:
2880 2883 return stringutil.binary(flog.read(ctx.filenode(fn)))
2881 2884 except error.WdirUnsupported:
2882 2885 return ctx[fn].isbinary()
2883 2886
2884 2887 fieldnamemap = {'linenumber': 'lineno'}
2885 2888 if diff:
2886 2889 iter = difflinestates(pstates, states)
2887 2890 else:
2888 2891 iter = [('', l) for l in states]
2889 2892 for change, l in iter:
2890 2893 fm.startitem()
2891 2894 fm.context(ctx=ctx)
2892 2895 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
2893 2896 fm.plain(uipathfn(fn), label='grep.filename')
2894 2897
2895 2898 cols = [
2896 2899 ('rev', '%d', rev, not plaingrep, ''),
2897 2900 ('linenumber', '%d', l.linenum, opts.get('line_number'), ''),
2898 2901 ]
2899 2902 if diff:
2900 2903 cols.append(
2901 2904 ('change', '%s', change, True,
2902 2905 'grep.inserted ' if change == '+' else 'grep.deleted ')
2903 2906 )
2904 2907 cols.extend([
2905 2908 ('user', '%s', formatuser(ctx.user()), opts.get('user'), ''),
2906 2909 ('date', '%s', fm.formatdate(ctx.date(), datefmt),
2907 2910 opts.get('date'), ''),
2908 2911 ])
2909 2912 for name, fmt, data, cond, extra_label in cols:
2910 2913 if cond:
2911 2914 fm.plain(sep, label='grep.sep')
2912 2915 field = fieldnamemap.get(name, name)
2913 2916 label = extra_label + ('grep.%s' % name)
2914 2917 fm.condwrite(cond, field, fmt, data, label=label)
2915 2918 if not opts.get('files_with_matches'):
2916 2919 fm.plain(sep, label='grep.sep')
2917 2920 if not opts.get('text') and binary():
2918 2921 fm.plain(_(" Binary file matches"))
2919 2922 else:
2920 2923 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2921 2924 fm.plain(eol)
2922 2925 found = True
2923 2926 if opts.get('files_with_matches'):
2924 2927 break
2925 2928 return found
2926 2929
2927 2930 def displaymatches(fm, l):
2928 2931 p = 0
2929 2932 for s, e in l.findpos():
2930 2933 if p < s:
2931 2934 fm.startitem()
2932 2935 fm.write('text', '%s', l.line[p:s])
2933 2936 fm.data(matched=False)
2934 2937 fm.startitem()
2935 2938 fm.write('text', '%s', l.line[s:e], label='grep.match')
2936 2939 fm.data(matched=True)
2937 2940 p = e
2938 2941 if p < len(l.line):
2939 2942 fm.startitem()
2940 2943 fm.write('text', '%s', l.line[p:])
2941 2944 fm.data(matched=False)
2942 2945 fm.end()
2943 2946
2944 2947 skip = set()
2945 2948 revfiles = {}
2946 2949 match = scmutil.match(repo[None], pats, opts)
2947 2950 found = False
2948 2951 follow = opts.get('follow')
2949 2952
2950 2953 getrenamed = scmutil.getrenamedfn(repo)
2951 2954 def prep(ctx, fns):
2952 2955 rev = ctx.rev()
2953 2956 pctx = ctx.p1()
2954 2957 parent = pctx.rev()
2955 2958 matches.setdefault(rev, {})
2956 2959 matches.setdefault(parent, {})
2957 2960 files = revfiles.setdefault(rev, [])
2958 2961 for fn in fns:
2959 2962 flog = getfile(fn)
2960 2963 try:
2961 2964 fnode = ctx.filenode(fn)
2962 2965 except error.LookupError:
2963 2966 continue
2964 2967
2965 2968 copy = None
2966 2969 if follow:
2967 2970 copy = getrenamed(fn, rev)
2968 2971 if copy:
2969 2972 copies.setdefault(rev, {})[fn] = copy
2970 2973 if fn in skip:
2971 2974 skip.add(copy)
2972 2975 if fn in skip:
2973 2976 continue
2974 2977 files.append(fn)
2975 2978
2976 2979 if fn not in matches[rev]:
2977 2980 try:
2978 2981 content = flog.read(fnode)
2979 2982 except error.WdirUnsupported:
2980 2983 content = ctx[fn].data()
2981 2984 grepbody(fn, rev, content)
2982 2985
2983 2986 pfn = copy or fn
2984 2987 if pfn not in matches[parent]:
2985 2988 try:
2986 2989 fnode = pctx.filenode(pfn)
2987 2990 grepbody(pfn, parent, flog.read(fnode))
2988 2991 except error.LookupError:
2989 2992 pass
2990 2993
2991 2994 ui.pager('grep')
2992 2995 fm = ui.formatter('grep', opts)
2993 2996 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2994 2997 rev = ctx.rev()
2995 2998 parent = ctx.p1().rev()
2996 2999 for fn in sorted(revfiles.get(rev, [])):
2997 3000 states = matches[rev][fn]
2998 3001 copy = copies.get(rev, {}).get(fn)
2999 3002 if fn in skip:
3000 3003 if copy:
3001 3004 skip.add(copy)
3002 3005 continue
3003 3006 pstates = matches.get(parent, {}).get(copy or fn, [])
3004 3007 if pstates or states:
3005 3008 r = display(fm, fn, ctx, pstates, states)
3006 3009 found = found or r
3007 3010 if r and not diff and not all_files:
3008 3011 skip.add(fn)
3009 3012 if copy:
3010 3013 skip.add(copy)
3011 3014 del revfiles[rev]
3012 3015 # We will keep the matches dict for the duration of the window
3013 3016 # clear the matches dict once the window is over
3014 3017 if not revfiles:
3015 3018 matches.clear()
3016 3019 fm.end()
3017 3020
3018 3021 return not found
3019 3022
3020 3023 @command('heads',
3021 3024 [('r', 'rev', '',
3022 3025 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3023 3026 ('t', 'topo', False, _('show topological heads only')),
3024 3027 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3025 3028 ('c', 'closed', False, _('show normal and closed branch heads')),
3026 3029 ] + templateopts,
3027 3030 _('[-ct] [-r STARTREV] [REV]...'),
3028 3031 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3029 3032 intents={INTENT_READONLY})
3030 3033 def heads(ui, repo, *branchrevs, **opts):
3031 3034 """show branch heads
3032 3035
3033 3036 With no arguments, show all open branch heads in the repository.
3034 3037 Branch heads are changesets that have no descendants on the
3035 3038 same branch. They are where development generally takes place and
3036 3039 are the usual targets for update and merge operations.
3037 3040
3038 3041 If one or more REVs are given, only open branch heads on the
3039 3042 branches associated with the specified changesets are shown. This
3040 3043 means that you can use :hg:`heads .` to see the heads on the
3041 3044 currently checked-out branch.
3042 3045
3043 3046 If -c/--closed is specified, also show branch heads marked closed
3044 3047 (see :hg:`commit --close-branch`).
3045 3048
3046 3049 If STARTREV is specified, only those heads that are descendants of
3047 3050 STARTREV will be displayed.
3048 3051
3049 3052 If -t/--topo is specified, named branch mechanics will be ignored and only
3050 3053 topological heads (changesets with no children) will be shown.
3051 3054
3052 3055 Returns 0 if matching heads are found, 1 if not.
3053 3056 """
3054 3057
3055 3058 opts = pycompat.byteskwargs(opts)
3056 3059 start = None
3057 3060 rev = opts.get('rev')
3058 3061 if rev:
3059 3062 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3060 3063 start = scmutil.revsingle(repo, rev, None).node()
3061 3064
3062 3065 if opts.get('topo'):
3063 3066 heads = [repo[h] for h in repo.heads(start)]
3064 3067 else:
3065 3068 heads = []
3066 3069 for branch in repo.branchmap():
3067 3070 heads += repo.branchheads(branch, start, opts.get('closed'))
3068 3071 heads = [repo[h] for h in heads]
3069 3072
3070 3073 if branchrevs:
3071 3074 branches = set(repo[r].branch()
3072 3075 for r in scmutil.revrange(repo, branchrevs))
3073 3076 heads = [h for h in heads if h.branch() in branches]
3074 3077
3075 3078 if opts.get('active') and branchrevs:
3076 3079 dagheads = repo.heads(start)
3077 3080 heads = [h for h in heads if h.node() in dagheads]
3078 3081
3079 3082 if branchrevs:
3080 3083 haveheads = set(h.branch() for h in heads)
3081 3084 if branches - haveheads:
3082 3085 headless = ', '.join(b for b in branches - haveheads)
3083 3086 msg = _('no open branch heads found on branches %s')
3084 3087 if opts.get('rev'):
3085 3088 msg += _(' (started at %s)') % opts['rev']
3086 3089 ui.warn((msg + '\n') % headless)
3087 3090
3088 3091 if not heads:
3089 3092 return 1
3090 3093
3091 3094 ui.pager('heads')
3092 3095 heads = sorted(heads, key=lambda x: -x.rev())
3093 3096 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3094 3097 for ctx in heads:
3095 3098 displayer.show(ctx)
3096 3099 displayer.close()
3097 3100
3098 3101 @command('help',
3099 3102 [('e', 'extension', None, _('show only help for extensions')),
3100 3103 ('c', 'command', None, _('show only help for commands')),
3101 3104 ('k', 'keyword', None, _('show topics matching keyword')),
3102 3105 ('s', 'system', [],
3103 3106 _('show help for specific platform(s)'), _('PLATFORM')),
3104 3107 ],
3105 3108 _('[-eck] [-s PLATFORM] [TOPIC]'),
3106 3109 helpcategory=command.CATEGORY_HELP,
3107 3110 norepo=True,
3108 3111 intents={INTENT_READONLY})
3109 3112 def help_(ui, name=None, **opts):
3110 3113 """show help for a given topic or a help overview
3111 3114
3112 3115 With no arguments, print a list of commands with short help messages.
3113 3116
3114 3117 Given a topic, extension, or command name, print help for that
3115 3118 topic.
3116 3119
3117 3120 Returns 0 if successful.
3118 3121 """
3119 3122
3120 3123 keep = opts.get(r'system') or []
3121 3124 if len(keep) == 0:
3122 3125 if pycompat.sysplatform.startswith('win'):
3123 3126 keep.append('windows')
3124 3127 elif pycompat.sysplatform == 'OpenVMS':
3125 3128 keep.append('vms')
3126 3129 elif pycompat.sysplatform == 'plan9':
3127 3130 keep.append('plan9')
3128 3131 else:
3129 3132 keep.append('unix')
3130 3133 keep.append(pycompat.sysplatform.lower())
3131 3134 if ui.verbose:
3132 3135 keep.append('verbose')
3133 3136
3134 3137 commands = sys.modules[__name__]
3135 3138 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3136 3139 ui.pager('help')
3137 3140 ui.write(formatted)
3138 3141
3139 3142
3140 3143 @command('identify|id',
3141 3144 [('r', 'rev', '',
3142 3145 _('identify the specified revision'), _('REV')),
3143 3146 ('n', 'num', None, _('show local revision number')),
3144 3147 ('i', 'id', None, _('show global revision id')),
3145 3148 ('b', 'branch', None, _('show branch')),
3146 3149 ('t', 'tags', None, _('show tags')),
3147 3150 ('B', 'bookmarks', None, _('show bookmarks')),
3148 3151 ] + remoteopts + formatteropts,
3149 3152 _('[-nibtB] [-r REV] [SOURCE]'),
3150 3153 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3151 3154 optionalrepo=True,
3152 3155 intents={INTENT_READONLY})
3153 3156 def identify(ui, repo, source=None, rev=None,
3154 3157 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3155 3158 """identify the working directory or specified revision
3156 3159
3157 3160 Print a summary identifying the repository state at REV using one or
3158 3161 two parent hash identifiers, followed by a "+" if the working
3159 3162 directory has uncommitted changes, the branch name (if not default),
3160 3163 a list of tags, and a list of bookmarks.
3161 3164
3162 3165 When REV is not given, print a summary of the current state of the
3163 3166 repository including the working directory. Specify -r. to get information
3164 3167 of the working directory parent without scanning uncommitted changes.
3165 3168
3166 3169 Specifying a path to a repository root or Mercurial bundle will
3167 3170 cause lookup to operate on that repository/bundle.
3168 3171
3169 3172 .. container:: verbose
3170 3173
3171 3174 Template:
3172 3175
3173 3176 The following keywords are supported in addition to the common template
3174 3177 keywords and functions. See also :hg:`help templates`.
3175 3178
3176 3179 :dirty: String. Character ``+`` denoting if the working directory has
3177 3180 uncommitted changes.
3178 3181 :id: String. One or two nodes, optionally followed by ``+``.
3179 3182 :parents: List of strings. Parent nodes of the changeset.
3180 3183
3181 3184 Examples:
3182 3185
3183 3186 - generate a build identifier for the working directory::
3184 3187
3185 3188 hg id --id > build-id.dat
3186 3189
3187 3190 - find the revision corresponding to a tag::
3188 3191
3189 3192 hg id -n -r 1.3
3190 3193
3191 3194 - check the most recent revision of a remote repository::
3192 3195
3193 3196 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3194 3197
3195 3198 See :hg:`log` for generating more information about specific revisions,
3196 3199 including full hash identifiers.
3197 3200
3198 3201 Returns 0 if successful.
3199 3202 """
3200 3203
3201 3204 opts = pycompat.byteskwargs(opts)
3202 3205 if not repo and not source:
3203 3206 raise error.Abort(_("there is no Mercurial repository here "
3204 3207 "(.hg not found)"))
3205 3208
3206 3209 default = not (num or id or branch or tags or bookmarks)
3207 3210 output = []
3208 3211 revs = []
3209 3212
3210 3213 if source:
3211 3214 source, branches = hg.parseurl(ui.expandpath(source))
3212 3215 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3213 3216 repo = peer.local()
3214 3217 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3215 3218
3216 3219 fm = ui.formatter('identify', opts)
3217 3220 fm.startitem()
3218 3221
3219 3222 if not repo:
3220 3223 if num or branch or tags:
3221 3224 raise error.Abort(
3222 3225 _("can't query remote revision number, branch, or tags"))
3223 3226 if not rev and revs:
3224 3227 rev = revs[0]
3225 3228 if not rev:
3226 3229 rev = "tip"
3227 3230
3228 3231 remoterev = peer.lookup(rev)
3229 3232 hexrev = fm.hexfunc(remoterev)
3230 3233 if default or id:
3231 3234 output = [hexrev]
3232 3235 fm.data(id=hexrev)
3233 3236
3234 3237 @util.cachefunc
3235 3238 def getbms():
3236 3239 bms = []
3237 3240
3238 3241 if 'bookmarks' in peer.listkeys('namespaces'):
3239 3242 hexremoterev = hex(remoterev)
3240 3243 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3241 3244 if bmr == hexremoterev]
3242 3245
3243 3246 return sorted(bms)
3244 3247
3245 3248 if fm.isplain():
3246 3249 if bookmarks:
3247 3250 output.extend(getbms())
3248 3251 elif default and not ui.quiet:
3249 3252 # multiple bookmarks for a single parent separated by '/'
3250 3253 bm = '/'.join(getbms())
3251 3254 if bm:
3252 3255 output.append(bm)
3253 3256 else:
3254 3257 fm.data(node=hex(remoterev))
3255 3258 if bookmarks or 'bookmarks' in fm.datahint():
3256 3259 fm.data(bookmarks=fm.formatlist(getbms(), name='bookmark'))
3257 3260 else:
3258 3261 if rev:
3259 3262 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3260 3263 ctx = scmutil.revsingle(repo, rev, None)
3261 3264
3262 3265 if ctx.rev() is None:
3263 3266 ctx = repo[None]
3264 3267 parents = ctx.parents()
3265 3268 taglist = []
3266 3269 for p in parents:
3267 3270 taglist.extend(p.tags())
3268 3271
3269 3272 dirty = ""
3270 3273 if ctx.dirty(missing=True, merge=False, branch=False):
3271 3274 dirty = '+'
3272 3275 fm.data(dirty=dirty)
3273 3276
3274 3277 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3275 3278 if default or id:
3276 3279 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3277 3280 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3278 3281
3279 3282 if num:
3280 3283 numoutput = ["%d" % p.rev() for p in parents]
3281 3284 output.append("%s%s" % ('+'.join(numoutput), dirty))
3282 3285
3283 3286 fm.data(parents=fm.formatlist([fm.hexfunc(p.node())
3284 3287 for p in parents], name='node'))
3285 3288 else:
3286 3289 hexoutput = fm.hexfunc(ctx.node())
3287 3290 if default or id:
3288 3291 output = [hexoutput]
3289 3292 fm.data(id=hexoutput)
3290 3293
3291 3294 if num:
3292 3295 output.append(pycompat.bytestr(ctx.rev()))
3293 3296 taglist = ctx.tags()
3294 3297
3295 3298 if default and not ui.quiet:
3296 3299 b = ctx.branch()
3297 3300 if b != 'default':
3298 3301 output.append("(%s)" % b)
3299 3302
3300 3303 # multiple tags for a single parent separated by '/'
3301 3304 t = '/'.join(taglist)
3302 3305 if t:
3303 3306 output.append(t)
3304 3307
3305 3308 # multiple bookmarks for a single parent separated by '/'
3306 3309 bm = '/'.join(ctx.bookmarks())
3307 3310 if bm:
3308 3311 output.append(bm)
3309 3312 else:
3310 3313 if branch:
3311 3314 output.append(ctx.branch())
3312 3315
3313 3316 if tags:
3314 3317 output.extend(taglist)
3315 3318
3316 3319 if bookmarks:
3317 3320 output.extend(ctx.bookmarks())
3318 3321
3319 3322 fm.data(node=ctx.hex())
3320 3323 fm.data(branch=ctx.branch())
3321 3324 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3322 3325 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3323 3326 fm.context(ctx=ctx)
3324 3327
3325 3328 fm.plain("%s\n" % ' '.join(output))
3326 3329 fm.end()
3327 3330
3328 3331 @command('import|patch',
3329 3332 [('p', 'strip', 1,
3330 3333 _('directory strip option for patch. This has the same '
3331 3334 'meaning as the corresponding patch option'), _('NUM')),
3332 3335 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3333 3336 ('e', 'edit', False, _('invoke editor on commit messages')),
3334 3337 ('f', 'force', None,
3335 3338 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3336 3339 ('', 'no-commit', None,
3337 3340 _("don't commit, just update the working directory")),
3338 3341 ('', 'bypass', None,
3339 3342 _("apply patch without touching the working directory")),
3340 3343 ('', 'partial', None,
3341 3344 _('commit even if some hunks fail')),
3342 3345 ('', 'exact', None,
3343 3346 _('abort if patch would apply lossily')),
3344 3347 ('', 'prefix', '',
3345 3348 _('apply patch to subdirectory'), _('DIR')),
3346 3349 ('', 'import-branch', None,
3347 3350 _('use any branch information in patch (implied by --exact)'))] +
3348 3351 commitopts + commitopts2 + similarityopts,
3349 3352 _('[OPTION]... PATCH...'),
3350 3353 helpcategory=command.CATEGORY_IMPORT_EXPORT)
3351 3354 def import_(ui, repo, patch1=None, *patches, **opts):
3352 3355 """import an ordered set of patches
3353 3356
3354 3357 Import a list of patches and commit them individually (unless
3355 3358 --no-commit is specified).
3356 3359
3357 3360 To read a patch from standard input (stdin), use "-" as the patch
3358 3361 name. If a URL is specified, the patch will be downloaded from
3359 3362 there.
3360 3363
3361 3364 Import first applies changes to the working directory (unless
3362 3365 --bypass is specified), import will abort if there are outstanding
3363 3366 changes.
3364 3367
3365 3368 Use --bypass to apply and commit patches directly to the
3366 3369 repository, without affecting the working directory. Without
3367 3370 --exact, patches will be applied on top of the working directory
3368 3371 parent revision.
3369 3372
3370 3373 You can import a patch straight from a mail message. Even patches
3371 3374 as attachments work (to use the body part, it must have type
3372 3375 text/plain or text/x-patch). From and Subject headers of email
3373 3376 message are used as default committer and commit message. All
3374 3377 text/plain body parts before first diff are added to the commit
3375 3378 message.
3376 3379
3377 3380 If the imported patch was generated by :hg:`export`, user and
3378 3381 description from patch override values from message headers and
3379 3382 body. Values given on command line with -m/--message and -u/--user
3380 3383 override these.
3381 3384
3382 3385 If --exact is specified, import will set the working directory to
3383 3386 the parent of each patch before applying it, and will abort if the
3384 3387 resulting changeset has a different ID than the one recorded in
3385 3388 the patch. This will guard against various ways that portable
3386 3389 patch formats and mail systems might fail to transfer Mercurial
3387 3390 data or metadata. See :hg:`bundle` for lossless transmission.
3388 3391
3389 3392 Use --partial to ensure a changeset will be created from the patch
3390 3393 even if some hunks fail to apply. Hunks that fail to apply will be
3391 3394 written to a <target-file>.rej file. Conflicts can then be resolved
3392 3395 by hand before :hg:`commit --amend` is run to update the created
3393 3396 changeset. This flag exists to let people import patches that
3394 3397 partially apply without losing the associated metadata (author,
3395 3398 date, description, ...).
3396 3399
3397 3400 .. note::
3398 3401
3399 3402 When no hunks apply cleanly, :hg:`import --partial` will create
3400 3403 an empty changeset, importing only the patch metadata.
3401 3404
3402 3405 With -s/--similarity, hg will attempt to discover renames and
3403 3406 copies in the patch in the same way as :hg:`addremove`.
3404 3407
3405 3408 It is possible to use external patch programs to perform the patch
3406 3409 by setting the ``ui.patch`` configuration option. For the default
3407 3410 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3408 3411 See :hg:`help config` for more information about configuration
3409 3412 files and how to use these options.
3410 3413
3411 3414 See :hg:`help dates` for a list of formats valid for -d/--date.
3412 3415
3413 3416 .. container:: verbose
3414 3417
3415 3418 Examples:
3416 3419
3417 3420 - import a traditional patch from a website and detect renames::
3418 3421
3419 3422 hg import -s 80 http://example.com/bugfix.patch
3420 3423
3421 3424 - import a changeset from an hgweb server::
3422 3425
3423 3426 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3424 3427
3425 3428 - import all the patches in an Unix-style mbox::
3426 3429
3427 3430 hg import incoming-patches.mbox
3428 3431
3429 3432 - import patches from stdin::
3430 3433
3431 3434 hg import -
3432 3435
3433 3436 - attempt to exactly restore an exported changeset (not always
3434 3437 possible)::
3435 3438
3436 3439 hg import --exact proposed-fix.patch
3437 3440
3438 3441 - use an external tool to apply a patch which is too fuzzy for
3439 3442 the default internal tool.
3440 3443
3441 3444 hg import --config ui.patch="patch --merge" fuzzy.patch
3442 3445
3443 3446 - change the default fuzzing from 2 to a less strict 7
3444 3447
3445 3448 hg import --config ui.fuzz=7 fuzz.patch
3446 3449
3447 3450 Returns 0 on success, 1 on partial success (see --partial).
3448 3451 """
3449 3452
3450 3453 opts = pycompat.byteskwargs(opts)
3451 3454 if not patch1:
3452 3455 raise error.Abort(_('need at least one patch to import'))
3453 3456
3454 3457 patches = (patch1,) + patches
3455 3458
3456 3459 date = opts.get('date')
3457 3460 if date:
3458 3461 opts['date'] = dateutil.parsedate(date)
3459 3462
3460 3463 exact = opts.get('exact')
3461 3464 update = not opts.get('bypass')
3462 3465 if not update and opts.get('no_commit'):
3463 3466 raise error.Abort(_('cannot use --no-commit with --bypass'))
3464 3467 try:
3465 3468 sim = float(opts.get('similarity') or 0)
3466 3469 except ValueError:
3467 3470 raise error.Abort(_('similarity must be a number'))
3468 3471 if sim < 0 or sim > 100:
3469 3472 raise error.Abort(_('similarity must be between 0 and 100'))
3470 3473 if sim and not update:
3471 3474 raise error.Abort(_('cannot use --similarity with --bypass'))
3472 3475 if exact:
3473 3476 if opts.get('edit'):
3474 3477 raise error.Abort(_('cannot use --exact with --edit'))
3475 3478 if opts.get('prefix'):
3476 3479 raise error.Abort(_('cannot use --exact with --prefix'))
3477 3480
3478 3481 base = opts["base"]
3479 3482 msgs = []
3480 3483 ret = 0
3481 3484
3482 3485 with repo.wlock():
3483 3486 if update:
3484 3487 cmdutil.checkunfinished(repo)
3485 3488 if (exact or not opts.get('force')):
3486 3489 cmdutil.bailifchanged(repo)
3487 3490
3488 3491 if not opts.get('no_commit'):
3489 3492 lock = repo.lock
3490 3493 tr = lambda: repo.transaction('import')
3491 3494 dsguard = util.nullcontextmanager
3492 3495 else:
3493 3496 lock = util.nullcontextmanager
3494 3497 tr = util.nullcontextmanager
3495 3498 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3496 3499 with lock(), tr(), dsguard():
3497 3500 parents = repo[None].parents()
3498 3501 for patchurl in patches:
3499 3502 if patchurl == '-':
3500 3503 ui.status(_('applying patch from stdin\n'))
3501 3504 patchfile = ui.fin
3502 3505 patchurl = 'stdin' # for error message
3503 3506 else:
3504 3507 patchurl = os.path.join(base, patchurl)
3505 3508 ui.status(_('applying %s\n') % patchurl)
3506 3509 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
3507 3510
3508 3511 haspatch = False
3509 3512 for hunk in patch.split(patchfile):
3510 3513 with patch.extract(ui, hunk) as patchdata:
3511 3514 msg, node, rej = cmdutil.tryimportone(ui, repo,
3512 3515 patchdata,
3513 3516 parents, opts,
3514 3517 msgs, hg.clean)
3515 3518 if msg:
3516 3519 haspatch = True
3517 3520 ui.note(msg + '\n')
3518 3521 if update or exact:
3519 3522 parents = repo[None].parents()
3520 3523 else:
3521 3524 parents = [repo[node]]
3522 3525 if rej:
3523 3526 ui.write_err(_("patch applied partially\n"))
3524 3527 ui.write_err(_("(fix the .rej files and run "
3525 3528 "`hg commit --amend`)\n"))
3526 3529 ret = 1
3527 3530 break
3528 3531
3529 3532 if not haspatch:
3530 3533 raise error.Abort(_('%s: no diffs found') % patchurl)
3531 3534
3532 3535 if msgs:
3533 3536 repo.savecommitmessage('\n* * *\n'.join(msgs))
3534 3537 return ret
3535 3538
3536 3539 @command('incoming|in',
3537 3540 [('f', 'force', None,
3538 3541 _('run even if remote repository is unrelated')),
3539 3542 ('n', 'newest-first', None, _('show newest record first')),
3540 3543 ('', 'bundle', '',
3541 3544 _('file to store the bundles into'), _('FILE')),
3542 3545 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3543 3546 ('B', 'bookmarks', False, _("compare bookmarks")),
3544 3547 ('b', 'branch', [],
3545 3548 _('a specific branch you would like to pull'), _('BRANCH')),
3546 3549 ] + logopts + remoteopts + subrepoopts,
3547 3550 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
3548 3551 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
3549 3552 def incoming(ui, repo, source="default", **opts):
3550 3553 """show new changesets found in source
3551 3554
3552 3555 Show new changesets found in the specified path/URL or the default
3553 3556 pull location. These are the changesets that would have been pulled
3554 3557 by :hg:`pull` at the time you issued this command.
3555 3558
3556 3559 See pull for valid source format details.
3557 3560
3558 3561 .. container:: verbose
3559 3562
3560 3563 With -B/--bookmarks, the result of bookmark comparison between
3561 3564 local and remote repositories is displayed. With -v/--verbose,
3562 3565 status is also displayed for each bookmark like below::
3563 3566
3564 3567 BM1 01234567890a added
3565 3568 BM2 1234567890ab advanced
3566 3569 BM3 234567890abc diverged
3567 3570 BM4 34567890abcd changed
3568 3571
3569 3572 The action taken locally when pulling depends on the
3570 3573 status of each bookmark:
3571 3574
3572 3575 :``added``: pull will create it
3573 3576 :``advanced``: pull will update it
3574 3577 :``diverged``: pull will create a divergent bookmark
3575 3578 :``changed``: result depends on remote changesets
3576 3579
3577 3580 From the point of view of pulling behavior, bookmark
3578 3581 existing only in the remote repository are treated as ``added``,
3579 3582 even if it is in fact locally deleted.
3580 3583
3581 3584 .. container:: verbose
3582 3585
3583 3586 For remote repository, using --bundle avoids downloading the
3584 3587 changesets twice if the incoming is followed by a pull.
3585 3588
3586 3589 Examples:
3587 3590
3588 3591 - show incoming changes with patches and full description::
3589 3592
3590 3593 hg incoming -vp
3591 3594
3592 3595 - show incoming changes excluding merges, store a bundle::
3593 3596
3594 3597 hg in -vpM --bundle incoming.hg
3595 3598 hg pull incoming.hg
3596 3599
3597 3600 - briefly list changes inside a bundle::
3598 3601
3599 3602 hg in changes.hg -T "{desc|firstline}\\n"
3600 3603
3601 3604 Returns 0 if there are incoming changes, 1 otherwise.
3602 3605 """
3603 3606 opts = pycompat.byteskwargs(opts)
3604 3607 if opts.get('graph'):
3605 3608 logcmdutil.checkunsupportedgraphflags([], opts)
3606 3609 def display(other, chlist, displayer):
3607 3610 revdag = logcmdutil.graphrevs(other, chlist, opts)
3608 3611 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3609 3612 graphmod.asciiedges)
3610 3613
3611 3614 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3612 3615 return 0
3613 3616
3614 3617 if opts.get('bundle') and opts.get('subrepos'):
3615 3618 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3616 3619
3617 3620 if opts.get('bookmarks'):
3618 3621 source, branches = hg.parseurl(ui.expandpath(source),
3619 3622 opts.get('branch'))
3620 3623 other = hg.peer(repo, opts, source)
3621 3624 if 'bookmarks' not in other.listkeys('namespaces'):
3622 3625 ui.warn(_("remote doesn't support bookmarks\n"))
3623 3626 return 0
3624 3627 ui.pager('incoming')
3625 3628 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3626 3629 return bookmarks.incoming(ui, repo, other)
3627 3630
3628 3631 repo._subtoppath = ui.expandpath(source)
3629 3632 try:
3630 3633 return hg.incoming(ui, repo, source, opts)
3631 3634 finally:
3632 3635 del repo._subtoppath
3633 3636
3634 3637
3635 3638 @command('init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3636 3639 helpcategory=command.CATEGORY_REPO_CREATION,
3637 3640 helpbasic=True, norepo=True)
3638 3641 def init(ui, dest=".", **opts):
3639 3642 """create a new repository in the given directory
3640 3643
3641 3644 Initialize a new repository in the given directory. If the given
3642 3645 directory does not exist, it will be created.
3643 3646
3644 3647 If no directory is given, the current directory is used.
3645 3648
3646 3649 It is possible to specify an ``ssh://`` URL as the destination.
3647 3650 See :hg:`help urls` for more information.
3648 3651
3649 3652 Returns 0 on success.
3650 3653 """
3651 3654 opts = pycompat.byteskwargs(opts)
3652 3655 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3653 3656
3654 3657 @command('locate',
3655 3658 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3656 3659 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3657 3660 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3658 3661 ] + walkopts,
3659 3662 _('[OPTION]... [PATTERN]...'),
3660 3663 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
3661 3664 def locate(ui, repo, *pats, **opts):
3662 3665 """locate files matching specific patterns (DEPRECATED)
3663 3666
3664 3667 Print files under Mercurial control in the working directory whose
3665 3668 names match the given patterns.
3666 3669
3667 3670 By default, this command searches all directories in the working
3668 3671 directory. To search just the current directory and its
3669 3672 subdirectories, use "--include .".
3670 3673
3671 3674 If no patterns are given to match, this command prints the names
3672 3675 of all files under Mercurial control in the working directory.
3673 3676
3674 3677 If you want to feed the output of this command into the "xargs"
3675 3678 command, use the -0 option to both this command and "xargs". This
3676 3679 will avoid the problem of "xargs" treating single filenames that
3677 3680 contain whitespace as multiple filenames.
3678 3681
3679 3682 See :hg:`help files` for a more versatile command.
3680 3683
3681 3684 Returns 0 if a match is found, 1 otherwise.
3682 3685 """
3683 3686 opts = pycompat.byteskwargs(opts)
3684 3687 if opts.get('print0'):
3685 3688 end = '\0'
3686 3689 else:
3687 3690 end = '\n'
3688 3691 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3689 3692
3690 3693 ret = 1
3691 3694 m = scmutil.match(ctx, pats, opts, default='relglob',
3692 3695 badfn=lambda x, y: False)
3693 3696
3694 3697 ui.pager('locate')
3695 3698 if ctx.rev() is None:
3696 3699 # When run on the working copy, "locate" includes removed files, so
3697 3700 # we get the list of files from the dirstate.
3698 3701 filesgen = sorted(repo.dirstate.matches(m))
3699 3702 else:
3700 3703 filesgen = ctx.matches(m)
3701 3704 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
3702 3705 for abs in filesgen:
3703 3706 if opts.get('fullpath'):
3704 3707 ui.write(repo.wjoin(abs), end)
3705 3708 else:
3706 3709 ui.write(uipathfn(abs), end)
3707 3710 ret = 0
3708 3711
3709 3712 return ret
3710 3713
3711 3714 @command('log|history',
3712 3715 [('f', 'follow', None,
3713 3716 _('follow changeset history, or file history across copies and renames')),
3714 3717 ('', 'follow-first', None,
3715 3718 _('only follow the first parent of merge changesets (DEPRECATED)')),
3716 3719 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3717 3720 ('C', 'copies', None, _('show copied files')),
3718 3721 ('k', 'keyword', [],
3719 3722 _('do case-insensitive search for a given text'), _('TEXT')),
3720 3723 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3721 3724 ('L', 'line-range', [],
3722 3725 _('follow line range of specified file (EXPERIMENTAL)'),
3723 3726 _('FILE,RANGE')),
3724 3727 ('', 'removed', None, _('include revisions where files were removed')),
3725 3728 ('m', 'only-merges', None,
3726 3729 _('show only merges (DEPRECATED) (use -r "merge()" instead)')),
3727 3730 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3728 3731 ('', 'only-branch', [],
3729 3732 _('show only changesets within the given named branch (DEPRECATED)'),
3730 3733 _('BRANCH')),
3731 3734 ('b', 'branch', [],
3732 3735 _('show changesets within the given named branch'), _('BRANCH')),
3733 3736 ('P', 'prune', [],
3734 3737 _('do not display revision or any of its ancestors'), _('REV')),
3735 3738 ] + logopts + walkopts,
3736 3739 _('[OPTION]... [FILE]'),
3737 3740 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3738 3741 helpbasic=True, inferrepo=True,
3739 3742 intents={INTENT_READONLY})
3740 3743 def log(ui, repo, *pats, **opts):
3741 3744 """show revision history of entire repository or files
3742 3745
3743 3746 Print the revision history of the specified files or the entire
3744 3747 project.
3745 3748
3746 3749 If no revision range is specified, the default is ``tip:0`` unless
3747 3750 --follow is set, in which case the working directory parent is
3748 3751 used as the starting revision.
3749 3752
3750 3753 File history is shown without following rename or copy history of
3751 3754 files. Use -f/--follow with a filename to follow history across
3752 3755 renames and copies. --follow without a filename will only show
3753 3756 ancestors of the starting revision.
3754 3757
3755 3758 By default this command prints revision number and changeset id,
3756 3759 tags, non-trivial parents, user, date and time, and a summary for
3757 3760 each commit. When the -v/--verbose switch is used, the list of
3758 3761 changed files and full commit message are shown.
3759 3762
3760 3763 With --graph the revisions are shown as an ASCII art DAG with the most
3761 3764 recent changeset at the top.
3762 3765 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3763 3766 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3764 3767 changeset from the lines below is a parent of the 'o' merge on the same
3765 3768 line.
3766 3769 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3767 3770 of a '|' indicates one or more revisions in a path are omitted.
3768 3771
3769 3772 .. container:: verbose
3770 3773
3771 3774 Use -L/--line-range FILE,M:N options to follow the history of lines
3772 3775 from M to N in FILE. With -p/--patch only diff hunks affecting
3773 3776 specified line range will be shown. This option requires --follow;
3774 3777 it can be specified multiple times. Currently, this option is not
3775 3778 compatible with --graph. This option is experimental.
3776 3779
3777 3780 .. note::
3778 3781
3779 3782 :hg:`log --patch` may generate unexpected diff output for merge
3780 3783 changesets, as it will only compare the merge changeset against
3781 3784 its first parent. Also, only files different from BOTH parents
3782 3785 will appear in files:.
3783 3786
3784 3787 .. note::
3785 3788
3786 3789 For performance reasons, :hg:`log FILE` may omit duplicate changes
3787 3790 made on branches and will not show removals or mode changes. To
3788 3791 see all such changes, use the --removed switch.
3789 3792
3790 3793 .. container:: verbose
3791 3794
3792 3795 .. note::
3793 3796
3794 3797 The history resulting from -L/--line-range options depends on diff
3795 3798 options; for instance if white-spaces are ignored, respective changes
3796 3799 with only white-spaces in specified line range will not be listed.
3797 3800
3798 3801 .. container:: verbose
3799 3802
3800 3803 Some examples:
3801 3804
3802 3805 - changesets with full descriptions and file lists::
3803 3806
3804 3807 hg log -v
3805 3808
3806 3809 - changesets ancestral to the working directory::
3807 3810
3808 3811 hg log -f
3809 3812
3810 3813 - last 10 commits on the current branch::
3811 3814
3812 3815 hg log -l 10 -b .
3813 3816
3814 3817 - changesets showing all modifications of a file, including removals::
3815 3818
3816 3819 hg log --removed file.c
3817 3820
3818 3821 - all changesets that touch a directory, with diffs, excluding merges::
3819 3822
3820 3823 hg log -Mp lib/
3821 3824
3822 3825 - all revision numbers that match a keyword::
3823 3826
3824 3827 hg log -k bug --template "{rev}\\n"
3825 3828
3826 3829 - the full hash identifier of the working directory parent::
3827 3830
3828 3831 hg log -r . --template "{node}\\n"
3829 3832
3830 3833 - list available log templates::
3831 3834
3832 3835 hg log -T list
3833 3836
3834 3837 - check if a given changeset is included in a tagged release::
3835 3838
3836 3839 hg log -r "a21ccf and ancestor(1.9)"
3837 3840
3838 3841 - find all changesets by some user in a date range::
3839 3842
3840 3843 hg log -k alice -d "may 2008 to jul 2008"
3841 3844
3842 3845 - summary of all changesets after the last tag::
3843 3846
3844 3847 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3845 3848
3846 3849 - changesets touching lines 13 to 23 for file.c::
3847 3850
3848 3851 hg log -L file.c,13:23
3849 3852
3850 3853 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3851 3854 main.c with patch::
3852 3855
3853 3856 hg log -L file.c,13:23 -L main.c,2:6 -p
3854 3857
3855 3858 See :hg:`help dates` for a list of formats valid for -d/--date.
3856 3859
3857 3860 See :hg:`help revisions` for more about specifying and ordering
3858 3861 revisions.
3859 3862
3860 3863 See :hg:`help templates` for more about pre-packaged styles and
3861 3864 specifying custom templates. The default template used by the log
3862 3865 command can be customized via the ``ui.logtemplate`` configuration
3863 3866 setting.
3864 3867
3865 3868 Returns 0 on success.
3866 3869
3867 3870 """
3868 3871 opts = pycompat.byteskwargs(opts)
3869 3872 linerange = opts.get('line_range')
3870 3873
3871 3874 if linerange and not opts.get('follow'):
3872 3875 raise error.Abort(_('--line-range requires --follow'))
3873 3876
3874 3877 if linerange and pats:
3875 3878 # TODO: take pats as patterns with no line-range filter
3876 3879 raise error.Abort(
3877 3880 _('FILE arguments are not compatible with --line-range option')
3878 3881 )
3879 3882
3880 3883 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3881 3884 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3882 3885 if linerange:
3883 3886 # TODO: should follow file history from logcmdutil._initialrevs(),
3884 3887 # then filter the result by logcmdutil._makerevset() and --limit
3885 3888 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3886 3889
3887 3890 getrenamed = None
3888 3891 if opts.get('copies'):
3889 3892 endrev = None
3890 3893 if revs:
3891 3894 endrev = revs.max() + 1
3892 3895 getrenamed = scmutil.getrenamedfn(repo, endrev=endrev)
3893 3896
3894 3897 ui.pager('log')
3895 3898 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3896 3899 buffered=True)
3897 3900 if opts.get('graph'):
3898 3901 displayfn = logcmdutil.displaygraphrevs
3899 3902 else:
3900 3903 displayfn = logcmdutil.displayrevs
3901 3904 displayfn(ui, repo, revs, displayer, getrenamed)
3902 3905
3903 3906 @command('manifest',
3904 3907 [('r', 'rev', '', _('revision to display'), _('REV')),
3905 3908 ('', 'all', False, _("list files from all revisions"))]
3906 3909 + formatteropts,
3907 3910 _('[-r REV]'),
3908 3911 helpcategory=command.CATEGORY_MAINTENANCE,
3909 3912 intents={INTENT_READONLY})
3910 3913 def manifest(ui, repo, node=None, rev=None, **opts):
3911 3914 """output the current or given revision of the project manifest
3912 3915
3913 3916 Print a list of version controlled files for the given revision.
3914 3917 If no revision is given, the first parent of the working directory
3915 3918 is used, or the null revision if no revision is checked out.
3916 3919
3917 3920 With -v, print file permissions, symlink and executable bits.
3918 3921 With --debug, print file revision hashes.
3919 3922
3920 3923 If option --all is specified, the list of all files from all revisions
3921 3924 is printed. This includes deleted and renamed files.
3922 3925
3923 3926 Returns 0 on success.
3924 3927 """
3925 3928 opts = pycompat.byteskwargs(opts)
3926 3929 fm = ui.formatter('manifest', opts)
3927 3930
3928 3931 if opts.get('all'):
3929 3932 if rev or node:
3930 3933 raise error.Abort(_("can't specify a revision with --all"))
3931 3934
3932 3935 res = set()
3933 3936 for rev in repo:
3934 3937 ctx = repo[rev]
3935 3938 res |= set(ctx.files())
3936 3939
3937 3940 ui.pager('manifest')
3938 3941 for f in sorted(res):
3939 3942 fm.startitem()
3940 3943 fm.write("path", '%s\n', f)
3941 3944 fm.end()
3942 3945 return
3943 3946
3944 3947 if rev and node:
3945 3948 raise error.Abort(_("please specify just one revision"))
3946 3949
3947 3950 if not node:
3948 3951 node = rev
3949 3952
3950 3953 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3951 3954 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3952 3955 if node:
3953 3956 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3954 3957 ctx = scmutil.revsingle(repo, node)
3955 3958 mf = ctx.manifest()
3956 3959 ui.pager('manifest')
3957 3960 for f in ctx:
3958 3961 fm.startitem()
3959 3962 fm.context(ctx=ctx)
3960 3963 fl = ctx[f].flags()
3961 3964 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3962 3965 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3963 3966 fm.write('path', '%s\n', f)
3964 3967 fm.end()
3965 3968
3966 3969 @command('merge',
3967 3970 [('f', 'force', None,
3968 3971 _('force a merge including outstanding changes (DEPRECATED)')),
3969 3972 ('r', 'rev', '', _('revision to merge'), _('REV')),
3970 3973 ('P', 'preview', None,
3971 3974 _('review revisions to merge (no merge is performed)')),
3972 3975 ('', 'abort', None, _('abort the ongoing merge')),
3973 3976 ] + mergetoolopts,
3974 3977 _('[-P] [[-r] REV]'),
3975 3978 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, helpbasic=True)
3976 3979 def merge(ui, repo, node=None, **opts):
3977 3980 """merge another revision into working directory
3978 3981
3979 3982 The current working directory is updated with all changes made in
3980 3983 the requested revision since the last common predecessor revision.
3981 3984
3982 3985 Files that changed between either parent are marked as changed for
3983 3986 the next commit and a commit must be performed before any further
3984 3987 updates to the repository are allowed. The next commit will have
3985 3988 two parents.
3986 3989
3987 3990 ``--tool`` can be used to specify the merge tool used for file
3988 3991 merges. It overrides the HGMERGE environment variable and your
3989 3992 configuration files. See :hg:`help merge-tools` for options.
3990 3993
3991 3994 If no revision is specified, the working directory's parent is a
3992 3995 head revision, and the current branch contains exactly one other
3993 3996 head, the other head is merged with by default. Otherwise, an
3994 3997 explicit revision with which to merge must be provided.
3995 3998
3996 3999 See :hg:`help resolve` for information on handling file conflicts.
3997 4000
3998 4001 To undo an uncommitted merge, use :hg:`merge --abort` which
3999 4002 will check out a clean copy of the original merge parent, losing
4000 4003 all changes.
4001 4004
4002 4005 Returns 0 on success, 1 if there are unresolved files.
4003 4006 """
4004 4007
4005 4008 opts = pycompat.byteskwargs(opts)
4006 4009 abort = opts.get('abort')
4007 4010 if abort and repo.dirstate.p2() == nullid:
4008 4011 cmdutil.wrongtooltocontinue(repo, _('merge'))
4009 4012 if abort:
4010 4013 if node:
4011 4014 raise error.Abort(_("cannot specify a node with --abort"))
4012 4015 if opts.get('rev'):
4013 4016 raise error.Abort(_("cannot specify both --rev and --abort"))
4014 4017 if opts.get('preview'):
4015 4018 raise error.Abort(_("cannot specify --preview with --abort"))
4016 4019 if opts.get('rev') and node:
4017 4020 raise error.Abort(_("please specify just one revision"))
4018 4021 if not node:
4019 4022 node = opts.get('rev')
4020 4023
4021 4024 if node:
4022 4025 node = scmutil.revsingle(repo, node).node()
4023 4026
4024 4027 if not node and not abort:
4025 4028 node = repo[destutil.destmerge(repo)].node()
4026 4029
4027 4030 if opts.get('preview'):
4028 4031 # find nodes that are ancestors of p2 but not of p1
4029 4032 p1 = repo.lookup('.')
4030 4033 p2 = node
4031 4034 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4032 4035
4033 4036 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4034 4037 for node in nodes:
4035 4038 displayer.show(repo[node])
4036 4039 displayer.close()
4037 4040 return 0
4038 4041
4039 4042 # ui.forcemerge is an internal variable, do not document
4040 4043 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4041 4044 with ui.configoverride(overrides, 'merge'):
4042 4045 force = opts.get('force')
4043 4046 labels = ['working copy', 'merge rev']
4044 4047 return hg.merge(repo, node, force=force, mergeforce=force,
4045 4048 labels=labels, abort=abort)
4046 4049
4047 4050 @command('outgoing|out',
4048 4051 [('f', 'force', None, _('run even when the destination is unrelated')),
4049 4052 ('r', 'rev', [],
4050 4053 _('a changeset intended to be included in the destination'), _('REV')),
4051 4054 ('n', 'newest-first', None, _('show newest record first')),
4052 4055 ('B', 'bookmarks', False, _('compare bookmarks')),
4053 4056 ('b', 'branch', [], _('a specific branch you would like to push'),
4054 4057 _('BRANCH')),
4055 4058 ] + logopts + remoteopts + subrepoopts,
4056 4059 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4057 4060 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
4058 4061 def outgoing(ui, repo, dest=None, **opts):
4059 4062 """show changesets not found in the destination
4060 4063
4061 4064 Show changesets not found in the specified destination repository
4062 4065 or the default push location. These are the changesets that would
4063 4066 be pushed if a push was requested.
4064 4067
4065 4068 See pull for details of valid destination formats.
4066 4069
4067 4070 .. container:: verbose
4068 4071
4069 4072 With -B/--bookmarks, the result of bookmark comparison between
4070 4073 local and remote repositories is displayed. With -v/--verbose,
4071 4074 status is also displayed for each bookmark like below::
4072 4075
4073 4076 BM1 01234567890a added
4074 4077 BM2 deleted
4075 4078 BM3 234567890abc advanced
4076 4079 BM4 34567890abcd diverged
4077 4080 BM5 4567890abcde changed
4078 4081
4079 4082 The action taken when pushing depends on the
4080 4083 status of each bookmark:
4081 4084
4082 4085 :``added``: push with ``-B`` will create it
4083 4086 :``deleted``: push with ``-B`` will delete it
4084 4087 :``advanced``: push will update it
4085 4088 :``diverged``: push with ``-B`` will update it
4086 4089 :``changed``: push with ``-B`` will update it
4087 4090
4088 4091 From the point of view of pushing behavior, bookmarks
4089 4092 existing only in the remote repository are treated as
4090 4093 ``deleted``, even if it is in fact added remotely.
4091 4094
4092 4095 Returns 0 if there are outgoing changes, 1 otherwise.
4093 4096 """
4094 4097 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4095 4098 # style URLs, so don't overwrite dest.
4096 4099 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4097 4100 if not path:
4098 4101 raise error.Abort(_('default repository not configured!'),
4099 4102 hint=_("see 'hg help config.paths'"))
4100 4103
4101 4104 opts = pycompat.byteskwargs(opts)
4102 4105 if opts.get('graph'):
4103 4106 logcmdutil.checkunsupportedgraphflags([], opts)
4104 4107 o, other = hg._outgoing(ui, repo, dest, opts)
4105 4108 if not o:
4106 4109 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4107 4110 return
4108 4111
4109 4112 revdag = logcmdutil.graphrevs(repo, o, opts)
4110 4113 ui.pager('outgoing')
4111 4114 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4112 4115 logcmdutil.displaygraph(ui, repo, revdag, displayer,
4113 4116 graphmod.asciiedges)
4114 4117 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4115 4118 return 0
4116 4119
4117 4120 if opts.get('bookmarks'):
4118 4121 dest = path.pushloc or path.loc
4119 4122 other = hg.peer(repo, opts, dest)
4120 4123 if 'bookmarks' not in other.listkeys('namespaces'):
4121 4124 ui.warn(_("remote doesn't support bookmarks\n"))
4122 4125 return 0
4123 4126 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4124 4127 ui.pager('outgoing')
4125 4128 return bookmarks.outgoing(ui, repo, other)
4126 4129
4127 4130 repo._subtoppath = path.pushloc or path.loc
4128 4131 try:
4129 4132 return hg.outgoing(ui, repo, dest, opts)
4130 4133 finally:
4131 4134 del repo._subtoppath
4132 4135
4133 4136 @command('parents',
4134 4137 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4135 4138 ] + templateopts,
4136 4139 _('[-r REV] [FILE]'),
4137 4140 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4138 4141 inferrepo=True)
4139 4142 def parents(ui, repo, file_=None, **opts):
4140 4143 """show the parents of the working directory or revision (DEPRECATED)
4141 4144
4142 4145 Print the working directory's parent revisions. If a revision is
4143 4146 given via -r/--rev, the parent of that revision will be printed.
4144 4147 If a file argument is given, the revision in which the file was
4145 4148 last changed (before the working directory revision or the
4146 4149 argument to --rev if given) is printed.
4147 4150
4148 4151 This command is equivalent to::
4149 4152
4150 4153 hg log -r "p1()+p2()" or
4151 4154 hg log -r "p1(REV)+p2(REV)" or
4152 4155 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4153 4156 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4154 4157
4155 4158 See :hg:`summary` and :hg:`help revsets` for related information.
4156 4159
4157 4160 Returns 0 on success.
4158 4161 """
4159 4162
4160 4163 opts = pycompat.byteskwargs(opts)
4161 4164 rev = opts.get('rev')
4162 4165 if rev:
4163 4166 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4164 4167 ctx = scmutil.revsingle(repo, rev, None)
4165 4168
4166 4169 if file_:
4167 4170 m = scmutil.match(ctx, (file_,), opts)
4168 4171 if m.anypats() or len(m.files()) != 1:
4169 4172 raise error.Abort(_('can only specify an explicit filename'))
4170 4173 file_ = m.files()[0]
4171 4174 filenodes = []
4172 4175 for cp in ctx.parents():
4173 4176 if not cp:
4174 4177 continue
4175 4178 try:
4176 4179 filenodes.append(cp.filenode(file_))
4177 4180 except error.LookupError:
4178 4181 pass
4179 4182 if not filenodes:
4180 4183 raise error.Abort(_("'%s' not found in manifest!") % file_)
4181 4184 p = []
4182 4185 for fn in filenodes:
4183 4186 fctx = repo.filectx(file_, fileid=fn)
4184 4187 p.append(fctx.node())
4185 4188 else:
4186 4189 p = [cp.node() for cp in ctx.parents()]
4187 4190
4188 4191 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4189 4192 for n in p:
4190 4193 if n != nullid:
4191 4194 displayer.show(repo[n])
4192 4195 displayer.close()
4193 4196
4194 4197 @command('paths', formatteropts, _('[NAME]'),
4195 4198 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4196 4199 optionalrepo=True, intents={INTENT_READONLY})
4197 4200 def paths(ui, repo, search=None, **opts):
4198 4201 """show aliases for remote repositories
4199 4202
4200 4203 Show definition of symbolic path name NAME. If no name is given,
4201 4204 show definition of all available names.
4202 4205
4203 4206 Option -q/--quiet suppresses all output when searching for NAME
4204 4207 and shows only the path names when listing all definitions.
4205 4208
4206 4209 Path names are defined in the [paths] section of your
4207 4210 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4208 4211 repository, ``.hg/hgrc`` is used, too.
4209 4212
4210 4213 The path names ``default`` and ``default-push`` have a special
4211 4214 meaning. When performing a push or pull operation, they are used
4212 4215 as fallbacks if no location is specified on the command-line.
4213 4216 When ``default-push`` is set, it will be used for push and
4214 4217 ``default`` will be used for pull; otherwise ``default`` is used
4215 4218 as the fallback for both. When cloning a repository, the clone
4216 4219 source is written as ``default`` in ``.hg/hgrc``.
4217 4220
4218 4221 .. note::
4219 4222
4220 4223 ``default`` and ``default-push`` apply to all inbound (e.g.
4221 4224 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4222 4225 and :hg:`bundle`) operations.
4223 4226
4224 4227 See :hg:`help urls` for more information.
4225 4228
4226 4229 .. container:: verbose
4227 4230
4228 4231 Template:
4229 4232
4230 4233 The following keywords are supported. See also :hg:`help templates`.
4231 4234
4232 4235 :name: String. Symbolic name of the path alias.
4233 4236 :pushurl: String. URL for push operations.
4234 4237 :url: String. URL or directory path for the other operations.
4235 4238
4236 4239 Returns 0 on success.
4237 4240 """
4238 4241
4239 4242 opts = pycompat.byteskwargs(opts)
4240 4243 ui.pager('paths')
4241 4244 if search:
4242 4245 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4243 4246 if name == search]
4244 4247 else:
4245 4248 pathitems = sorted(ui.paths.iteritems())
4246 4249
4247 4250 fm = ui.formatter('paths', opts)
4248 4251 if fm.isplain():
4249 4252 hidepassword = util.hidepassword
4250 4253 else:
4251 4254 hidepassword = bytes
4252 4255 if ui.quiet:
4253 4256 namefmt = '%s\n'
4254 4257 else:
4255 4258 namefmt = '%s = '
4256 4259 showsubopts = not search and not ui.quiet
4257 4260
4258 4261 for name, path in pathitems:
4259 4262 fm.startitem()
4260 4263 fm.condwrite(not search, 'name', namefmt, name)
4261 4264 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4262 4265 for subopt, value in sorted(path.suboptions.items()):
4263 4266 assert subopt not in ('name', 'url')
4264 4267 if showsubopts:
4265 4268 fm.plain('%s:%s = ' % (name, subopt))
4266 4269 fm.condwrite(showsubopts, subopt, '%s\n', value)
4267 4270
4268 4271 fm.end()
4269 4272
4270 4273 if search and not pathitems:
4271 4274 if not ui.quiet:
4272 4275 ui.warn(_("not found!\n"))
4273 4276 return 1
4274 4277 else:
4275 4278 return 0
4276 4279
4277 4280 @command('phase',
4278 4281 [('p', 'public', False, _('set changeset phase to public')),
4279 4282 ('d', 'draft', False, _('set changeset phase to draft')),
4280 4283 ('s', 'secret', False, _('set changeset phase to secret')),
4281 4284 ('f', 'force', False, _('allow to move boundary backward')),
4282 4285 ('r', 'rev', [], _('target revision'), _('REV')),
4283 4286 ],
4284 4287 _('[-p|-d|-s] [-f] [-r] [REV...]'),
4285 4288 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
4286 4289 def phase(ui, repo, *revs, **opts):
4287 4290 """set or show the current phase name
4288 4291
4289 4292 With no argument, show the phase name of the current revision(s).
4290 4293
4291 4294 With one of -p/--public, -d/--draft or -s/--secret, change the
4292 4295 phase value of the specified revisions.
4293 4296
4294 4297 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4295 4298 lower phase to a higher phase. Phases are ordered as follows::
4296 4299
4297 4300 public < draft < secret
4298 4301
4299 4302 Returns 0 on success, 1 if some phases could not be changed.
4300 4303
4301 4304 (For more information about the phases concept, see :hg:`help phases`.)
4302 4305 """
4303 4306 opts = pycompat.byteskwargs(opts)
4304 4307 # search for a unique phase argument
4305 4308 targetphase = None
4306 4309 for idx, name in enumerate(phases.cmdphasenames):
4307 4310 if opts[name]:
4308 4311 if targetphase is not None:
4309 4312 raise error.Abort(_('only one phase can be specified'))
4310 4313 targetphase = idx
4311 4314
4312 4315 # look for specified revision
4313 4316 revs = list(revs)
4314 4317 revs.extend(opts['rev'])
4315 4318 if not revs:
4316 4319 # display both parents as the second parent phase can influence
4317 4320 # the phase of a merge commit
4318 4321 revs = [c.rev() for c in repo[None].parents()]
4319 4322
4320 4323 revs = scmutil.revrange(repo, revs)
4321 4324
4322 4325 ret = 0
4323 4326 if targetphase is None:
4324 4327 # display
4325 4328 for r in revs:
4326 4329 ctx = repo[r]
4327 4330 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4328 4331 else:
4329 4332 with repo.lock(), repo.transaction("phase") as tr:
4330 4333 # set phase
4331 4334 if not revs:
4332 4335 raise error.Abort(_('empty revision set'))
4333 4336 nodes = [repo[r].node() for r in revs]
4334 4337 # moving revision from public to draft may hide them
4335 4338 # We have to check result on an unfiltered repository
4336 4339 unfi = repo.unfiltered()
4337 4340 getphase = unfi._phasecache.phase
4338 4341 olddata = [getphase(unfi, r) for r in unfi]
4339 4342 phases.advanceboundary(repo, tr, targetphase, nodes)
4340 4343 if opts['force']:
4341 4344 phases.retractboundary(repo, tr, targetphase, nodes)
4342 4345 getphase = unfi._phasecache.phase
4343 4346 newdata = [getphase(unfi, r) for r in unfi]
4344 4347 changes = sum(newdata[r] != olddata[r] for r in unfi)
4345 4348 cl = unfi.changelog
4346 4349 rejected = [n for n in nodes
4347 4350 if newdata[cl.rev(n)] < targetphase]
4348 4351 if rejected:
4349 4352 ui.warn(_('cannot move %i changesets to a higher '
4350 4353 'phase, use --force\n') % len(rejected))
4351 4354 ret = 1
4352 4355 if changes:
4353 4356 msg = _('phase changed for %i changesets\n') % changes
4354 4357 if ret:
4355 4358 ui.status(msg)
4356 4359 else:
4357 4360 ui.note(msg)
4358 4361 else:
4359 4362 ui.warn(_('no phases changed\n'))
4360 4363 return ret
4361 4364
4362 4365 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4363 4366 """Run after a changegroup has been added via pull/unbundle
4364 4367
4365 4368 This takes arguments below:
4366 4369
4367 4370 :modheads: change of heads by pull/unbundle
4368 4371 :optupdate: updating working directory is needed or not
4369 4372 :checkout: update destination revision (or None to default destination)
4370 4373 :brev: a name, which might be a bookmark to be activated after updating
4371 4374 """
4372 4375 if modheads == 0:
4373 4376 return
4374 4377 if optupdate:
4375 4378 try:
4376 4379 return hg.updatetotally(ui, repo, checkout, brev)
4377 4380 except error.UpdateAbort as inst:
4378 4381 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4379 4382 hint = inst.hint
4380 4383 raise error.UpdateAbort(msg, hint=hint)
4381 4384 if modheads is not None and modheads > 1:
4382 4385 currentbranchheads = len(repo.branchheads())
4383 4386 if currentbranchheads == modheads:
4384 4387 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4385 4388 elif currentbranchheads > 1:
4386 4389 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4387 4390 "merge)\n"))
4388 4391 else:
4389 4392 ui.status(_("(run 'hg heads' to see heads)\n"))
4390 4393 elif not ui.configbool('commands', 'update.requiredest'):
4391 4394 ui.status(_("(run 'hg update' to get a working copy)\n"))
4392 4395
4393 4396 @command('pull',
4394 4397 [('u', 'update', None,
4395 4398 _('update to new branch head if new descendants were pulled')),
4396 4399 ('f', 'force', None, _('run even when remote repository is unrelated')),
4397 4400 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4398 4401 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4399 4402 ('b', 'branch', [], _('a specific branch you would like to pull'),
4400 4403 _('BRANCH')),
4401 4404 ] + remoteopts,
4402 4405 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
4403 4406 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4404 4407 helpbasic=True)
4405 4408 def pull(ui, repo, source="default", **opts):
4406 4409 """pull changes from the specified source
4407 4410
4408 4411 Pull changes from a remote repository to a local one.
4409 4412
4410 4413 This finds all changes from the repository at the specified path
4411 4414 or URL and adds them to a local repository (the current one unless
4412 4415 -R is specified). By default, this does not update the copy of the
4413 4416 project in the working directory.
4414 4417
4415 4418 When cloning from servers that support it, Mercurial may fetch
4416 4419 pre-generated data. When this is done, hooks operating on incoming
4417 4420 changesets and changegroups may fire more than once, once for each
4418 4421 pre-generated bundle and as well as for any additional remaining
4419 4422 data. See :hg:`help -e clonebundles` for more.
4420 4423
4421 4424 Use :hg:`incoming` if you want to see what would have been added
4422 4425 by a pull at the time you issued this command. If you then decide
4423 4426 to add those changes to the repository, you should use :hg:`pull
4424 4427 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4425 4428
4426 4429 If SOURCE is omitted, the 'default' path will be used.
4427 4430 See :hg:`help urls` for more information.
4428 4431
4429 4432 Specifying bookmark as ``.`` is equivalent to specifying the active
4430 4433 bookmark's name.
4431 4434
4432 4435 Returns 0 on success, 1 if an update had unresolved files.
4433 4436 """
4434 4437
4435 4438 opts = pycompat.byteskwargs(opts)
4436 4439 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4437 4440 msg = _('update destination required by configuration')
4438 4441 hint = _('use hg pull followed by hg update DEST')
4439 4442 raise error.Abort(msg, hint=hint)
4440 4443
4441 4444 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4442 4445 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4443 4446 other = hg.peer(repo, opts, source)
4444 4447 try:
4445 4448 revs, checkout = hg.addbranchrevs(repo, other, branches,
4446 4449 opts.get('rev'))
4447 4450
4448 4451 pullopargs = {}
4449 4452
4450 4453 nodes = None
4451 4454 if opts.get('bookmark') or revs:
4452 4455 # The list of bookmark used here is the same used to actually update
4453 4456 # the bookmark names, to avoid the race from issue 4689 and we do
4454 4457 # all lookup and bookmark queries in one go so they see the same
4455 4458 # version of the server state (issue 4700).
4456 4459 nodes = []
4457 4460 fnodes = []
4458 4461 revs = revs or []
4459 4462 if revs and not other.capable('lookup'):
4460 4463 err = _("other repository doesn't support revision lookup, "
4461 4464 "so a rev cannot be specified.")
4462 4465 raise error.Abort(err)
4463 4466 with other.commandexecutor() as e:
4464 4467 fremotebookmarks = e.callcommand('listkeys', {
4465 4468 'namespace': 'bookmarks'
4466 4469 })
4467 4470 for r in revs:
4468 4471 fnodes.append(e.callcommand('lookup', {'key': r}))
4469 4472 remotebookmarks = fremotebookmarks.result()
4470 4473 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4471 4474 pullopargs['remotebookmarks'] = remotebookmarks
4472 4475 for b in opts.get('bookmark', []):
4473 4476 b = repo._bookmarks.expandname(b)
4474 4477 if b not in remotebookmarks:
4475 4478 raise error.Abort(_('remote bookmark %s not found!') % b)
4476 4479 nodes.append(remotebookmarks[b])
4477 4480 for i, rev in enumerate(revs):
4478 4481 node = fnodes[i].result()
4479 4482 nodes.append(node)
4480 4483 if rev == checkout:
4481 4484 checkout = node
4482 4485
4483 4486 wlock = util.nullcontextmanager()
4484 4487 if opts.get('update'):
4485 4488 wlock = repo.wlock()
4486 4489 with wlock:
4487 4490 pullopargs.update(opts.get('opargs', {}))
4488 4491 modheads = exchange.pull(repo, other, heads=nodes,
4489 4492 force=opts.get('force'),
4490 4493 bookmarks=opts.get('bookmark', ()),
4491 4494 opargs=pullopargs).cgresult
4492 4495
4493 4496 # brev is a name, which might be a bookmark to be activated at
4494 4497 # the end of the update. In other words, it is an explicit
4495 4498 # destination of the update
4496 4499 brev = None
4497 4500
4498 4501 if checkout:
4499 4502 checkout = repo.unfiltered().changelog.rev(checkout)
4500 4503
4501 4504 # order below depends on implementation of
4502 4505 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4503 4506 # because 'checkout' is determined without it.
4504 4507 if opts.get('rev'):
4505 4508 brev = opts['rev'][0]
4506 4509 elif opts.get('branch'):
4507 4510 brev = opts['branch'][0]
4508 4511 else:
4509 4512 brev = branches[0]
4510 4513 repo._subtoppath = source
4511 4514 try:
4512 4515 ret = postincoming(ui, repo, modheads, opts.get('update'),
4513 4516 checkout, brev)
4514 4517 except error.FilteredRepoLookupError as exc:
4515 4518 msg = _('cannot update to target: %s') % exc.args[0]
4516 4519 exc.args = (msg,) + exc.args[1:]
4517 4520 raise
4518 4521 finally:
4519 4522 del repo._subtoppath
4520 4523
4521 4524 finally:
4522 4525 other.close()
4523 4526 return ret
4524 4527
4525 4528 @command('push',
4526 4529 [('f', 'force', None, _('force push')),
4527 4530 ('r', 'rev', [],
4528 4531 _('a changeset intended to be included in the destination'),
4529 4532 _('REV')),
4530 4533 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4531 4534 ('b', 'branch', [],
4532 4535 _('a specific branch you would like to push'), _('BRANCH')),
4533 4536 ('', 'new-branch', False, _('allow pushing a new branch')),
4534 4537 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4535 4538 ('', 'publish', False, _('push the changeset as public (EXPERIMENTAL)')),
4536 4539 ] + remoteopts,
4537 4540 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
4538 4541 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4539 4542 helpbasic=True)
4540 4543 def push(ui, repo, dest=None, **opts):
4541 4544 """push changes to the specified destination
4542 4545
4543 4546 Push changesets from the local repository to the specified
4544 4547 destination.
4545 4548
4546 4549 This operation is symmetrical to pull: it is identical to a pull
4547 4550 in the destination repository from the current one.
4548 4551
4549 4552 By default, push will not allow creation of new heads at the
4550 4553 destination, since multiple heads would make it unclear which head
4551 4554 to use. In this situation, it is recommended to pull and merge
4552 4555 before pushing.
4553 4556
4554 4557 Use --new-branch if you want to allow push to create a new named
4555 4558 branch that is not present at the destination. This allows you to
4556 4559 only create a new branch without forcing other changes.
4557 4560
4558 4561 .. note::
4559 4562
4560 4563 Extra care should be taken with the -f/--force option,
4561 4564 which will push all new heads on all branches, an action which will
4562 4565 almost always cause confusion for collaborators.
4563 4566
4564 4567 If -r/--rev is used, the specified revision and all its ancestors
4565 4568 will be pushed to the remote repository.
4566 4569
4567 4570 If -B/--bookmark is used, the specified bookmarked revision, its
4568 4571 ancestors, and the bookmark will be pushed to the remote
4569 4572 repository. Specifying ``.`` is equivalent to specifying the active
4570 4573 bookmark's name.
4571 4574
4572 4575 Please see :hg:`help urls` for important details about ``ssh://``
4573 4576 URLs. If DESTINATION is omitted, a default path will be used.
4574 4577
4575 4578 .. container:: verbose
4576 4579
4577 4580 The --pushvars option sends strings to the server that become
4578 4581 environment variables prepended with ``HG_USERVAR_``. For example,
4579 4582 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4580 4583 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4581 4584
4582 4585 pushvars can provide for user-overridable hooks as well as set debug
4583 4586 levels. One example is having a hook that blocks commits containing
4584 4587 conflict markers, but enables the user to override the hook if the file
4585 4588 is using conflict markers for testing purposes or the file format has
4586 4589 strings that look like conflict markers.
4587 4590
4588 4591 By default, servers will ignore `--pushvars`. To enable it add the
4589 4592 following to your configuration file::
4590 4593
4591 4594 [push]
4592 4595 pushvars.server = true
4593 4596
4594 4597 Returns 0 if push was successful, 1 if nothing to push.
4595 4598 """
4596 4599
4597 4600 opts = pycompat.byteskwargs(opts)
4598 4601 if opts.get('bookmark'):
4599 4602 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4600 4603 for b in opts['bookmark']:
4601 4604 # translate -B options to -r so changesets get pushed
4602 4605 b = repo._bookmarks.expandname(b)
4603 4606 if b in repo._bookmarks:
4604 4607 opts.setdefault('rev', []).append(b)
4605 4608 else:
4606 4609 # if we try to push a deleted bookmark, translate it to null
4607 4610 # this lets simultaneous -r, -b options continue working
4608 4611 opts.setdefault('rev', []).append("null")
4609 4612
4610 4613 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4611 4614 if not path:
4612 4615 raise error.Abort(_('default repository not configured!'),
4613 4616 hint=_("see 'hg help config.paths'"))
4614 4617 dest = path.pushloc or path.loc
4615 4618 branches = (path.branch, opts.get('branch') or [])
4616 4619 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4617 4620 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4618 4621 other = hg.peer(repo, opts, dest)
4619 4622
4620 4623 if revs:
4621 4624 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4622 4625 if not revs:
4623 4626 raise error.Abort(_("specified revisions evaluate to an empty set"),
4624 4627 hint=_("use different revision arguments"))
4625 4628 elif path.pushrev:
4626 4629 # It doesn't make any sense to specify ancestor revisions. So limit
4627 4630 # to DAG heads to make discovery simpler.
4628 4631 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4629 4632 revs = scmutil.revrange(repo, [expr])
4630 4633 revs = [repo[rev].node() for rev in revs]
4631 4634 if not revs:
4632 4635 raise error.Abort(_('default push revset for path evaluates to an '
4633 4636 'empty set'))
4634 4637
4635 4638 repo._subtoppath = dest
4636 4639 try:
4637 4640 # push subrepos depth-first for coherent ordering
4638 4641 c = repo['.']
4639 4642 subs = c.substate # only repos that are committed
4640 4643 for s in sorted(subs):
4641 4644 result = c.sub(s).push(opts)
4642 4645 if result == 0:
4643 4646 return not result
4644 4647 finally:
4645 4648 del repo._subtoppath
4646 4649
4647 4650 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4648 4651 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4649 4652
4650 4653 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4651 4654 newbranch=opts.get('new_branch'),
4652 4655 bookmarks=opts.get('bookmark', ()),
4653 4656 publish=opts.get('publish'),
4654 4657 opargs=opargs)
4655 4658
4656 4659 result = not pushop.cgresult
4657 4660
4658 4661 if pushop.bkresult is not None:
4659 4662 if pushop.bkresult == 2:
4660 4663 result = 2
4661 4664 elif not result and pushop.bkresult:
4662 4665 result = 2
4663 4666
4664 4667 return result
4665 4668
4666 4669 @command('recover',
4667 4670 [('','verify', True, "run `hg verify` after succesful recover"),
4668 4671 ],
4669 4672 helpcategory=command.CATEGORY_MAINTENANCE)
4670 4673 def recover(ui, repo, **opts):
4671 4674 """roll back an interrupted transaction
4672 4675
4673 4676 Recover from an interrupted commit or pull.
4674 4677
4675 4678 This command tries to fix the repository status after an
4676 4679 interrupted operation. It should only be necessary when Mercurial
4677 4680 suggests it.
4678 4681
4679 4682 Returns 0 if successful, 1 if nothing to recover or verify fails.
4680 4683 """
4681 4684 ret = repo.recover()
4682 4685 if ret:
4683 4686 if opts[r'verify']:
4684 4687 return hg.verify(repo)
4685 4688 else:
4686 4689 msg = _("(verify step skipped, run `hg verify` to check your "
4687 4690 "repository content)\n")
4688 4691 ui.warn(msg)
4689 4692 return 0
4690 4693 return 1
4691 4694
4692 4695 @command('remove|rm',
4693 4696 [('A', 'after', None, _('record delete for missing files')),
4694 4697 ('f', 'force', None,
4695 4698 _('forget added files, delete modified files')),
4696 4699 ] + subrepoopts + walkopts + dryrunopts,
4697 4700 _('[OPTION]... FILE...'),
4698 4701 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4699 4702 helpbasic=True, inferrepo=True)
4700 4703 def remove(ui, repo, *pats, **opts):
4701 4704 """remove the specified files on the next commit
4702 4705
4703 4706 Schedule the indicated files for removal from the current branch.
4704 4707
4705 4708 This command schedules the files to be removed at the next commit.
4706 4709 To undo a remove before that, see :hg:`revert`. To undo added
4707 4710 files, see :hg:`forget`.
4708 4711
4709 4712 .. container:: verbose
4710 4713
4711 4714 -A/--after can be used to remove only files that have already
4712 4715 been deleted, -f/--force can be used to force deletion, and -Af
4713 4716 can be used to remove files from the next revision without
4714 4717 deleting them from the working directory.
4715 4718
4716 4719 The following table details the behavior of remove for different
4717 4720 file states (columns) and option combinations (rows). The file
4718 4721 states are Added [A], Clean [C], Modified [M] and Missing [!]
4719 4722 (as reported by :hg:`status`). The actions are Warn, Remove
4720 4723 (from branch) and Delete (from disk):
4721 4724
4722 4725 ========= == == == ==
4723 4726 opt/state A C M !
4724 4727 ========= == == == ==
4725 4728 none W RD W R
4726 4729 -f R RD RD R
4727 4730 -A W W W R
4728 4731 -Af R R R R
4729 4732 ========= == == == ==
4730 4733
4731 4734 .. note::
4732 4735
4733 4736 :hg:`remove` never deletes files in Added [A] state from the
4734 4737 working directory, not even if ``--force`` is specified.
4735 4738
4736 4739 Returns 0 on success, 1 if any warnings encountered.
4737 4740 """
4738 4741
4739 4742 opts = pycompat.byteskwargs(opts)
4740 4743 after, force = opts.get('after'), opts.get('force')
4741 4744 dryrun = opts.get('dry_run')
4742 4745 if not pats and not after:
4743 4746 raise error.Abort(_('no files specified'))
4744 4747
4745 4748 m = scmutil.match(repo[None], pats, opts)
4746 4749 subrepos = opts.get('subrepos')
4747 4750 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
4748 4751 return cmdutil.remove(ui, repo, m, "", uipathfn, after, force, subrepos,
4749 4752 dryrun=dryrun)
4750 4753
4751 4754 @command('rename|move|mv',
4752 4755 [('A', 'after', None, _('record a rename that has already occurred')),
4753 4756 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4754 4757 ] + walkopts + dryrunopts,
4755 4758 _('[OPTION]... SOURCE... DEST'),
4756 4759 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
4757 4760 def rename(ui, repo, *pats, **opts):
4758 4761 """rename files; equivalent of copy + remove
4759 4762
4760 4763 Mark dest as copies of sources; mark sources for deletion. If dest
4761 4764 is a directory, copies are put in that directory. If dest is a
4762 4765 file, there can only be one source.
4763 4766
4764 4767 By default, this command copies the contents of files as they
4765 4768 exist in the working directory. If invoked with -A/--after, the
4766 4769 operation is recorded, but no copying is performed.
4767 4770
4768 4771 This command takes effect at the next commit. To undo a rename
4769 4772 before that, see :hg:`revert`.
4770 4773
4771 4774 Returns 0 on success, 1 if errors are encountered.
4772 4775 """
4773 4776 opts = pycompat.byteskwargs(opts)
4774 4777 with repo.wlock(False):
4775 4778 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4776 4779
4777 4780 @command('resolve',
4778 4781 [('a', 'all', None, _('select all unresolved files')),
4779 4782 ('l', 'list', None, _('list state of files needing merge')),
4780 4783 ('m', 'mark', None, _('mark files as resolved')),
4781 4784 ('u', 'unmark', None, _('mark files as unresolved')),
4782 4785 ('n', 'no-status', None, _('hide status prefix')),
4783 4786 ('', 're-merge', None, _('re-merge files'))]
4784 4787 + mergetoolopts + walkopts + formatteropts,
4785 4788 _('[OPTION]... [FILE]...'),
4786 4789 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4787 4790 inferrepo=True)
4788 4791 def resolve(ui, repo, *pats, **opts):
4789 4792 """redo merges or set/view the merge status of files
4790 4793
4791 4794 Merges with unresolved conflicts are often the result of
4792 4795 non-interactive merging using the ``internal:merge`` configuration
4793 4796 setting, or a command-line merge tool like ``diff3``. The resolve
4794 4797 command is used to manage the files involved in a merge, after
4795 4798 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4796 4799 working directory must have two parents). See :hg:`help
4797 4800 merge-tools` for information on configuring merge tools.
4798 4801
4799 4802 The resolve command can be used in the following ways:
4800 4803
4801 4804 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
4802 4805 the specified files, discarding any previous merge attempts. Re-merging
4803 4806 is not performed for files already marked as resolved. Use ``--all/-a``
4804 4807 to select all unresolved files. ``--tool`` can be used to specify
4805 4808 the merge tool used for the given files. It overrides the HGMERGE
4806 4809 environment variable and your configuration files. Previous file
4807 4810 contents are saved with a ``.orig`` suffix.
4808 4811
4809 4812 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4810 4813 (e.g. after having manually fixed-up the files). The default is
4811 4814 to mark all unresolved files.
4812 4815
4813 4816 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4814 4817 default is to mark all resolved files.
4815 4818
4816 4819 - :hg:`resolve -l`: list files which had or still have conflicts.
4817 4820 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4818 4821 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4819 4822 the list. See :hg:`help filesets` for details.
4820 4823
4821 4824 .. note::
4822 4825
4823 4826 Mercurial will not let you commit files with unresolved merge
4824 4827 conflicts. You must use :hg:`resolve -m ...` before you can
4825 4828 commit after a conflicting merge.
4826 4829
4827 4830 .. container:: verbose
4828 4831
4829 4832 Template:
4830 4833
4831 4834 The following keywords are supported in addition to the common template
4832 4835 keywords and functions. See also :hg:`help templates`.
4833 4836
4834 4837 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
4835 4838 :path: String. Repository-absolute path of the file.
4836 4839
4837 4840 Returns 0 on success, 1 if any files fail a resolve attempt.
4838 4841 """
4839 4842
4840 4843 opts = pycompat.byteskwargs(opts)
4841 4844 confirm = ui.configbool('commands', 'resolve.confirm')
4842 4845 flaglist = 'all mark unmark list no_status re_merge'.split()
4843 4846 all, mark, unmark, show, nostatus, remerge = [
4844 4847 opts.get(o) for o in flaglist]
4845 4848
4846 4849 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
4847 4850 if actioncount > 1:
4848 4851 raise error.Abort(_("too many actions specified"))
4849 4852 elif (actioncount == 0
4850 4853 and ui.configbool('commands', 'resolve.explicit-re-merge')):
4851 4854 hint = _('use --mark, --unmark, --list or --re-merge')
4852 4855 raise error.Abort(_('no action specified'), hint=hint)
4853 4856 if pats and all:
4854 4857 raise error.Abort(_("can't specify --all and patterns"))
4855 4858 if not (all or pats or show or mark or unmark):
4856 4859 raise error.Abort(_('no files or directories specified'),
4857 4860 hint=('use --all to re-merge all unresolved files'))
4858 4861
4859 4862 if confirm:
4860 4863 if all:
4861 4864 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4862 4865 b'$$ &Yes $$ &No')):
4863 4866 raise error.Abort(_('user quit'))
4864 4867 if mark and not pats:
4865 4868 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4866 4869 b'$$ &Yes $$ &No')):
4867 4870 raise error.Abort(_('user quit'))
4868 4871 if unmark and not pats:
4869 4872 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4870 4873 b'$$ &Yes $$ &No')):
4871 4874 raise error.Abort(_('user quit'))
4872 4875
4873 4876 uipathfn = scmutil.getuipathfn(repo)
4874 4877
4875 4878 if show:
4876 4879 ui.pager('resolve')
4877 4880 fm = ui.formatter('resolve', opts)
4878 4881 ms = mergemod.mergestate.read(repo)
4879 4882 wctx = repo[None]
4880 4883 m = scmutil.match(wctx, pats, opts)
4881 4884
4882 4885 # Labels and keys based on merge state. Unresolved path conflicts show
4883 4886 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4884 4887 # resolved conflicts.
4885 4888 mergestateinfo = {
4886 4889 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4887 4890 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4888 4891 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4889 4892 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4890 4893 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4891 4894 'D'),
4892 4895 }
4893 4896
4894 4897 for f in ms:
4895 4898 if not m(f):
4896 4899 continue
4897 4900
4898 4901 label, key = mergestateinfo[ms[f]]
4899 4902 fm.startitem()
4900 4903 fm.context(ctx=wctx)
4901 4904 fm.condwrite(not nostatus, 'mergestatus', '%s ', key, label=label)
4902 4905 fm.data(path=f)
4903 4906 fm.plain('%s\n' % uipathfn(f), label=label)
4904 4907 fm.end()
4905 4908 return 0
4906 4909
4907 4910 with repo.wlock():
4908 4911 ms = mergemod.mergestate.read(repo)
4909 4912
4910 4913 if not (ms.active() or repo.dirstate.p2() != nullid):
4911 4914 raise error.Abort(
4912 4915 _('resolve command not applicable when not merging'))
4913 4916
4914 4917 wctx = repo[None]
4915 4918
4916 4919 if (ms.mergedriver
4917 4920 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4918 4921 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4919 4922 ms.commit()
4920 4923 # allow mark and unmark to go through
4921 4924 if not mark and not unmark and not proceed:
4922 4925 return 1
4923 4926
4924 4927 m = scmutil.match(wctx, pats, opts)
4925 4928 ret = 0
4926 4929 didwork = False
4927 4930 runconclude = False
4928 4931
4929 4932 tocomplete = []
4930 4933 hasconflictmarkers = []
4931 4934 if mark:
4932 4935 markcheck = ui.config('commands', 'resolve.mark-check')
4933 4936 if markcheck not in ['warn', 'abort']:
4934 4937 # Treat all invalid / unrecognized values as 'none'.
4935 4938 markcheck = False
4936 4939 for f in ms:
4937 4940 if not m(f):
4938 4941 continue
4939 4942
4940 4943 didwork = True
4941 4944
4942 4945 # don't let driver-resolved files be marked, and run the conclude
4943 4946 # step if asked to resolve
4944 4947 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4945 4948 exact = m.exact(f)
4946 4949 if mark:
4947 4950 if exact:
4948 4951 ui.warn(_('not marking %s as it is driver-resolved\n')
4949 4952 % uipathfn(f))
4950 4953 elif unmark:
4951 4954 if exact:
4952 4955 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4953 4956 % uipathfn(f))
4954 4957 else:
4955 4958 runconclude = True
4956 4959 continue
4957 4960
4958 4961 # path conflicts must be resolved manually
4959 4962 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4960 4963 mergemod.MERGE_RECORD_RESOLVED_PATH):
4961 4964 if mark:
4962 4965 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4963 4966 elif unmark:
4964 4967 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4965 4968 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4966 4969 ui.warn(_('%s: path conflict must be resolved manually\n')
4967 4970 % uipathfn(f))
4968 4971 continue
4969 4972
4970 4973 if mark:
4971 4974 if markcheck:
4972 4975 fdata = repo.wvfs.tryread(f)
4973 4976 if (filemerge.hasconflictmarkers(fdata) and
4974 4977 ms[f] != mergemod.MERGE_RECORD_RESOLVED):
4975 4978 hasconflictmarkers.append(f)
4976 4979 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4977 4980 elif unmark:
4978 4981 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4979 4982 else:
4980 4983 # backup pre-resolve (merge uses .orig for its own purposes)
4981 4984 a = repo.wjoin(f)
4982 4985 try:
4983 4986 util.copyfile(a, a + ".resolve")
4984 4987 except (IOError, OSError) as inst:
4985 4988 if inst.errno != errno.ENOENT:
4986 4989 raise
4987 4990
4988 4991 try:
4989 4992 # preresolve file
4990 4993 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4991 4994 with ui.configoverride(overrides, 'resolve'):
4992 4995 complete, r = ms.preresolve(f, wctx)
4993 4996 if not complete:
4994 4997 tocomplete.append(f)
4995 4998 elif r:
4996 4999 ret = 1
4997 5000 finally:
4998 5001 ms.commit()
4999 5002
5000 5003 # replace filemerge's .orig file with our resolve file, but only
5001 5004 # for merges that are complete
5002 5005 if complete:
5003 5006 try:
5004 5007 util.rename(a + ".resolve",
5005 5008 scmutil.backuppath(ui, repo, f))
5006 5009 except OSError as inst:
5007 5010 if inst.errno != errno.ENOENT:
5008 5011 raise
5009 5012
5010 5013 if hasconflictmarkers:
5011 5014 ui.warn(_('warning: the following files still have conflict '
5012 5015 'markers:\n') + ''.join(' ' + uipathfn(f) + '\n'
5013 5016 for f in hasconflictmarkers))
5014 5017 if markcheck == 'abort' and not all and not pats:
5015 5018 raise error.Abort(_('conflict markers detected'),
5016 5019 hint=_('use --all to mark anyway'))
5017 5020
5018 5021 for f in tocomplete:
5019 5022 try:
5020 5023 # resolve file
5021 5024 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
5022 5025 with ui.configoverride(overrides, 'resolve'):
5023 5026 r = ms.resolve(f, wctx)
5024 5027 if r:
5025 5028 ret = 1
5026 5029 finally:
5027 5030 ms.commit()
5028 5031
5029 5032 # replace filemerge's .orig file with our resolve file
5030 5033 a = repo.wjoin(f)
5031 5034 try:
5032 5035 util.rename(a + ".resolve", scmutil.backuppath(ui, repo, f))
5033 5036 except OSError as inst:
5034 5037 if inst.errno != errno.ENOENT:
5035 5038 raise
5036 5039
5037 5040 ms.commit()
5038 5041 ms.recordactions()
5039 5042
5040 5043 if not didwork and pats:
5041 5044 hint = None
5042 5045 if not any([p for p in pats if p.find(':') >= 0]):
5043 5046 pats = ['path:%s' % p for p in pats]
5044 5047 m = scmutil.match(wctx, pats, opts)
5045 5048 for f in ms:
5046 5049 if not m(f):
5047 5050 continue
5048 5051 def flag(o):
5049 5052 if o == 're_merge':
5050 5053 return '--re-merge '
5051 5054 return '-%s ' % o[0:1]
5052 5055 flags = ''.join([flag(o) for o in flaglist if opts.get(o)])
5053 5056 hint = _("(try: hg resolve %s%s)\n") % (
5054 5057 flags,
5055 5058 ' '.join(pats))
5056 5059 break
5057 5060 ui.warn(_("arguments do not match paths that need resolving\n"))
5058 5061 if hint:
5059 5062 ui.warn(hint)
5060 5063 elif ms.mergedriver and ms.mdstate() != 's':
5061 5064 # run conclude step when either a driver-resolved file is requested
5062 5065 # or there are no driver-resolved files
5063 5066 # we can't use 'ret' to determine whether any files are unresolved
5064 5067 # because we might not have tried to resolve some
5065 5068 if ((runconclude or not list(ms.driverresolved()))
5066 5069 and not list(ms.unresolved())):
5067 5070 proceed = mergemod.driverconclude(repo, ms, wctx)
5068 5071 ms.commit()
5069 5072 if not proceed:
5070 5073 return 1
5071 5074
5072 5075 # Nudge users into finishing an unfinished operation
5073 5076 unresolvedf = list(ms.unresolved())
5074 5077 driverresolvedf = list(ms.driverresolved())
5075 5078 if not unresolvedf and not driverresolvedf:
5076 5079 ui.status(_('(no more unresolved files)\n'))
5077 5080 cmdutil.checkafterresolved(repo)
5078 5081 elif not unresolvedf:
5079 5082 ui.status(_('(no more unresolved files -- '
5080 5083 'run "hg resolve --all" to conclude)\n'))
5081 5084
5082 5085 return ret
5083 5086
5084 5087 @command('revert',
5085 5088 [('a', 'all', None, _('revert all changes when no arguments given')),
5086 5089 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5087 5090 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5088 5091 ('C', 'no-backup', None, _('do not save backup copies of files')),
5089 5092 ('i', 'interactive', None, _('interactively select the changes')),
5090 5093 ] + walkopts + dryrunopts,
5091 5094 _('[OPTION]... [-r REV] [NAME]...'),
5092 5095 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5093 5096 def revert(ui, repo, *pats, **opts):
5094 5097 """restore files to their checkout state
5095 5098
5096 5099 .. note::
5097 5100
5098 5101 To check out earlier revisions, you should use :hg:`update REV`.
5099 5102 To cancel an uncommitted merge (and lose your changes),
5100 5103 use :hg:`merge --abort`.
5101 5104
5102 5105 With no revision specified, revert the specified files or directories
5103 5106 to the contents they had in the parent of the working directory.
5104 5107 This restores the contents of files to an unmodified
5105 5108 state and unschedules adds, removes, copies, and renames. If the
5106 5109 working directory has two parents, you must explicitly specify a
5107 5110 revision.
5108 5111
5109 5112 Using the -r/--rev or -d/--date options, revert the given files or
5110 5113 directories to their states as of a specific revision. Because
5111 5114 revert does not change the working directory parents, this will
5112 5115 cause these files to appear modified. This can be helpful to "back
5113 5116 out" some or all of an earlier change. See :hg:`backout` for a
5114 5117 related method.
5115 5118
5116 5119 Modified files are saved with a .orig suffix before reverting.
5117 5120 To disable these backups, use --no-backup. It is possible to store
5118 5121 the backup files in a custom directory relative to the root of the
5119 5122 repository by setting the ``ui.origbackuppath`` configuration
5120 5123 option.
5121 5124
5122 5125 See :hg:`help dates` for a list of formats valid for -d/--date.
5123 5126
5124 5127 See :hg:`help backout` for a way to reverse the effect of an
5125 5128 earlier changeset.
5126 5129
5127 5130 Returns 0 on success.
5128 5131 """
5129 5132
5130 5133 opts = pycompat.byteskwargs(opts)
5131 5134 if opts.get("date"):
5132 5135 if opts.get("rev"):
5133 5136 raise error.Abort(_("you can't specify a revision and a date"))
5134 5137 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5135 5138
5136 5139 parent, p2 = repo.dirstate.parents()
5137 5140 if not opts.get('rev') and p2 != nullid:
5138 5141 # revert after merge is a trap for new users (issue2915)
5139 5142 raise error.Abort(_('uncommitted merge with no revision specified'),
5140 5143 hint=_("use 'hg update' or see 'hg help revert'"))
5141 5144
5142 5145 rev = opts.get('rev')
5143 5146 if rev:
5144 5147 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5145 5148 ctx = scmutil.revsingle(repo, rev)
5146 5149
5147 5150 if (not (pats or opts.get('include') or opts.get('exclude') or
5148 5151 opts.get('all') or opts.get('interactive'))):
5149 5152 msg = _("no files or directories specified")
5150 5153 if p2 != nullid:
5151 5154 hint = _("uncommitted merge, use --all to discard all changes,"
5152 5155 " or 'hg update -C .' to abort the merge")
5153 5156 raise error.Abort(msg, hint=hint)
5154 5157 dirty = any(repo.status())
5155 5158 node = ctx.node()
5156 5159 if node != parent:
5157 5160 if dirty:
5158 5161 hint = _("uncommitted changes, use --all to discard all"
5159 5162 " changes, or 'hg update %d' to update") % ctx.rev()
5160 5163 else:
5161 5164 hint = _("use --all to revert all files,"
5162 5165 " or 'hg update %d' to update") % ctx.rev()
5163 5166 elif dirty:
5164 5167 hint = _("uncommitted changes, use --all to discard all changes")
5165 5168 else:
5166 5169 hint = _("use --all to revert all files")
5167 5170 raise error.Abort(msg, hint=hint)
5168 5171
5169 5172 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
5170 5173 **pycompat.strkwargs(opts))
5171 5174
5172 5175 @command(
5173 5176 'rollback',
5174 5177 dryrunopts + [('f', 'force', False, _('ignore safety measures'))],
5175 5178 helpcategory=command.CATEGORY_MAINTENANCE)
5176 5179 def rollback(ui, repo, **opts):
5177 5180 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5178 5181
5179 5182 Please use :hg:`commit --amend` instead of rollback to correct
5180 5183 mistakes in the last commit.
5181 5184
5182 5185 This command should be used with care. There is only one level of
5183 5186 rollback, and there is no way to undo a rollback. It will also
5184 5187 restore the dirstate at the time of the last transaction, losing
5185 5188 any dirstate changes since that time. This command does not alter
5186 5189 the working directory.
5187 5190
5188 5191 Transactions are used to encapsulate the effects of all commands
5189 5192 that create new changesets or propagate existing changesets into a
5190 5193 repository.
5191 5194
5192 5195 .. container:: verbose
5193 5196
5194 5197 For example, the following commands are transactional, and their
5195 5198 effects can be rolled back:
5196 5199
5197 5200 - commit
5198 5201 - import
5199 5202 - pull
5200 5203 - push (with this repository as the destination)
5201 5204 - unbundle
5202 5205
5203 5206 To avoid permanent data loss, rollback will refuse to rollback a
5204 5207 commit transaction if it isn't checked out. Use --force to
5205 5208 override this protection.
5206 5209
5207 5210 The rollback command can be entirely disabled by setting the
5208 5211 ``ui.rollback`` configuration setting to false. If you're here
5209 5212 because you want to use rollback and it's disabled, you can
5210 5213 re-enable the command by setting ``ui.rollback`` to true.
5211 5214
5212 5215 This command is not intended for use on public repositories. Once
5213 5216 changes are visible for pull by other users, rolling a transaction
5214 5217 back locally is ineffective (someone else may already have pulled
5215 5218 the changes). Furthermore, a race is possible with readers of the
5216 5219 repository; for example an in-progress pull from the repository
5217 5220 may fail if a rollback is performed.
5218 5221
5219 5222 Returns 0 on success, 1 if no rollback data is available.
5220 5223 """
5221 5224 if not ui.configbool('ui', 'rollback'):
5222 5225 raise error.Abort(_('rollback is disabled because it is unsafe'),
5223 5226 hint=('see `hg help -v rollback` for information'))
5224 5227 return repo.rollback(dryrun=opts.get(r'dry_run'),
5225 5228 force=opts.get(r'force'))
5226 5229
5227 5230 @command(
5228 5231 'root', [] + formatteropts, intents={INTENT_READONLY},
5229 5232 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5230 5233 def root(ui, repo, **opts):
5231 5234 """print the root (top) of the current working directory
5232 5235
5233 5236 Print the root directory of the current repository.
5234 5237
5235 5238 .. container:: verbose
5236 5239
5237 5240 Template:
5238 5241
5239 5242 The following keywords are supported in addition to the common template
5240 5243 keywords and functions. See also :hg:`help templates`.
5241 5244
5242 5245 :hgpath: String. Path to the .hg directory.
5243 5246 :storepath: String. Path to the directory holding versioned data.
5244 5247
5245 5248 Returns 0 on success.
5246 5249 """
5247 5250 opts = pycompat.byteskwargs(opts)
5248 5251 with ui.formatter('root', opts) as fm:
5249 5252 fm.startitem()
5250 5253 fm.write('reporoot', '%s\n', repo.root)
5251 5254 fm.data(hgpath=repo.path, storepath=repo.spath)
5252 5255
5253 5256 @command('serve',
5254 5257 [('A', 'accesslog', '', _('name of access log file to write to'),
5255 5258 _('FILE')),
5256 5259 ('d', 'daemon', None, _('run server in background')),
5257 5260 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5258 5261 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5259 5262 # use string type, then we can check if something was passed
5260 5263 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5261 5264 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5262 5265 _('ADDR')),
5263 5266 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5264 5267 _('PREFIX')),
5265 5268 ('n', 'name', '',
5266 5269 _('name to show in web pages (default: working directory)'), _('NAME')),
5267 5270 ('', 'web-conf', '',
5268 5271 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5269 5272 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5270 5273 _('FILE')),
5271 5274 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5272 5275 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
5273 5276 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
5274 5277 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5275 5278 ('', 'style', '', _('template style to use'), _('STYLE')),
5276 5279 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5277 5280 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
5278 5281 ('', 'print-url', None, _('start and print only the URL'))]
5279 5282 + subrepoopts,
5280 5283 _('[OPTION]...'),
5281 5284 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5282 5285 helpbasic=True, optionalrepo=True)
5283 5286 def serve(ui, repo, **opts):
5284 5287 """start stand-alone webserver
5285 5288
5286 5289 Start a local HTTP repository browser and pull server. You can use
5287 5290 this for ad-hoc sharing and browsing of repositories. It is
5288 5291 recommended to use a real web server to serve a repository for
5289 5292 longer periods of time.
5290 5293
5291 5294 Please note that the server does not implement access control.
5292 5295 This means that, by default, anybody can read from the server and
5293 5296 nobody can write to it by default. Set the ``web.allow-push``
5294 5297 option to ``*`` to allow everybody to push to the server. You
5295 5298 should use a real web server if you need to authenticate users.
5296 5299
5297 5300 By default, the server logs accesses to stdout and errors to
5298 5301 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5299 5302 files.
5300 5303
5301 5304 To have the server choose a free port number to listen on, specify
5302 5305 a port number of 0; in this case, the server will print the port
5303 5306 number it uses.
5304 5307
5305 5308 Returns 0 on success.
5306 5309 """
5307 5310
5308 5311 opts = pycompat.byteskwargs(opts)
5309 5312 if opts["stdio"] and opts["cmdserver"]:
5310 5313 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5311 5314 if opts["print_url"] and ui.verbose:
5312 5315 raise error.Abort(_("cannot use --print-url with --verbose"))
5313 5316
5314 5317 if opts["stdio"]:
5315 5318 if repo is None:
5316 5319 raise error.RepoError(_("there is no Mercurial repository here"
5317 5320 " (.hg not found)"))
5318 5321 s = wireprotoserver.sshserver(ui, repo)
5319 5322 s.serve_forever()
5320 5323
5321 5324 service = server.createservice(ui, repo, opts)
5322 5325 return server.runservice(opts, initfn=service.init, runfn=service.run)
5323 5326
5324 5327 _NOTTERSE = 'nothing'
5325 5328
5326 5329 @command('status|st',
5327 5330 [('A', 'all', None, _('show status of all files')),
5328 5331 ('m', 'modified', None, _('show only modified files')),
5329 5332 ('a', 'added', None, _('show only added files')),
5330 5333 ('r', 'removed', None, _('show only removed files')),
5331 5334 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5332 5335 ('c', 'clean', None, _('show only files without changes')),
5333 5336 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5334 5337 ('i', 'ignored', None, _('show only ignored files')),
5335 5338 ('n', 'no-status', None, _('hide status prefix')),
5336 5339 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
5337 5340 ('C', 'copies', None, _('show source of copied files')),
5338 5341 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5339 5342 ('', 'rev', [], _('show difference from revision'), _('REV')),
5340 5343 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5341 5344 ] + walkopts + subrepoopts + formatteropts,
5342 5345 _('[OPTION]... [FILE]...'),
5343 5346 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5344 5347 helpbasic=True, inferrepo=True,
5345 5348 intents={INTENT_READONLY})
5346 5349 def status(ui, repo, *pats, **opts):
5347 5350 """show changed files in the working directory
5348 5351
5349 5352 Show status of files in the repository. If names are given, only
5350 5353 files that match are shown. Files that are clean or ignored or
5351 5354 the source of a copy/move operation, are not listed unless
5352 5355 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5353 5356 Unless options described with "show only ..." are given, the
5354 5357 options -mardu are used.
5355 5358
5356 5359 Option -q/--quiet hides untracked (unknown and ignored) files
5357 5360 unless explicitly requested with -u/--unknown or -i/--ignored.
5358 5361
5359 5362 .. note::
5360 5363
5361 5364 :hg:`status` may appear to disagree with diff if permissions have
5362 5365 changed or a merge has occurred. The standard diff format does
5363 5366 not report permission changes and diff only reports changes
5364 5367 relative to one merge parent.
5365 5368
5366 5369 If one revision is given, it is used as the base revision.
5367 5370 If two revisions are given, the differences between them are
5368 5371 shown. The --change option can also be used as a shortcut to list
5369 5372 the changed files of a revision from its first parent.
5370 5373
5371 5374 The codes used to show the status of files are::
5372 5375
5373 5376 M = modified
5374 5377 A = added
5375 5378 R = removed
5376 5379 C = clean
5377 5380 ! = missing (deleted by non-hg command, but still tracked)
5378 5381 ? = not tracked
5379 5382 I = ignored
5380 5383 = origin of the previous file (with --copies)
5381 5384
5382 5385 .. container:: verbose
5383 5386
5384 5387 The -t/--terse option abbreviates the output by showing only the directory
5385 5388 name if all the files in it share the same status. The option takes an
5386 5389 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5387 5390 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5388 5391 for 'ignored' and 'c' for clean.
5389 5392
5390 5393 It abbreviates only those statuses which are passed. Note that clean and
5391 5394 ignored files are not displayed with '--terse ic' unless the -c/--clean
5392 5395 and -i/--ignored options are also used.
5393 5396
5394 5397 The -v/--verbose option shows information when the repository is in an
5395 5398 unfinished merge, shelve, rebase state etc. You can have this behavior
5396 5399 turned on by default by enabling the ``commands.status.verbose`` option.
5397 5400
5398 5401 You can skip displaying some of these states by setting
5399 5402 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5400 5403 'histedit', 'merge', 'rebase', or 'unshelve'.
5401 5404
5402 5405 Template:
5403 5406
5404 5407 The following keywords are supported in addition to the common template
5405 5408 keywords and functions. See also :hg:`help templates`.
5406 5409
5407 5410 :path: String. Repository-absolute path of the file.
5408 5411 :source: String. Repository-absolute path of the file originated from.
5409 5412 Available if ``--copies`` is specified.
5410 5413 :status: String. Character denoting file's status.
5411 5414
5412 5415 Examples:
5413 5416
5414 5417 - show changes in the working directory relative to a
5415 5418 changeset::
5416 5419
5417 5420 hg status --rev 9353
5418 5421
5419 5422 - show changes in the working directory relative to the
5420 5423 current directory (see :hg:`help patterns` for more information)::
5421 5424
5422 5425 hg status re:
5423 5426
5424 5427 - show all changes including copies in an existing changeset::
5425 5428
5426 5429 hg status --copies --change 9353
5427 5430
5428 5431 - get a NUL separated list of added files, suitable for xargs::
5429 5432
5430 5433 hg status -an0
5431 5434
5432 5435 - show more information about the repository status, abbreviating
5433 5436 added, removed, modified, deleted, and untracked paths::
5434 5437
5435 5438 hg status -v -t mardu
5436 5439
5437 5440 Returns 0 on success.
5438 5441
5439 5442 """
5440 5443
5441 5444 opts = pycompat.byteskwargs(opts)
5442 5445 revs = opts.get('rev')
5443 5446 change = opts.get('change')
5444 5447 terse = opts.get('terse')
5445 5448 if terse is _NOTTERSE:
5446 5449 if revs:
5447 5450 terse = ''
5448 5451 else:
5449 5452 terse = ui.config('commands', 'status.terse')
5450 5453
5451 5454 if revs and change:
5452 5455 msg = _('cannot specify --rev and --change at the same time')
5453 5456 raise error.Abort(msg)
5454 5457 elif revs and terse:
5455 5458 msg = _('cannot use --terse with --rev')
5456 5459 raise error.Abort(msg)
5457 5460 elif change:
5458 5461 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5459 5462 ctx2 = scmutil.revsingle(repo, change, None)
5460 5463 ctx1 = ctx2.p1()
5461 5464 else:
5462 5465 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5463 5466 ctx1, ctx2 = scmutil.revpair(repo, revs)
5464 5467
5465 5468 forcerelativevalue = None
5466 5469 if ui.hasconfig('commands', 'status.relative'):
5467 5470 forcerelativevalue = ui.configbool('commands', 'status.relative')
5468 5471 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats),
5469 5472 forcerelativevalue=forcerelativevalue)
5470 5473
5471 5474 if opts.get('print0'):
5472 5475 end = '\0'
5473 5476 else:
5474 5477 end = '\n'
5475 5478 copy = {}
5476 5479 states = 'modified added removed deleted unknown ignored clean'.split()
5477 5480 show = [k for k in states if opts.get(k)]
5478 5481 if opts.get('all'):
5479 5482 show += ui.quiet and (states[:4] + ['clean']) or states
5480 5483
5481 5484 if not show:
5482 5485 if ui.quiet:
5483 5486 show = states[:4]
5484 5487 else:
5485 5488 show = states[:5]
5486 5489
5487 5490 m = scmutil.match(ctx2, pats, opts)
5488 5491 if terse:
5489 5492 # we need to compute clean and unknown to terse
5490 5493 stat = repo.status(ctx1.node(), ctx2.node(), m,
5491 5494 'ignored' in show or 'i' in terse,
5492 5495 clean=True, unknown=True,
5493 5496 listsubrepos=opts.get('subrepos'))
5494 5497
5495 5498 stat = cmdutil.tersedir(stat, terse)
5496 5499 else:
5497 5500 stat = repo.status(ctx1.node(), ctx2.node(), m,
5498 5501 'ignored' in show, 'clean' in show,
5499 5502 'unknown' in show, opts.get('subrepos'))
5500 5503
5501 5504 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5502 5505
5503 5506 if (opts.get('all') or opts.get('copies')
5504 5507 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5505 5508 copy = copies.pathcopies(ctx1, ctx2, m)
5506 5509
5507 5510 ui.pager('status')
5508 5511 fm = ui.formatter('status', opts)
5509 5512 fmt = '%s' + end
5510 5513 showchar = not opts.get('no_status')
5511 5514
5512 5515 for state, char, files in changestates:
5513 5516 if state in show:
5514 5517 label = 'status.' + state
5515 5518 for f in files:
5516 5519 fm.startitem()
5517 5520 fm.context(ctx=ctx2)
5518 5521 fm.data(path=f)
5519 5522 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5520 5523 fm.plain(fmt % uipathfn(f), label=label)
5521 5524 if f in copy:
5522 5525 fm.data(source=copy[f])
5523 5526 fm.plain((' %s' + end) % uipathfn(copy[f]),
5524 5527 label='status.copied')
5525 5528
5526 5529 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5527 5530 and not ui.plain()):
5528 5531 cmdutil.morestatus(repo, fm)
5529 5532 fm.end()
5530 5533
5531 5534 @command('summary|sum',
5532 5535 [('', 'remote', None, _('check for push and pull'))],
5533 5536 '[--remote]',
5534 5537 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5535 5538 helpbasic=True,
5536 5539 intents={INTENT_READONLY})
5537 5540 def summary(ui, repo, **opts):
5538 5541 """summarize working directory state
5539 5542
5540 5543 This generates a brief summary of the working directory state,
5541 5544 including parents, branch, commit status, phase and available updates.
5542 5545
5543 5546 With the --remote option, this will check the default paths for
5544 5547 incoming and outgoing changes. This can be time-consuming.
5545 5548
5546 5549 Returns 0 on success.
5547 5550 """
5548 5551
5549 5552 opts = pycompat.byteskwargs(opts)
5550 5553 ui.pager('summary')
5551 5554 ctx = repo[None]
5552 5555 parents = ctx.parents()
5553 5556 pnode = parents[0].node()
5554 5557 marks = []
5555 5558
5556 5559 try:
5557 5560 ms = mergemod.mergestate.read(repo)
5558 5561 except error.UnsupportedMergeRecords as e:
5559 5562 s = ' '.join(e.recordtypes)
5560 5563 ui.warn(
5561 5564 _('warning: merge state has unsupported record types: %s\n') % s)
5562 5565 unresolved = []
5563 5566 else:
5564 5567 unresolved = list(ms.unresolved())
5565 5568
5566 5569 for p in parents:
5567 5570 # label with log.changeset (instead of log.parent) since this
5568 5571 # shows a working directory parent *changeset*:
5569 5572 # i18n: column positioning for "hg summary"
5570 5573 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5571 5574 label=logcmdutil.changesetlabels(p))
5572 5575 ui.write(' '.join(p.tags()), label='log.tag')
5573 5576 if p.bookmarks():
5574 5577 marks.extend(p.bookmarks())
5575 5578 if p.rev() == -1:
5576 5579 if not len(repo):
5577 5580 ui.write(_(' (empty repository)'))
5578 5581 else:
5579 5582 ui.write(_(' (no revision checked out)'))
5580 5583 if p.obsolete():
5581 5584 ui.write(_(' (obsolete)'))
5582 5585 if p.isunstable():
5583 5586 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5584 5587 for instability in p.instabilities())
5585 5588 ui.write(' ('
5586 5589 + ', '.join(instabilities)
5587 5590 + ')')
5588 5591 ui.write('\n')
5589 5592 if p.description():
5590 5593 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5591 5594 label='log.summary')
5592 5595
5593 5596 branch = ctx.branch()
5594 5597 bheads = repo.branchheads(branch)
5595 5598 # i18n: column positioning for "hg summary"
5596 5599 m = _('branch: %s\n') % branch
5597 5600 if branch != 'default':
5598 5601 ui.write(m, label='log.branch')
5599 5602 else:
5600 5603 ui.status(m, label='log.branch')
5601 5604
5602 5605 if marks:
5603 5606 active = repo._activebookmark
5604 5607 # i18n: column positioning for "hg summary"
5605 5608 ui.write(_('bookmarks:'), label='log.bookmark')
5606 5609 if active is not None:
5607 5610 if active in marks:
5608 5611 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5609 5612 marks.remove(active)
5610 5613 else:
5611 5614 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5612 5615 for m in marks:
5613 5616 ui.write(' ' + m, label='log.bookmark')
5614 5617 ui.write('\n', label='log.bookmark')
5615 5618
5616 5619 status = repo.status(unknown=True)
5617 5620
5618 5621 c = repo.dirstate.copies()
5619 5622 copied, renamed = [], []
5620 5623 for d, s in c.iteritems():
5621 5624 if s in status.removed:
5622 5625 status.removed.remove(s)
5623 5626 renamed.append(d)
5624 5627 else:
5625 5628 copied.append(d)
5626 5629 if d in status.added:
5627 5630 status.added.remove(d)
5628 5631
5629 5632 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5630 5633
5631 5634 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5632 5635 (ui.label(_('%d added'), 'status.added'), status.added),
5633 5636 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5634 5637 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5635 5638 (ui.label(_('%d copied'), 'status.copied'), copied),
5636 5639 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5637 5640 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5638 5641 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5639 5642 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5640 5643 t = []
5641 5644 for l, s in labels:
5642 5645 if s:
5643 5646 t.append(l % len(s))
5644 5647
5645 5648 t = ', '.join(t)
5646 5649 cleanworkdir = False
5647 5650
5648 5651 if repo.vfs.exists('graftstate'):
5649 5652 t += _(' (graft in progress)')
5650 5653 if repo.vfs.exists('updatestate'):
5651 5654 t += _(' (interrupted update)')
5652 5655 elif len(parents) > 1:
5653 5656 t += _(' (merge)')
5654 5657 elif branch != parents[0].branch():
5655 5658 t += _(' (new branch)')
5656 5659 elif (parents[0].closesbranch() and
5657 5660 pnode in repo.branchheads(branch, closed=True)):
5658 5661 t += _(' (head closed)')
5659 5662 elif not (status.modified or status.added or status.removed or renamed or
5660 5663 copied or subs):
5661 5664 t += _(' (clean)')
5662 5665 cleanworkdir = True
5663 5666 elif pnode not in bheads:
5664 5667 t += _(' (new branch head)')
5665 5668
5666 5669 if parents:
5667 5670 pendingphase = max(p.phase() for p in parents)
5668 5671 else:
5669 5672 pendingphase = phases.public
5670 5673
5671 5674 if pendingphase > phases.newcommitphase(ui):
5672 5675 t += ' (%s)' % phases.phasenames[pendingphase]
5673 5676
5674 5677 if cleanworkdir:
5675 5678 # i18n: column positioning for "hg summary"
5676 5679 ui.status(_('commit: %s\n') % t.strip())
5677 5680 else:
5678 5681 # i18n: column positioning for "hg summary"
5679 5682 ui.write(_('commit: %s\n') % t.strip())
5680 5683
5681 5684 # all ancestors of branch heads - all ancestors of parent = new csets
5682 5685 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5683 5686 bheads))
5684 5687
5685 5688 if new == 0:
5686 5689 # i18n: column positioning for "hg summary"
5687 5690 ui.status(_('update: (current)\n'))
5688 5691 elif pnode not in bheads:
5689 5692 # i18n: column positioning for "hg summary"
5690 5693 ui.write(_('update: %d new changesets (update)\n') % new)
5691 5694 else:
5692 5695 # i18n: column positioning for "hg summary"
5693 5696 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5694 5697 (new, len(bheads)))
5695 5698
5696 5699 t = []
5697 5700 draft = len(repo.revs('draft()'))
5698 5701 if draft:
5699 5702 t.append(_('%d draft') % draft)
5700 5703 secret = len(repo.revs('secret()'))
5701 5704 if secret:
5702 5705 t.append(_('%d secret') % secret)
5703 5706
5704 5707 if draft or secret:
5705 5708 ui.status(_('phases: %s\n') % ', '.join(t))
5706 5709
5707 5710 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5708 5711 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5709 5712 numtrouble = len(repo.revs(trouble + "()"))
5710 5713 # We write all the possibilities to ease translation
5711 5714 troublemsg = {
5712 5715 "orphan": _("orphan: %d changesets"),
5713 5716 "contentdivergent": _("content-divergent: %d changesets"),
5714 5717 "phasedivergent": _("phase-divergent: %d changesets"),
5715 5718 }
5716 5719 if numtrouble > 0:
5717 5720 ui.status(troublemsg[trouble] % numtrouble + "\n")
5718 5721
5719 5722 cmdutil.summaryhooks(ui, repo)
5720 5723
5721 5724 if opts.get('remote'):
5722 5725 needsincoming, needsoutgoing = True, True
5723 5726 else:
5724 5727 needsincoming, needsoutgoing = False, False
5725 5728 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5726 5729 if i:
5727 5730 needsincoming = True
5728 5731 if o:
5729 5732 needsoutgoing = True
5730 5733 if not needsincoming and not needsoutgoing:
5731 5734 return
5732 5735
5733 5736 def getincoming():
5734 5737 source, branches = hg.parseurl(ui.expandpath('default'))
5735 5738 sbranch = branches[0]
5736 5739 try:
5737 5740 other = hg.peer(repo, {}, source)
5738 5741 except error.RepoError:
5739 5742 if opts.get('remote'):
5740 5743 raise
5741 5744 return source, sbranch, None, None, None
5742 5745 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5743 5746 if revs:
5744 5747 revs = [other.lookup(rev) for rev in revs]
5745 5748 ui.debug('comparing with %s\n' % util.hidepassword(source))
5746 5749 repo.ui.pushbuffer()
5747 5750 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5748 5751 repo.ui.popbuffer()
5749 5752 return source, sbranch, other, commoninc, commoninc[1]
5750 5753
5751 5754 if needsincoming:
5752 5755 source, sbranch, sother, commoninc, incoming = getincoming()
5753 5756 else:
5754 5757 source = sbranch = sother = commoninc = incoming = None
5755 5758
5756 5759 def getoutgoing():
5757 5760 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5758 5761 dbranch = branches[0]
5759 5762 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5760 5763 if source != dest:
5761 5764 try:
5762 5765 dother = hg.peer(repo, {}, dest)
5763 5766 except error.RepoError:
5764 5767 if opts.get('remote'):
5765 5768 raise
5766 5769 return dest, dbranch, None, None
5767 5770 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5768 5771 elif sother is None:
5769 5772 # there is no explicit destination peer, but source one is invalid
5770 5773 return dest, dbranch, None, None
5771 5774 else:
5772 5775 dother = sother
5773 5776 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5774 5777 common = None
5775 5778 else:
5776 5779 common = commoninc
5777 5780 if revs:
5778 5781 revs = [repo.lookup(rev) for rev in revs]
5779 5782 repo.ui.pushbuffer()
5780 5783 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5781 5784 commoninc=common)
5782 5785 repo.ui.popbuffer()
5783 5786 return dest, dbranch, dother, outgoing
5784 5787
5785 5788 if needsoutgoing:
5786 5789 dest, dbranch, dother, outgoing = getoutgoing()
5787 5790 else:
5788 5791 dest = dbranch = dother = outgoing = None
5789 5792
5790 5793 if opts.get('remote'):
5791 5794 t = []
5792 5795 if incoming:
5793 5796 t.append(_('1 or more incoming'))
5794 5797 o = outgoing.missing
5795 5798 if o:
5796 5799 t.append(_('%d outgoing') % len(o))
5797 5800 other = dother or sother
5798 5801 if 'bookmarks' in other.listkeys('namespaces'):
5799 5802 counts = bookmarks.summary(repo, other)
5800 5803 if counts[0] > 0:
5801 5804 t.append(_('%d incoming bookmarks') % counts[0])
5802 5805 if counts[1] > 0:
5803 5806 t.append(_('%d outgoing bookmarks') % counts[1])
5804 5807
5805 5808 if t:
5806 5809 # i18n: column positioning for "hg summary"
5807 5810 ui.write(_('remote: %s\n') % (', '.join(t)))
5808 5811 else:
5809 5812 # i18n: column positioning for "hg summary"
5810 5813 ui.status(_('remote: (synced)\n'))
5811 5814
5812 5815 cmdutil.summaryremotehooks(ui, repo, opts,
5813 5816 ((source, sbranch, sother, commoninc),
5814 5817 (dest, dbranch, dother, outgoing)))
5815 5818
5816 5819 @command('tag',
5817 5820 [('f', 'force', None, _('force tag')),
5818 5821 ('l', 'local', None, _('make the tag local')),
5819 5822 ('r', 'rev', '', _('revision to tag'), _('REV')),
5820 5823 ('', 'remove', None, _('remove a tag')),
5821 5824 # -l/--local is already there, commitopts cannot be used
5822 5825 ('e', 'edit', None, _('invoke editor on commit messages')),
5823 5826 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5824 5827 ] + commitopts2,
5825 5828 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
5826 5829 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
5827 5830 def tag(ui, repo, name1, *names, **opts):
5828 5831 """add one or more tags for the current or given revision
5829 5832
5830 5833 Name a particular revision using <name>.
5831 5834
5832 5835 Tags are used to name particular revisions of the repository and are
5833 5836 very useful to compare different revisions, to go back to significant
5834 5837 earlier versions or to mark branch points as releases, etc. Changing
5835 5838 an existing tag is normally disallowed; use -f/--force to override.
5836 5839
5837 5840 If no revision is given, the parent of the working directory is
5838 5841 used.
5839 5842
5840 5843 To facilitate version control, distribution, and merging of tags,
5841 5844 they are stored as a file named ".hgtags" which is managed similarly
5842 5845 to other project files and can be hand-edited if necessary. This
5843 5846 also means that tagging creates a new commit. The file
5844 5847 ".hg/localtags" is used for local tags (not shared among
5845 5848 repositories).
5846 5849
5847 5850 Tag commits are usually made at the head of a branch. If the parent
5848 5851 of the working directory is not a branch head, :hg:`tag` aborts; use
5849 5852 -f/--force to force the tag commit to be based on a non-head
5850 5853 changeset.
5851 5854
5852 5855 See :hg:`help dates` for a list of formats valid for -d/--date.
5853 5856
5854 5857 Since tag names have priority over branch names during revision
5855 5858 lookup, using an existing branch name as a tag name is discouraged.
5856 5859
5857 5860 Returns 0 on success.
5858 5861 """
5859 5862 opts = pycompat.byteskwargs(opts)
5860 5863 with repo.wlock(), repo.lock():
5861 5864 rev_ = "."
5862 5865 names = [t.strip() for t in (name1,) + names]
5863 5866 if len(names) != len(set(names)):
5864 5867 raise error.Abort(_('tag names must be unique'))
5865 5868 for n in names:
5866 5869 scmutil.checknewlabel(repo, n, 'tag')
5867 5870 if not n:
5868 5871 raise error.Abort(_('tag names cannot consist entirely of '
5869 5872 'whitespace'))
5870 5873 if opts.get('rev') and opts.get('remove'):
5871 5874 raise error.Abort(_("--rev and --remove are incompatible"))
5872 5875 if opts.get('rev'):
5873 5876 rev_ = opts['rev']
5874 5877 message = opts.get('message')
5875 5878 if opts.get('remove'):
5876 5879 if opts.get('local'):
5877 5880 expectedtype = 'local'
5878 5881 else:
5879 5882 expectedtype = 'global'
5880 5883
5881 5884 for n in names:
5882 5885 if repo.tagtype(n) == 'global':
5883 5886 alltags = tagsmod.findglobaltags(ui, repo)
5884 5887 if alltags[n][0] == nullid:
5885 5888 raise error.Abort(_("tag '%s' is already removed") % n)
5886 5889 if not repo.tagtype(n):
5887 5890 raise error.Abort(_("tag '%s' does not exist") % n)
5888 5891 if repo.tagtype(n) != expectedtype:
5889 5892 if expectedtype == 'global':
5890 5893 raise error.Abort(_("tag '%s' is not a global tag") % n)
5891 5894 else:
5892 5895 raise error.Abort(_("tag '%s' is not a local tag") % n)
5893 5896 rev_ = 'null'
5894 5897 if not message:
5895 5898 # we don't translate commit messages
5896 5899 message = 'Removed tag %s' % ', '.join(names)
5897 5900 elif not opts.get('force'):
5898 5901 for n in names:
5899 5902 if n in repo.tags():
5900 5903 raise error.Abort(_("tag '%s' already exists "
5901 5904 "(use -f to force)") % n)
5902 5905 if not opts.get('local'):
5903 5906 p1, p2 = repo.dirstate.parents()
5904 5907 if p2 != nullid:
5905 5908 raise error.Abort(_('uncommitted merge'))
5906 5909 bheads = repo.branchheads()
5907 5910 if not opts.get('force') and bheads and p1 not in bheads:
5908 5911 raise error.Abort(_('working directory is not at a branch head '
5909 5912 '(use -f to force)'))
5910 5913 node = scmutil.revsingle(repo, rev_).node()
5911 5914
5912 5915 if not message:
5913 5916 # we don't translate commit messages
5914 5917 message = ('Added tag %s for changeset %s' %
5915 5918 (', '.join(names), short(node)))
5916 5919
5917 5920 date = opts.get('date')
5918 5921 if date:
5919 5922 date = dateutil.parsedate(date)
5920 5923
5921 5924 if opts.get('remove'):
5922 5925 editform = 'tag.remove'
5923 5926 else:
5924 5927 editform = 'tag.add'
5925 5928 editor = cmdutil.getcommiteditor(editform=editform,
5926 5929 **pycompat.strkwargs(opts))
5927 5930
5928 5931 # don't allow tagging the null rev
5929 5932 if (not opts.get('remove') and
5930 5933 scmutil.revsingle(repo, rev_).rev() == nullrev):
5931 5934 raise error.Abort(_("cannot tag null revision"))
5932 5935
5933 5936 tagsmod.tag(repo, names, node, message, opts.get('local'),
5934 5937 opts.get('user'), date, editor=editor)
5935 5938
5936 5939 @command(
5937 5940 'tags', formatteropts, '',
5938 5941 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5939 5942 intents={INTENT_READONLY})
5940 5943 def tags(ui, repo, **opts):
5941 5944 """list repository tags
5942 5945
5943 5946 This lists both regular and local tags. When the -v/--verbose
5944 5947 switch is used, a third column "local" is printed for local tags.
5945 5948 When the -q/--quiet switch is used, only the tag name is printed.
5946 5949
5947 5950 .. container:: verbose
5948 5951
5949 5952 Template:
5950 5953
5951 5954 The following keywords are supported in addition to the common template
5952 5955 keywords and functions such as ``{tag}``. See also
5953 5956 :hg:`help templates`.
5954 5957
5955 5958 :type: String. ``local`` for local tags.
5956 5959
5957 5960 Returns 0 on success.
5958 5961 """
5959 5962
5960 5963 opts = pycompat.byteskwargs(opts)
5961 5964 ui.pager('tags')
5962 5965 fm = ui.formatter('tags', opts)
5963 5966 hexfunc = fm.hexfunc
5964 5967
5965 5968 for t, n in reversed(repo.tagslist()):
5966 5969 hn = hexfunc(n)
5967 5970 label = 'tags.normal'
5968 5971 tagtype = ''
5969 5972 if repo.tagtype(t) == 'local':
5970 5973 label = 'tags.local'
5971 5974 tagtype = 'local'
5972 5975
5973 5976 fm.startitem()
5974 5977 fm.context(repo=repo)
5975 5978 fm.write('tag', '%s', t, label=label)
5976 5979 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5977 5980 fm.condwrite(not ui.quiet, 'rev node', fmt,
5978 5981 repo.changelog.rev(n), hn, label=label)
5979 5982 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5980 5983 tagtype, label=label)
5981 5984 fm.plain('\n')
5982 5985 fm.end()
5983 5986
5984 5987 @command('tip',
5985 5988 [('p', 'patch', None, _('show patch')),
5986 5989 ('g', 'git', None, _('use git extended diff format')),
5987 5990 ] + templateopts,
5988 5991 _('[-p] [-g]'),
5989 5992 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
5990 5993 def tip(ui, repo, **opts):
5991 5994 """show the tip revision (DEPRECATED)
5992 5995
5993 5996 The tip revision (usually just called the tip) is the changeset
5994 5997 most recently added to the repository (and therefore the most
5995 5998 recently changed head).
5996 5999
5997 6000 If you have just made a commit, that commit will be the tip. If
5998 6001 you have just pulled changes from another repository, the tip of
5999 6002 that repository becomes the current tip. The "tip" tag is special
6000 6003 and cannot be renamed or assigned to a different changeset.
6001 6004
6002 6005 This command is deprecated, please use :hg:`heads` instead.
6003 6006
6004 6007 Returns 0 on success.
6005 6008 """
6006 6009 opts = pycompat.byteskwargs(opts)
6007 6010 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
6008 6011 displayer.show(repo['tip'])
6009 6012 displayer.close()
6010 6013
6011 6014 @command('unbundle',
6012 6015 [('u', 'update', None,
6013 6016 _('update to new branch head if changesets were unbundled'))],
6014 6017 _('[-u] FILE...'),
6015 6018 helpcategory=command.CATEGORY_IMPORT_EXPORT)
6016 6019 def unbundle(ui, repo, fname1, *fnames, **opts):
6017 6020 """apply one or more bundle files
6018 6021
6019 6022 Apply one or more bundle files generated by :hg:`bundle`.
6020 6023
6021 6024 Returns 0 on success, 1 if an update has unresolved files.
6022 6025 """
6023 6026 fnames = (fname1,) + fnames
6024 6027
6025 6028 with repo.lock():
6026 6029 for fname in fnames:
6027 6030 f = hg.openpath(ui, fname)
6028 6031 gen = exchange.readbundle(ui, f, fname)
6029 6032 if isinstance(gen, streamclone.streamcloneapplier):
6030 6033 raise error.Abort(
6031 6034 _('packed bundles cannot be applied with '
6032 6035 '"hg unbundle"'),
6033 6036 hint=_('use "hg debugapplystreamclonebundle"'))
6034 6037 url = 'bundle:' + fname
6035 6038 try:
6036 6039 txnname = 'unbundle'
6037 6040 if not isinstance(gen, bundle2.unbundle20):
6038 6041 txnname = 'unbundle\n%s' % util.hidepassword(url)
6039 6042 with repo.transaction(txnname) as tr:
6040 6043 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
6041 6044 url=url)
6042 6045 except error.BundleUnknownFeatureError as exc:
6043 6046 raise error.Abort(
6044 6047 _('%s: unknown bundle feature, %s') % (fname, exc),
6045 6048 hint=_("see https://mercurial-scm.org/"
6046 6049 "wiki/BundleFeature for more "
6047 6050 "information"))
6048 6051 modheads = bundle2.combinechangegroupresults(op)
6049 6052
6050 6053 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
6051 6054
6052 6055 @command('update|up|checkout|co',
6053 6056 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6054 6057 ('c', 'check', None, _('require clean working directory')),
6055 6058 ('m', 'merge', None, _('merge uncommitted changes')),
6056 6059 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6057 6060 ('r', 'rev', '', _('revision'), _('REV'))
6058 6061 ] + mergetoolopts,
6059 6062 _('[-C|-c|-m] [-d DATE] [[-r] REV]'),
6060 6063 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6061 6064 helpbasic=True)
6062 6065 def update(ui, repo, node=None, **opts):
6063 6066 """update working directory (or switch revisions)
6064 6067
6065 6068 Update the repository's working directory to the specified
6066 6069 changeset. If no changeset is specified, update to the tip of the
6067 6070 current named branch and move the active bookmark (see :hg:`help
6068 6071 bookmarks`).
6069 6072
6070 6073 Update sets the working directory's parent revision to the specified
6071 6074 changeset (see :hg:`help parents`).
6072 6075
6073 6076 If the changeset is not a descendant or ancestor of the working
6074 6077 directory's parent and there are uncommitted changes, the update is
6075 6078 aborted. With the -c/--check option, the working directory is checked
6076 6079 for uncommitted changes; if none are found, the working directory is
6077 6080 updated to the specified changeset.
6078 6081
6079 6082 .. container:: verbose
6080 6083
6081 6084 The -C/--clean, -c/--check, and -m/--merge options control what
6082 6085 happens if the working directory contains uncommitted changes.
6083 6086 At most of one of them can be specified.
6084 6087
6085 6088 1. If no option is specified, and if
6086 6089 the requested changeset is an ancestor or descendant of
6087 6090 the working directory's parent, the uncommitted changes
6088 6091 are merged into the requested changeset and the merged
6089 6092 result is left uncommitted. If the requested changeset is
6090 6093 not an ancestor or descendant (that is, it is on another
6091 6094 branch), the update is aborted and the uncommitted changes
6092 6095 are preserved.
6093 6096
6094 6097 2. With the -m/--merge option, the update is allowed even if the
6095 6098 requested changeset is not an ancestor or descendant of
6096 6099 the working directory's parent.
6097 6100
6098 6101 3. With the -c/--check option, the update is aborted and the
6099 6102 uncommitted changes are preserved.
6100 6103
6101 6104 4. With the -C/--clean option, uncommitted changes are discarded and
6102 6105 the working directory is updated to the requested changeset.
6103 6106
6104 6107 To cancel an uncommitted merge (and lose your changes), use
6105 6108 :hg:`merge --abort`.
6106 6109
6107 6110 Use null as the changeset to remove the working directory (like
6108 6111 :hg:`clone -U`).
6109 6112
6110 6113 If you want to revert just one file to an older revision, use
6111 6114 :hg:`revert [-r REV] NAME`.
6112 6115
6113 6116 See :hg:`help dates` for a list of formats valid for -d/--date.
6114 6117
6115 6118 Returns 0 on success, 1 if there are unresolved files.
6116 6119 """
6117 6120 rev = opts.get(r'rev')
6118 6121 date = opts.get(r'date')
6119 6122 clean = opts.get(r'clean')
6120 6123 check = opts.get(r'check')
6121 6124 merge = opts.get(r'merge')
6122 6125 if rev and node:
6123 6126 raise error.Abort(_("please specify just one revision"))
6124 6127
6125 6128 if ui.configbool('commands', 'update.requiredest'):
6126 6129 if not node and not rev and not date:
6127 6130 raise error.Abort(_('you must specify a destination'),
6128 6131 hint=_('for example: hg update ".::"'))
6129 6132
6130 6133 if rev is None or rev == '':
6131 6134 rev = node
6132 6135
6133 6136 if date and rev is not None:
6134 6137 raise error.Abort(_("you can't specify a revision and a date"))
6135 6138
6136 6139 if len([x for x in (clean, check, merge) if x]) > 1:
6137 6140 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
6138 6141 "or -m/--merge"))
6139 6142
6140 6143 updatecheck = None
6141 6144 if check:
6142 6145 updatecheck = 'abort'
6143 6146 elif merge:
6144 6147 updatecheck = 'none'
6145 6148
6146 6149 with repo.wlock():
6147 6150 cmdutil.clearunfinished(repo)
6148 6151
6149 6152 if date:
6150 6153 rev = cmdutil.finddate(ui, repo, date)
6151 6154
6152 6155 # if we defined a bookmark, we have to remember the original name
6153 6156 brev = rev
6154 6157 if rev:
6155 6158 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
6156 6159 ctx = scmutil.revsingle(repo, rev, default=None)
6157 6160 rev = ctx.rev()
6158 6161 hidden = ctx.hidden()
6159 6162 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
6160 6163 with ui.configoverride(overrides, 'update'):
6161 6164 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
6162 6165 updatecheck=updatecheck)
6163 6166 if hidden:
6164 6167 ctxstr = ctx.hex()[:12]
6165 6168 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
6166 6169
6167 6170 if ctx.obsolete():
6168 6171 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
6169 6172 ui.warn("(%s)\n" % obsfatemsg)
6170 6173 return ret
6171 6174
6172 6175 @command('verify',
6173 6176 [('', 'full', False, 'perform more checks (EXPERIMENTAL)')],
6174 6177 helpcategory=command.CATEGORY_MAINTENANCE)
6175 6178 def verify(ui, repo, **opts):
6176 6179 """verify the integrity of the repository
6177 6180
6178 6181 Verify the integrity of the current repository.
6179 6182
6180 6183 This will perform an extensive check of the repository's
6181 6184 integrity, validating the hashes and checksums of each entry in
6182 6185 the changelog, manifest, and tracked files, as well as the
6183 6186 integrity of their crosslinks and indices.
6184 6187
6185 6188 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6186 6189 for more information about recovery from corruption of the
6187 6190 repository.
6188 6191
6189 6192 Returns 0 on success, 1 if errors are encountered.
6190 6193 """
6191 6194 opts = pycompat.byteskwargs(opts)
6192 6195
6193 6196 level = None
6194 6197 if opts['full']:
6195 6198 level = verifymod.VERIFY_FULL
6196 6199 return hg.verify(repo, level)
6197 6200
6198 6201 @command(
6199 6202 'version', [] + formatteropts, helpcategory=command.CATEGORY_HELP,
6200 6203 norepo=True, intents={INTENT_READONLY})
6201 6204 def version_(ui, **opts):
6202 6205 """output version and copyright information
6203 6206
6204 6207 .. container:: verbose
6205 6208
6206 6209 Template:
6207 6210
6208 6211 The following keywords are supported. See also :hg:`help templates`.
6209 6212
6210 6213 :extensions: List of extensions.
6211 6214 :ver: String. Version number.
6212 6215
6213 6216 And each entry of ``{extensions}`` provides the following sub-keywords
6214 6217 in addition to ``{ver}``.
6215 6218
6216 6219 :bundled: Boolean. True if included in the release.
6217 6220 :name: String. Extension name.
6218 6221 """
6219 6222 opts = pycompat.byteskwargs(opts)
6220 6223 if ui.verbose:
6221 6224 ui.pager('version')
6222 6225 fm = ui.formatter("version", opts)
6223 6226 fm.startitem()
6224 6227 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6225 6228 util.version())
6226 6229 license = _(
6227 6230 "(see https://mercurial-scm.org for more information)\n"
6228 6231 "\nCopyright (C) 2005-2019 Matt Mackall and others\n"
6229 6232 "This is free software; see the source for copying conditions. "
6230 6233 "There is NO\nwarranty; "
6231 6234 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6232 6235 )
6233 6236 if not ui.quiet:
6234 6237 fm.plain(license)
6235 6238
6236 6239 if ui.verbose:
6237 6240 fm.plain(_("\nEnabled extensions:\n\n"))
6238 6241 # format names and versions into columns
6239 6242 names = []
6240 6243 vers = []
6241 6244 isinternals = []
6242 6245 for name, module in extensions.extensions():
6243 6246 names.append(name)
6244 6247 vers.append(extensions.moduleversion(module) or None)
6245 6248 isinternals.append(extensions.ismoduleinternal(module))
6246 6249 fn = fm.nested("extensions", tmpl='{name}\n')
6247 6250 if names:
6248 6251 namefmt = " %%-%ds " % max(len(n) for n in names)
6249 6252 places = [_("external"), _("internal")]
6250 6253 for n, v, p in zip(names, vers, isinternals):
6251 6254 fn.startitem()
6252 6255 fn.condwrite(ui.verbose, "name", namefmt, n)
6253 6256 if ui.verbose:
6254 6257 fn.plain("%s " % places[p])
6255 6258 fn.data(bundled=p)
6256 6259 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6257 6260 if ui.verbose:
6258 6261 fn.plain("\n")
6259 6262 fn.end()
6260 6263 fm.end()
6261 6264
6262 6265 def loadcmdtable(ui, name, cmdtable):
6263 6266 """Load command functions from specified cmdtable
6264 6267 """
6265 6268 overrides = [cmd for cmd in cmdtable if cmd in table]
6266 6269 if overrides:
6267 6270 ui.warn(_("extension '%s' overrides commands: %s\n")
6268 6271 % (name, " ".join(overrides)))
6269 6272 table.update(cmdtable)
@@ -1,974 +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 abort: can only close branch heads
283 abort: current revision is already a branch closing head
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 959 $ hg ci -m "closing branch" --close-branch
960 960 abort: can only close branch heads
961 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 966 o 2: 155349b645be added c
967 967 | default
968 968 |
969 969 o 1: 5f6d8a4bf34a added b
970 970 | default
971 971 |
972 972 @ 0: 9092f1db7931 added a
973 973 default
974 974
General Comments 0
You need to be logged in to leave comments. Login now