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