##// END OF EJS Templates
backout: disable --merge with --no-commit (issue4874)...
Yuya Nishihara -
r27954:9960b636 stable
parent child Browse files
Show More
@@ -1,7040 +1,7042 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 node import hex, bin, nullhex, nullid, nullrev, short
9 9 from lock import release
10 10 from i18n import _
11 11 import os, re, difflib, time, tempfile, errno, shlex
12 12 import sys, socket
13 13 import hg, scmutil, util, revlog, copies, error, bookmarks
14 14 import patch, help, encoding, templatekw, discovery
15 15 import archival, changegroup, cmdutil, hbisect
16 16 import sshserver, hgweb
17 17 import extensions
18 18 import merge as mergemod
19 19 import minirst, revset, fileset
20 20 import dagparser, context, simplemerge, graphmod, copies
21 21 import random, operator
22 22 import setdiscovery, treediscovery, dagutil, pvec, localrepo, destutil
23 23 import phases, obsolete, exchange, bundle2, repair, lock as lockmod
24 24 import ui as uimod
25 25 import streamclone
26 26 import commandserver
27 27
28 28 table = {}
29 29
30 30 command = cmdutil.command(table)
31 31
32 32 # Space delimited list of commands that don't require local repositories.
33 33 # This should be populated by passing norepo=True into the @command decorator.
34 34 norepo = ''
35 35 # Space delimited list of commands that optionally require local repositories.
36 36 # This should be populated by passing optionalrepo=True into the @command
37 37 # decorator.
38 38 optionalrepo = ''
39 39 # Space delimited list of commands that will examine arguments looking for
40 40 # a repository. This should be populated by passing inferrepo=True into the
41 41 # @command decorator.
42 42 inferrepo = ''
43 43
44 44 # label constants
45 45 # until 3.5, bookmarks.current was the advertised name, not
46 46 # bookmarks.active, so we must use both to avoid breaking old
47 47 # custom styles
48 48 activebookmarklabel = 'bookmarks.active bookmarks.current'
49 49
50 50 # common command options
51 51
52 52 globalopts = [
53 53 ('R', 'repository', '',
54 54 _('repository root directory or name of overlay bundle file'),
55 55 _('REPO')),
56 56 ('', 'cwd', '',
57 57 _('change working directory'), _('DIR')),
58 58 ('y', 'noninteractive', None,
59 59 _('do not prompt, automatically pick the first choice for all prompts')),
60 60 ('q', 'quiet', None, _('suppress output')),
61 61 ('v', 'verbose', None, _('enable additional output')),
62 62 ('', 'config', [],
63 63 _('set/override config option (use \'section.name=value\')'),
64 64 _('CONFIG')),
65 65 ('', 'debug', None, _('enable debugging output')),
66 66 ('', 'debugger', None, _('start debugger')),
67 67 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
68 68 _('ENCODE')),
69 69 ('', 'encodingmode', encoding.encodingmode,
70 70 _('set the charset encoding mode'), _('MODE')),
71 71 ('', 'traceback', None, _('always print a traceback on exception')),
72 72 ('', 'time', None, _('time how long the command takes')),
73 73 ('', 'profile', None, _('print command execution profile')),
74 74 ('', 'version', None, _('output version information and exit')),
75 75 ('h', 'help', None, _('display help and exit')),
76 76 ('', 'hidden', False, _('consider hidden changesets')),
77 77 ]
78 78
79 79 dryrunopts = [('n', 'dry-run', None,
80 80 _('do not perform actions, just print output'))]
81 81
82 82 remoteopts = [
83 83 ('e', 'ssh', '',
84 84 _('specify ssh command to use'), _('CMD')),
85 85 ('', 'remotecmd', '',
86 86 _('specify hg command to run on the remote side'), _('CMD')),
87 87 ('', 'insecure', None,
88 88 _('do not verify server certificate (ignoring web.cacerts config)')),
89 89 ]
90 90
91 91 walkopts = [
92 92 ('I', 'include', [],
93 93 _('include names matching the given patterns'), _('PATTERN')),
94 94 ('X', 'exclude', [],
95 95 _('exclude names matching the given patterns'), _('PATTERN')),
96 96 ]
97 97
98 98 commitopts = [
99 99 ('m', 'message', '',
100 100 _('use text as commit message'), _('TEXT')),
101 101 ('l', 'logfile', '',
102 102 _('read commit message from file'), _('FILE')),
103 103 ]
104 104
105 105 commitopts2 = [
106 106 ('d', 'date', '',
107 107 _('record the specified date as commit date'), _('DATE')),
108 108 ('u', 'user', '',
109 109 _('record the specified user as committer'), _('USER')),
110 110 ]
111 111
112 112 # hidden for now
113 113 formatteropts = [
114 114 ('T', 'template', '',
115 115 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
116 116 ]
117 117
118 118 templateopts = [
119 119 ('', 'style', '',
120 120 _('display using template map file (DEPRECATED)'), _('STYLE')),
121 121 ('T', 'template', '',
122 122 _('display with template'), _('TEMPLATE')),
123 123 ]
124 124
125 125 logopts = [
126 126 ('p', 'patch', None, _('show patch')),
127 127 ('g', 'git', None, _('use git extended diff format')),
128 128 ('l', 'limit', '',
129 129 _('limit number of changes displayed'), _('NUM')),
130 130 ('M', 'no-merges', None, _('do not show merges')),
131 131 ('', 'stat', None, _('output diffstat-style summary of changes')),
132 132 ('G', 'graph', None, _("show the revision DAG")),
133 133 ] + templateopts
134 134
135 135 diffopts = [
136 136 ('a', 'text', None, _('treat all files as text')),
137 137 ('g', 'git', None, _('use git extended diff format')),
138 138 ('', 'nodates', None, _('omit dates from diff headers'))
139 139 ]
140 140
141 141 diffwsopts = [
142 142 ('w', 'ignore-all-space', None,
143 143 _('ignore white space when comparing lines')),
144 144 ('b', 'ignore-space-change', None,
145 145 _('ignore changes in the amount of white space')),
146 146 ('B', 'ignore-blank-lines', None,
147 147 _('ignore changes whose lines are all blank')),
148 148 ]
149 149
150 150 diffopts2 = [
151 151 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
152 152 ('p', 'show-function', None, _('show which function each change is in')),
153 153 ('', 'reverse', None, _('produce a diff that undoes the changes')),
154 154 ] + diffwsopts + [
155 155 ('U', 'unified', '',
156 156 _('number of lines of context to show'), _('NUM')),
157 157 ('', 'stat', None, _('output diffstat-style summary of changes')),
158 158 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
159 159 ]
160 160
161 161 mergetoolopts = [
162 162 ('t', 'tool', '', _('specify merge tool')),
163 163 ]
164 164
165 165 similarityopts = [
166 166 ('s', 'similarity', '',
167 167 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
168 168 ]
169 169
170 170 subrepoopts = [
171 171 ('S', 'subrepos', None,
172 172 _('recurse into subrepositories'))
173 173 ]
174 174
175 175 debugrevlogopts = [
176 176 ('c', 'changelog', False, _('open changelog')),
177 177 ('m', 'manifest', False, _('open manifest')),
178 178 ('', 'dir', False, _('open directory manifest')),
179 179 ]
180 180
181 181 # Commands start here, listed alphabetically
182 182
183 183 @command('^add',
184 184 walkopts + subrepoopts + dryrunopts,
185 185 _('[OPTION]... [FILE]...'),
186 186 inferrepo=True)
187 187 def add(ui, repo, *pats, **opts):
188 188 """add the specified files on the next commit
189 189
190 190 Schedule files to be version controlled and added to the
191 191 repository.
192 192
193 193 The files will be added to the repository at the next commit. To
194 194 undo an add before that, see :hg:`forget`.
195 195
196 196 If no names are given, add all files to the repository (except
197 197 files matching ``.hgignore``).
198 198
199 199 .. container:: verbose
200 200
201 201 Examples:
202 202
203 203 - New (unknown) files are added
204 204 automatically by :hg:`add`::
205 205
206 206 $ ls
207 207 foo.c
208 208 $ hg status
209 209 ? foo.c
210 210 $ hg add
211 211 adding foo.c
212 212 $ hg status
213 213 A foo.c
214 214
215 215 - Specific files to be added can be specified::
216 216
217 217 $ ls
218 218 bar.c foo.c
219 219 $ hg status
220 220 ? bar.c
221 221 ? foo.c
222 222 $ hg add bar.c
223 223 $ hg status
224 224 A bar.c
225 225 ? foo.c
226 226
227 227 Returns 0 if all files are successfully added.
228 228 """
229 229
230 230 m = scmutil.match(repo[None], pats, opts)
231 231 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
232 232 return rejected and 1 or 0
233 233
234 234 @command('addremove',
235 235 similarityopts + subrepoopts + walkopts + dryrunopts,
236 236 _('[OPTION]... [FILE]...'),
237 237 inferrepo=True)
238 238 def addremove(ui, repo, *pats, **opts):
239 239 """add all new files, delete all missing files
240 240
241 241 Add all new files and remove all missing files from the
242 242 repository.
243 243
244 244 Unless names are given, new files are ignored if they match any of
245 245 the patterns in ``.hgignore``. As with add, these changes take
246 246 effect at the next commit.
247 247
248 248 Use the -s/--similarity option to detect renamed files. This
249 249 option takes a percentage between 0 (disabled) and 100 (files must
250 250 be identical) as its parameter. With a parameter greater than 0,
251 251 this compares every removed file with every added file and records
252 252 those similar enough as renames. Detecting renamed files this way
253 253 can be expensive. After using this option, :hg:`status -C` can be
254 254 used to check which files were identified as moved or renamed. If
255 255 not specified, -s/--similarity defaults to 100 and only renames of
256 256 identical files are detected.
257 257
258 258 .. container:: verbose
259 259
260 260 Examples:
261 261
262 262 - A number of files (bar.c and foo.c) are new,
263 263 while foobar.c has been removed (without using :hg:`remove`)
264 264 from the repository::
265 265
266 266 $ ls
267 267 bar.c foo.c
268 268 $ hg status
269 269 ! foobar.c
270 270 ? bar.c
271 271 ? foo.c
272 272 $ hg addremove
273 273 adding bar.c
274 274 adding foo.c
275 275 removing foobar.c
276 276 $ hg status
277 277 A bar.c
278 278 A foo.c
279 279 R foobar.c
280 280
281 281 - A file foobar.c was moved to foo.c without using :hg:`rename`.
282 282 Afterwards, it was edited slightly::
283 283
284 284 $ ls
285 285 foo.c
286 286 $ hg status
287 287 ! foobar.c
288 288 ? foo.c
289 289 $ hg addremove --similarity 90
290 290 removing foobar.c
291 291 adding foo.c
292 292 recording removal of foobar.c as rename to foo.c (94% similar)
293 293 $ hg status -C
294 294 A foo.c
295 295 foobar.c
296 296 R foobar.c
297 297
298 298 Returns 0 if all files are successfully added.
299 299 """
300 300 try:
301 301 sim = float(opts.get('similarity') or 100)
302 302 except ValueError:
303 303 raise error.Abort(_('similarity must be a number'))
304 304 if sim < 0 or sim > 100:
305 305 raise error.Abort(_('similarity must be between 0 and 100'))
306 306 matcher = scmutil.match(repo[None], pats, opts)
307 307 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
308 308
309 309 @command('^annotate|blame',
310 310 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
311 311 ('', 'follow', None,
312 312 _('follow copies/renames and list the filename (DEPRECATED)')),
313 313 ('', 'no-follow', None, _("don't follow copies and renames")),
314 314 ('a', 'text', None, _('treat all files as text')),
315 315 ('u', 'user', None, _('list the author (long with -v)')),
316 316 ('f', 'file', None, _('list the filename')),
317 317 ('d', 'date', None, _('list the date (short with -q)')),
318 318 ('n', 'number', None, _('list the revision number (default)')),
319 319 ('c', 'changeset', None, _('list the changeset')),
320 320 ('l', 'line-number', None, _('show line number at the first appearance'))
321 321 ] + diffwsopts + walkopts + formatteropts,
322 322 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
323 323 inferrepo=True)
324 324 def annotate(ui, repo, *pats, **opts):
325 325 """show changeset information by line for each file
326 326
327 327 List changes in files, showing the revision id responsible for
328 328 each line.
329 329
330 330 This command is useful for discovering when a change was made and
331 331 by whom.
332 332
333 333 If you include --file, --user, or --date, the revision number is
334 334 suppressed unless you also include --number.
335 335
336 336 Without the -a/--text option, annotate will avoid processing files
337 337 it detects as binary. With -a, annotate will annotate the file
338 338 anyway, although the results will probably be neither useful
339 339 nor desirable.
340 340
341 341 Returns 0 on success.
342 342 """
343 343 if not pats:
344 344 raise error.Abort(_('at least one filename or pattern is required'))
345 345
346 346 if opts.get('follow'):
347 347 # --follow is deprecated and now just an alias for -f/--file
348 348 # to mimic the behavior of Mercurial before version 1.5
349 349 opts['file'] = True
350 350
351 351 ctx = scmutil.revsingle(repo, opts.get('rev'))
352 352
353 353 fm = ui.formatter('annotate', opts)
354 354 if ui.quiet:
355 355 datefunc = util.shortdate
356 356 else:
357 357 datefunc = util.datestr
358 358 if ctx.rev() is None:
359 359 def hexfn(node):
360 360 if node is None:
361 361 return None
362 362 else:
363 363 return fm.hexfunc(node)
364 364 if opts.get('changeset'):
365 365 # omit "+" suffix which is appended to node hex
366 366 def formatrev(rev):
367 367 if rev is None:
368 368 return '%d' % ctx.p1().rev()
369 369 else:
370 370 return '%d' % rev
371 371 else:
372 372 def formatrev(rev):
373 373 if rev is None:
374 374 return '%d+' % ctx.p1().rev()
375 375 else:
376 376 return '%d ' % rev
377 377 def formathex(hex):
378 378 if hex is None:
379 379 return '%s+' % fm.hexfunc(ctx.p1().node())
380 380 else:
381 381 return '%s ' % hex
382 382 else:
383 383 hexfn = fm.hexfunc
384 384 formatrev = formathex = str
385 385
386 386 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
387 387 ('number', ' ', lambda x: x[0].rev(), formatrev),
388 388 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
389 389 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
390 390 ('file', ' ', lambda x: x[0].path(), str),
391 391 ('line_number', ':', lambda x: x[1], str),
392 392 ]
393 393 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
394 394
395 395 if (not opts.get('user') and not opts.get('changeset')
396 396 and not opts.get('date') and not opts.get('file')):
397 397 opts['number'] = True
398 398
399 399 linenumber = opts.get('line_number') is not None
400 400 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
401 401 raise error.Abort(_('at least one of -n/-c is required for -l'))
402 402
403 403 if fm:
404 404 def makefunc(get, fmt):
405 405 return get
406 406 else:
407 407 def makefunc(get, fmt):
408 408 return lambda x: fmt(get(x))
409 409 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
410 410 if opts.get(op)]
411 411 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
412 412 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
413 413 if opts.get(op))
414 414
415 415 def bad(x, y):
416 416 raise error.Abort("%s: %s" % (x, y))
417 417
418 418 m = scmutil.match(ctx, pats, opts, badfn=bad)
419 419
420 420 follow = not opts.get('no_follow')
421 421 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
422 422 whitespace=True)
423 423 for abs in ctx.walk(m):
424 424 fctx = ctx[abs]
425 425 if not opts.get('text') and util.binary(fctx.data()):
426 426 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
427 427 continue
428 428
429 429 lines = fctx.annotate(follow=follow, linenumber=linenumber,
430 430 diffopts=diffopts)
431 431 formats = []
432 432 pieces = []
433 433
434 434 for f, sep in funcmap:
435 435 l = [f(n) for n, dummy in lines]
436 436 if l:
437 437 if fm:
438 438 formats.append(['%s' for x in l])
439 439 else:
440 440 sizes = [encoding.colwidth(x) for x in l]
441 441 ml = max(sizes)
442 442 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
443 443 pieces.append(l)
444 444
445 445 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
446 446 fm.startitem()
447 447 fm.write(fields, "".join(f), *p)
448 448 fm.write('line', ": %s", l[1])
449 449
450 450 if lines and not lines[-1][1].endswith('\n'):
451 451 fm.plain('\n')
452 452
453 453 fm.end()
454 454
455 455 @command('archive',
456 456 [('', 'no-decode', None, _('do not pass files through decoders')),
457 457 ('p', 'prefix', '', _('directory prefix for files in archive'),
458 458 _('PREFIX')),
459 459 ('r', 'rev', '', _('revision to distribute'), _('REV')),
460 460 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
461 461 ] + subrepoopts + walkopts,
462 462 _('[OPTION]... DEST'))
463 463 def archive(ui, repo, dest, **opts):
464 464 '''create an unversioned archive of a repository revision
465 465
466 466 By default, the revision used is the parent of the working
467 467 directory; use -r/--rev to specify a different revision.
468 468
469 469 The archive type is automatically detected based on file
470 470 extension (to override, use -t/--type).
471 471
472 472 .. container:: verbose
473 473
474 474 Examples:
475 475
476 476 - create a zip file containing the 1.0 release::
477 477
478 478 hg archive -r 1.0 project-1.0.zip
479 479
480 480 - create a tarball excluding .hg files::
481 481
482 482 hg archive project.tar.gz -X ".hg*"
483 483
484 484 Valid types are:
485 485
486 486 :``files``: a directory full of files (default)
487 487 :``tar``: tar archive, uncompressed
488 488 :``tbz2``: tar archive, compressed using bzip2
489 489 :``tgz``: tar archive, compressed using gzip
490 490 :``uzip``: zip archive, uncompressed
491 491 :``zip``: zip archive, compressed using deflate
492 492
493 493 The exact name of the destination archive or directory is given
494 494 using a format string; see :hg:`help export` for details.
495 495
496 496 Each member added to an archive file has a directory prefix
497 497 prepended. Use -p/--prefix to specify a format string for the
498 498 prefix. The default is the basename of the archive, with suffixes
499 499 removed.
500 500
501 501 Returns 0 on success.
502 502 '''
503 503
504 504 ctx = scmutil.revsingle(repo, opts.get('rev'))
505 505 if not ctx:
506 506 raise error.Abort(_('no working directory: please specify a revision'))
507 507 node = ctx.node()
508 508 dest = cmdutil.makefilename(repo, dest, node)
509 509 if os.path.realpath(dest) == repo.root:
510 510 raise error.Abort(_('repository root cannot be destination'))
511 511
512 512 kind = opts.get('type') or archival.guesskind(dest) or 'files'
513 513 prefix = opts.get('prefix')
514 514
515 515 if dest == '-':
516 516 if kind == 'files':
517 517 raise error.Abort(_('cannot archive plain files to stdout'))
518 518 dest = cmdutil.makefileobj(repo, dest)
519 519 if not prefix:
520 520 prefix = os.path.basename(repo.root) + '-%h'
521 521
522 522 prefix = cmdutil.makefilename(repo, prefix, node)
523 523 matchfn = scmutil.match(ctx, [], opts)
524 524 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
525 525 matchfn, prefix, subrepos=opts.get('subrepos'))
526 526
527 527 @command('backout',
528 528 [('', 'merge', None, _('merge with old dirstate parent after backout')),
529 529 ('', 'commit', None,
530 530 _('commit if no conflicts were encountered (DEPRECATED)')),
531 531 ('', 'no-commit', None, _('do not commit')),
532 532 ('', 'parent', '',
533 533 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
534 534 ('r', 'rev', '', _('revision to backout'), _('REV')),
535 535 ('e', 'edit', False, _('invoke editor on commit messages')),
536 536 ] + mergetoolopts + walkopts + commitopts + commitopts2,
537 537 _('[OPTION]... [-r] REV'))
538 538 def backout(ui, repo, node=None, rev=None, **opts):
539 539 '''reverse effect of earlier changeset
540 540
541 541 Prepare a new changeset with the effect of REV undone in the
542 542 current working directory. If no conflicts were encountered,
543 543 it will be committed immediately.
544 544
545 545 If REV is the parent of the working directory, then this new changeset
546 546 is committed automatically (unless --no-commit is specified).
547 547
548 548 .. note::
549 549
550 550 :hg:`backout` cannot be used to fix either an unwanted or
551 551 incorrect merge.
552 552
553 553 .. container:: verbose
554 554
555 555 Examples:
556 556
557 557 - Reverse the effect of the parent of the working directory.
558 558 This backout will be committed immediately::
559 559
560 560 hg backout -r .
561 561
562 562 - Reverse the effect of previous bad revision 23::
563 563
564 564 hg backout -r 23
565 565
566 566 - Reverse the effect of previous bad revision 23 and
567 567 leave changes uncommitted::
568 568
569 569 hg backout -r 23 --no-commit
570 570 hg commit -m "Backout revision 23"
571 571
572 572 By default, the pending changeset will have one parent,
573 573 maintaining a linear history. With --merge, the pending
574 574 changeset will instead have two parents: the old parent of the
575 575 working directory and a new child of REV that simply undoes REV.
576 576
577 577 Before version 1.7, the behavior without --merge was equivalent
578 578 to specifying --merge followed by :hg:`update --clean .` to
579 579 cancel the merge and leave the child of REV as a head to be
580 580 merged separately.
581 581
582 582 See :hg:`help dates` for a list of formats valid for -d/--date.
583 583
584 584 See :hg:`help revert` for a way to restore files to the state
585 585 of another revision.
586 586
587 587 Returns 0 on success, 1 if nothing to backout or there are unresolved
588 588 files.
589 589 '''
590 590 wlock = lock = None
591 591 try:
592 592 wlock = repo.wlock()
593 593 lock = repo.lock()
594 594 return _dobackout(ui, repo, node, rev, **opts)
595 595 finally:
596 596 release(lock, wlock)
597 597
598 598 def _dobackout(ui, repo, node=None, rev=None, **opts):
599 599 if opts.get('commit') and opts.get('no_commit'):
600 600 raise error.Abort(_("cannot use --commit with --no-commit"))
601 if opts.get('merge') and opts.get('no_commit'):
602 raise error.Abort(_("cannot use --merge with --no-commit"))
601 603
602 604 if rev and node:
603 605 raise error.Abort(_("please specify just one revision"))
604 606
605 607 if not rev:
606 608 rev = node
607 609
608 610 if not rev:
609 611 raise error.Abort(_("please specify a revision to backout"))
610 612
611 613 date = opts.get('date')
612 614 if date:
613 615 opts['date'] = util.parsedate(date)
614 616
615 617 cmdutil.checkunfinished(repo)
616 618 cmdutil.bailifchanged(repo)
617 619 node = scmutil.revsingle(repo, rev).node()
618 620
619 621 op1, op2 = repo.dirstate.parents()
620 622 if not repo.changelog.isancestor(node, op1):
621 623 raise error.Abort(_('cannot backout change that is not an ancestor'))
622 624
623 625 p1, p2 = repo.changelog.parents(node)
624 626 if p1 == nullid:
625 627 raise error.Abort(_('cannot backout a change with no parents'))
626 628 if p2 != nullid:
627 629 if not opts.get('parent'):
628 630 raise error.Abort(_('cannot backout a merge changeset'))
629 631 p = repo.lookup(opts['parent'])
630 632 if p not in (p1, p2):
631 633 raise error.Abort(_('%s is not a parent of %s') %
632 634 (short(p), short(node)))
633 635 parent = p
634 636 else:
635 637 if opts.get('parent'):
636 638 raise error.Abort(_('cannot use --parent on non-merge changeset'))
637 639 parent = p1
638 640
639 641 # the backout should appear on the same branch
640 642 branch = repo.dirstate.branch()
641 643 bheads = repo.branchheads(branch)
642 644 rctx = scmutil.revsingle(repo, hex(parent))
643 645 if not opts.get('merge') and op1 != node:
644 646 dsguard = cmdutil.dirstateguard(repo, 'backout')
645 647 try:
646 648 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
647 649 'backout')
648 650 stats = mergemod.update(repo, parent, True, True, node, False)
649 651 repo.setparents(op1, op2)
650 652 dsguard.close()
651 653 hg._showstats(repo, stats)
652 654 if stats[3]:
653 655 repo.ui.status(_("use 'hg resolve' to retry unresolved "
654 656 "file merges\n"))
655 657 return 1
656 658 finally:
657 659 ui.setconfig('ui', 'forcemerge', '', '')
658 660 lockmod.release(dsguard)
659 661 else:
660 662 hg.clean(repo, node, show_stats=False)
661 663 repo.dirstate.setbranch(branch)
662 664 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
663 665
664 666 if opts.get('no_commit'):
665 667 msg = _("changeset %s backed out, "
666 668 "don't forget to commit.\n")
667 669 ui.status(msg % short(node))
668 670 return 0
669 671
670 672 def commitfunc(ui, repo, message, match, opts):
671 673 editform = 'backout'
672 674 e = cmdutil.getcommiteditor(editform=editform, **opts)
673 675 if not message:
674 676 # we don't translate commit messages
675 677 message = "Backed out changeset %s" % short(node)
676 678 e = cmdutil.getcommiteditor(edit=True, editform=editform)
677 679 return repo.commit(message, opts.get('user'), opts.get('date'),
678 680 match, editor=e)
679 681 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
680 682 if not newnode:
681 683 ui.status(_("nothing changed\n"))
682 684 return 1
683 685 cmdutil.commitstatus(repo, newnode, branch, bheads)
684 686
685 687 def nice(node):
686 688 return '%d:%s' % (repo.changelog.rev(node), short(node))
687 689 ui.status(_('changeset %s backs out changeset %s\n') %
688 690 (nice(repo.changelog.tip()), nice(node)))
689 691 if opts.get('merge') and op1 != node:
690 692 hg.clean(repo, op1, show_stats=False)
691 693 ui.status(_('merging with changeset %s\n')
692 694 % nice(repo.changelog.tip()))
693 695 try:
694 696 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
695 697 'backout')
696 698 return hg.merge(repo, hex(repo.changelog.tip()))
697 699 finally:
698 700 ui.setconfig('ui', 'forcemerge', '', '')
699 701 return 0
700 702
701 703 @command('bisect',
702 704 [('r', 'reset', False, _('reset bisect state')),
703 705 ('g', 'good', False, _('mark changeset good')),
704 706 ('b', 'bad', False, _('mark changeset bad')),
705 707 ('s', 'skip', False, _('skip testing changeset')),
706 708 ('e', 'extend', False, _('extend the bisect range')),
707 709 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
708 710 ('U', 'noupdate', False, _('do not update to target'))],
709 711 _("[-gbsr] [-U] [-c CMD] [REV]"))
710 712 def bisect(ui, repo, rev=None, extra=None, command=None,
711 713 reset=None, good=None, bad=None, skip=None, extend=None,
712 714 noupdate=None):
713 715 """subdivision search of changesets
714 716
715 717 This command helps to find changesets which introduce problems. To
716 718 use, mark the earliest changeset you know exhibits the problem as
717 719 bad, then mark the latest changeset which is free from the problem
718 720 as good. Bisect will update your working directory to a revision
719 721 for testing (unless the -U/--noupdate option is specified). Once
720 722 you have performed tests, mark the working directory as good or
721 723 bad, and bisect will either update to another candidate changeset
722 724 or announce that it has found the bad revision.
723 725
724 726 As a shortcut, you can also use the revision argument to mark a
725 727 revision as good or bad without checking it out first.
726 728
727 729 If you supply a command, it will be used for automatic bisection.
728 730 The environment variable HG_NODE will contain the ID of the
729 731 changeset being tested. The exit status of the command will be
730 732 used to mark revisions as good or bad: status 0 means good, 125
731 733 means to skip the revision, 127 (command not found) will abort the
732 734 bisection, and any other non-zero exit status means the revision
733 735 is bad.
734 736
735 737 .. container:: verbose
736 738
737 739 Some examples:
738 740
739 741 - start a bisection with known bad revision 34, and good revision 12::
740 742
741 743 hg bisect --bad 34
742 744 hg bisect --good 12
743 745
744 746 - advance the current bisection by marking current revision as good or
745 747 bad::
746 748
747 749 hg bisect --good
748 750 hg bisect --bad
749 751
750 752 - mark the current revision, or a known revision, to be skipped (e.g. if
751 753 that revision is not usable because of another issue)::
752 754
753 755 hg bisect --skip
754 756 hg bisect --skip 23
755 757
756 758 - skip all revisions that do not touch directories ``foo`` or ``bar``::
757 759
758 760 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
759 761
760 762 - forget the current bisection::
761 763
762 764 hg bisect --reset
763 765
764 766 - use 'make && make tests' to automatically find the first broken
765 767 revision::
766 768
767 769 hg bisect --reset
768 770 hg bisect --bad 34
769 771 hg bisect --good 12
770 772 hg bisect --command "make && make tests"
771 773
772 774 - see all changesets whose states are already known in the current
773 775 bisection::
774 776
775 777 hg log -r "bisect(pruned)"
776 778
777 779 - see the changeset currently being bisected (especially useful
778 780 if running with -U/--noupdate)::
779 781
780 782 hg log -r "bisect(current)"
781 783
782 784 - see all changesets that took part in the current bisection::
783 785
784 786 hg log -r "bisect(range)"
785 787
786 788 - you can even get a nice graph::
787 789
788 790 hg log --graph -r "bisect(range)"
789 791
790 792 See :hg:`help revsets` for more about the `bisect()` keyword.
791 793
792 794 Returns 0 on success.
793 795 """
794 796 def extendbisectrange(nodes, good):
795 797 # bisect is incomplete when it ends on a merge node and
796 798 # one of the parent was not checked.
797 799 parents = repo[nodes[0]].parents()
798 800 if len(parents) > 1:
799 801 if good:
800 802 side = state['bad']
801 803 else:
802 804 side = state['good']
803 805 num = len(set(i.node() for i in parents) & set(side))
804 806 if num == 1:
805 807 return parents[0].ancestor(parents[1])
806 808 return None
807 809
808 810 def print_result(nodes, good):
809 811 displayer = cmdutil.show_changeset(ui, repo, {})
810 812 if len(nodes) == 1:
811 813 # narrowed it down to a single revision
812 814 if good:
813 815 ui.write(_("The first good revision is:\n"))
814 816 else:
815 817 ui.write(_("The first bad revision is:\n"))
816 818 displayer.show(repo[nodes[0]])
817 819 extendnode = extendbisectrange(nodes, good)
818 820 if extendnode is not None:
819 821 ui.write(_('Not all ancestors of this changeset have been'
820 822 ' checked.\nUse bisect --extend to continue the '
821 823 'bisection from\nthe common ancestor, %s.\n')
822 824 % extendnode)
823 825 else:
824 826 # multiple possible revisions
825 827 if good:
826 828 ui.write(_("Due to skipped revisions, the first "
827 829 "good revision could be any of:\n"))
828 830 else:
829 831 ui.write(_("Due to skipped revisions, the first "
830 832 "bad revision could be any of:\n"))
831 833 for n in nodes:
832 834 displayer.show(repo[n])
833 835 displayer.close()
834 836
835 837 def check_state(state, interactive=True):
836 838 if not state['good'] or not state['bad']:
837 839 if (good or bad or skip or reset) and interactive:
838 840 return
839 841 if not state['good']:
840 842 raise error.Abort(_('cannot bisect (no known good revisions)'))
841 843 else:
842 844 raise error.Abort(_('cannot bisect (no known bad revisions)'))
843 845 return True
844 846
845 847 # backward compatibility
846 848 if rev in "good bad reset init".split():
847 849 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
848 850 cmd, rev, extra = rev, extra, None
849 851 if cmd == "good":
850 852 good = True
851 853 elif cmd == "bad":
852 854 bad = True
853 855 else:
854 856 reset = True
855 857 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
856 858 raise error.Abort(_('incompatible arguments'))
857 859
858 860 cmdutil.checkunfinished(repo)
859 861
860 862 if reset:
861 863 p = repo.join("bisect.state")
862 864 if os.path.exists(p):
863 865 os.unlink(p)
864 866 return
865 867
866 868 state = hbisect.load_state(repo)
867 869
868 870 if command:
869 871 changesets = 1
870 872 if noupdate:
871 873 try:
872 874 node = state['current'][0]
873 875 except LookupError:
874 876 raise error.Abort(_('current bisect revision is unknown - '
875 877 'start a new bisect to fix'))
876 878 else:
877 879 node, p2 = repo.dirstate.parents()
878 880 if p2 != nullid:
879 881 raise error.Abort(_('current bisect revision is a merge'))
880 882 try:
881 883 while changesets:
882 884 # update state
883 885 state['current'] = [node]
884 886 hbisect.save_state(repo, state)
885 887 status = ui.system(command, environ={'HG_NODE': hex(node)})
886 888 if status == 125:
887 889 transition = "skip"
888 890 elif status == 0:
889 891 transition = "good"
890 892 # status < 0 means process was killed
891 893 elif status == 127:
892 894 raise error.Abort(_("failed to execute %s") % command)
893 895 elif status < 0:
894 896 raise error.Abort(_("%s killed") % command)
895 897 else:
896 898 transition = "bad"
897 899 ctx = scmutil.revsingle(repo, rev, node)
898 900 rev = None # clear for future iterations
899 901 state[transition].append(ctx.node())
900 902 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
901 903 check_state(state, interactive=False)
902 904 # bisect
903 905 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
904 906 # update to next check
905 907 node = nodes[0]
906 908 if not noupdate:
907 909 cmdutil.bailifchanged(repo)
908 910 hg.clean(repo, node, show_stats=False)
909 911 finally:
910 912 state['current'] = [node]
911 913 hbisect.save_state(repo, state)
912 914 print_result(nodes, bgood)
913 915 return
914 916
915 917 # update state
916 918
917 919 if rev:
918 920 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
919 921 else:
920 922 nodes = [repo.lookup('.')]
921 923
922 924 if good or bad or skip:
923 925 if good:
924 926 state['good'] += nodes
925 927 elif bad:
926 928 state['bad'] += nodes
927 929 elif skip:
928 930 state['skip'] += nodes
929 931 hbisect.save_state(repo, state)
930 932
931 933 if not check_state(state):
932 934 return
933 935
934 936 # actually bisect
935 937 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
936 938 if extend:
937 939 if not changesets:
938 940 extendnode = extendbisectrange(nodes, good)
939 941 if extendnode is not None:
940 942 ui.write(_("Extending search to changeset %d:%s\n")
941 943 % (extendnode.rev(), extendnode))
942 944 state['current'] = [extendnode.node()]
943 945 hbisect.save_state(repo, state)
944 946 if noupdate:
945 947 return
946 948 cmdutil.bailifchanged(repo)
947 949 return hg.clean(repo, extendnode.node())
948 950 raise error.Abort(_("nothing to extend"))
949 951
950 952 if changesets == 0:
951 953 print_result(nodes, good)
952 954 else:
953 955 assert len(nodes) == 1 # only a single node can be tested next
954 956 node = nodes[0]
955 957 # compute the approximate number of remaining tests
956 958 tests, size = 0, 2
957 959 while size <= changesets:
958 960 tests, size = tests + 1, size * 2
959 961 rev = repo.changelog.rev(node)
960 962 ui.write(_("Testing changeset %d:%s "
961 963 "(%d changesets remaining, ~%d tests)\n")
962 964 % (rev, short(node), changesets, tests))
963 965 state['current'] = [node]
964 966 hbisect.save_state(repo, state)
965 967 if not noupdate:
966 968 cmdutil.bailifchanged(repo)
967 969 return hg.clean(repo, node)
968 970
969 971 @command('bookmarks|bookmark',
970 972 [('f', 'force', False, _('force')),
971 973 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
972 974 ('d', 'delete', False, _('delete a given bookmark')),
973 975 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
974 976 ('i', 'inactive', False, _('mark a bookmark inactive')),
975 977 ] + formatteropts,
976 978 _('hg bookmarks [OPTIONS]... [NAME]...'))
977 979 def bookmark(ui, repo, *names, **opts):
978 980 '''create a new bookmark or list existing bookmarks
979 981
980 982 Bookmarks are labels on changesets to help track lines of development.
981 983 Bookmarks are unversioned and can be moved, renamed and deleted.
982 984 Deleting or moving a bookmark has no effect on the associated changesets.
983 985
984 986 Creating or updating to a bookmark causes it to be marked as 'active'.
985 987 The active bookmark is indicated with a '*'.
986 988 When a commit is made, the active bookmark will advance to the new commit.
987 989 A plain :hg:`update` will also advance an active bookmark, if possible.
988 990 Updating away from a bookmark will cause it to be deactivated.
989 991
990 992 Bookmarks can be pushed and pulled between repositories (see
991 993 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
992 994 diverged, a new 'divergent bookmark' of the form 'name@path' will
993 995 be created. Using :hg:`merge` will resolve the divergence.
994 996
995 997 A bookmark named '@' has the special property that :hg:`clone` will
996 998 check it out by default if it exists.
997 999
998 1000 .. container:: verbose
999 1001
1000 1002 Examples:
1001 1003
1002 1004 - create an active bookmark for a new line of development::
1003 1005
1004 1006 hg book new-feature
1005 1007
1006 1008 - create an inactive bookmark as a place marker::
1007 1009
1008 1010 hg book -i reviewed
1009 1011
1010 1012 - create an inactive bookmark on another changeset::
1011 1013
1012 1014 hg book -r .^ tested
1013 1015
1014 1016 - rename bookmark turkey to dinner::
1015 1017
1016 1018 hg book -m turkey dinner
1017 1019
1018 1020 - move the '@' bookmark from another branch::
1019 1021
1020 1022 hg book -f @
1021 1023 '''
1022 1024 force = opts.get('force')
1023 1025 rev = opts.get('rev')
1024 1026 delete = opts.get('delete')
1025 1027 rename = opts.get('rename')
1026 1028 inactive = opts.get('inactive')
1027 1029
1028 1030 def checkformat(mark):
1029 1031 mark = mark.strip()
1030 1032 if not mark:
1031 1033 raise error.Abort(_("bookmark names cannot consist entirely of "
1032 1034 "whitespace"))
1033 1035 scmutil.checknewlabel(repo, mark, 'bookmark')
1034 1036 return mark
1035 1037
1036 1038 def checkconflict(repo, mark, cur, force=False, target=None):
1037 1039 if mark in marks and not force:
1038 1040 if target:
1039 1041 if marks[mark] == target and target == cur:
1040 1042 # re-activating a bookmark
1041 1043 return
1042 1044 anc = repo.changelog.ancestors([repo[target].rev()])
1043 1045 bmctx = repo[marks[mark]]
1044 1046 divs = [repo[b].node() for b in marks
1045 1047 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
1046 1048
1047 1049 # allow resolving a single divergent bookmark even if moving
1048 1050 # the bookmark across branches when a revision is specified
1049 1051 # that contains a divergent bookmark
1050 1052 if bmctx.rev() not in anc and target in divs:
1051 1053 bookmarks.deletedivergent(repo, [target], mark)
1052 1054 return
1053 1055
1054 1056 deletefrom = [b for b in divs
1055 1057 if repo[b].rev() in anc or b == target]
1056 1058 bookmarks.deletedivergent(repo, deletefrom, mark)
1057 1059 if bookmarks.validdest(repo, bmctx, repo[target]):
1058 1060 ui.status(_("moving bookmark '%s' forward from %s\n") %
1059 1061 (mark, short(bmctx.node())))
1060 1062 return
1061 1063 raise error.Abort(_("bookmark '%s' already exists "
1062 1064 "(use -f to force)") % mark)
1063 1065 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
1064 1066 and not force):
1065 1067 raise error.Abort(
1066 1068 _("a bookmark cannot have the name of an existing branch"))
1067 1069
1068 1070 if delete and rename:
1069 1071 raise error.Abort(_("--delete and --rename are incompatible"))
1070 1072 if delete and rev:
1071 1073 raise error.Abort(_("--rev is incompatible with --delete"))
1072 1074 if rename and rev:
1073 1075 raise error.Abort(_("--rev is incompatible with --rename"))
1074 1076 if not names and (delete or rev):
1075 1077 raise error.Abort(_("bookmark name required"))
1076 1078
1077 1079 if delete or rename or names or inactive:
1078 1080 wlock = lock = tr = None
1079 1081 try:
1080 1082 wlock = repo.wlock()
1081 1083 lock = repo.lock()
1082 1084 cur = repo.changectx('.').node()
1083 1085 marks = repo._bookmarks
1084 1086 if delete:
1085 1087 tr = repo.transaction('bookmark')
1086 1088 for mark in names:
1087 1089 if mark not in marks:
1088 1090 raise error.Abort(_("bookmark '%s' does not exist") %
1089 1091 mark)
1090 1092 if mark == repo._activebookmark:
1091 1093 bookmarks.deactivate(repo)
1092 1094 del marks[mark]
1093 1095
1094 1096 elif rename:
1095 1097 tr = repo.transaction('bookmark')
1096 1098 if not names:
1097 1099 raise error.Abort(_("new bookmark name required"))
1098 1100 elif len(names) > 1:
1099 1101 raise error.Abort(_("only one new bookmark name allowed"))
1100 1102 mark = checkformat(names[0])
1101 1103 if rename not in marks:
1102 1104 raise error.Abort(_("bookmark '%s' does not exist")
1103 1105 % rename)
1104 1106 checkconflict(repo, mark, cur, force)
1105 1107 marks[mark] = marks[rename]
1106 1108 if repo._activebookmark == rename and not inactive:
1107 1109 bookmarks.activate(repo, mark)
1108 1110 del marks[rename]
1109 1111 elif names:
1110 1112 tr = repo.transaction('bookmark')
1111 1113 newact = None
1112 1114 for mark in names:
1113 1115 mark = checkformat(mark)
1114 1116 if newact is None:
1115 1117 newact = mark
1116 1118 if inactive and mark == repo._activebookmark:
1117 1119 bookmarks.deactivate(repo)
1118 1120 return
1119 1121 tgt = cur
1120 1122 if rev:
1121 1123 tgt = scmutil.revsingle(repo, rev).node()
1122 1124 checkconflict(repo, mark, cur, force, tgt)
1123 1125 marks[mark] = tgt
1124 1126 if not inactive and cur == marks[newact] and not rev:
1125 1127 bookmarks.activate(repo, newact)
1126 1128 elif cur != tgt and newact == repo._activebookmark:
1127 1129 bookmarks.deactivate(repo)
1128 1130 elif inactive:
1129 1131 if len(marks) == 0:
1130 1132 ui.status(_("no bookmarks set\n"))
1131 1133 elif not repo._activebookmark:
1132 1134 ui.status(_("no active bookmark\n"))
1133 1135 else:
1134 1136 bookmarks.deactivate(repo)
1135 1137 if tr is not None:
1136 1138 marks.recordchange(tr)
1137 1139 tr.close()
1138 1140 finally:
1139 1141 lockmod.release(tr, lock, wlock)
1140 1142 else: # show bookmarks
1141 1143 fm = ui.formatter('bookmarks', opts)
1142 1144 hexfn = fm.hexfunc
1143 1145 marks = repo._bookmarks
1144 1146 if len(marks) == 0 and not fm:
1145 1147 ui.status(_("no bookmarks set\n"))
1146 1148 for bmark, n in sorted(marks.iteritems()):
1147 1149 active = repo._activebookmark
1148 1150 if bmark == active:
1149 1151 prefix, label = '*', activebookmarklabel
1150 1152 else:
1151 1153 prefix, label = ' ', ''
1152 1154
1153 1155 fm.startitem()
1154 1156 if not ui.quiet:
1155 1157 fm.plain(' %s ' % prefix, label=label)
1156 1158 fm.write('bookmark', '%s', bmark, label=label)
1157 1159 pad = " " * (25 - encoding.colwidth(bmark))
1158 1160 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1159 1161 repo.changelog.rev(n), hexfn(n), label=label)
1160 1162 fm.data(active=(bmark == active))
1161 1163 fm.plain('\n')
1162 1164 fm.end()
1163 1165
1164 1166 @command('branch',
1165 1167 [('f', 'force', None,
1166 1168 _('set branch name even if it shadows an existing branch')),
1167 1169 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1168 1170 _('[-fC] [NAME]'))
1169 1171 def branch(ui, repo, label=None, **opts):
1170 1172 """set or show the current branch name
1171 1173
1172 1174 .. note::
1173 1175
1174 1176 Branch names are permanent and global. Use :hg:`bookmark` to create a
1175 1177 light-weight bookmark instead. See :hg:`help glossary` for more
1176 1178 information about named branches and bookmarks.
1177 1179
1178 1180 With no argument, show the current branch name. With one argument,
1179 1181 set the working directory branch name (the branch will not exist
1180 1182 in the repository until the next commit). Standard practice
1181 1183 recommends that primary development take place on the 'default'
1182 1184 branch.
1183 1185
1184 1186 Unless -f/--force is specified, branch will not let you set a
1185 1187 branch name that already exists.
1186 1188
1187 1189 Use -C/--clean to reset the working directory branch to that of
1188 1190 the parent of the working directory, negating a previous branch
1189 1191 change.
1190 1192
1191 1193 Use the command :hg:`update` to switch to an existing branch. Use
1192 1194 :hg:`commit --close-branch` to mark this branch head as closed.
1193 1195 When all heads of a branch are closed, the branch will be
1194 1196 considered closed.
1195 1197
1196 1198 Returns 0 on success.
1197 1199 """
1198 1200 if label:
1199 1201 label = label.strip()
1200 1202
1201 1203 if not opts.get('clean') and not label:
1202 1204 ui.write("%s\n" % repo.dirstate.branch())
1203 1205 return
1204 1206
1205 1207 with repo.wlock():
1206 1208 if opts.get('clean'):
1207 1209 label = repo[None].p1().branch()
1208 1210 repo.dirstate.setbranch(label)
1209 1211 ui.status(_('reset working directory to branch %s\n') % label)
1210 1212 elif label:
1211 1213 if not opts.get('force') and label in repo.branchmap():
1212 1214 if label not in [p.branch() for p in repo[None].parents()]:
1213 1215 raise error.Abort(_('a branch of the same name already'
1214 1216 ' exists'),
1215 1217 # i18n: "it" refers to an existing branch
1216 1218 hint=_("use 'hg update' to switch to it"))
1217 1219 scmutil.checknewlabel(repo, label, 'branch')
1218 1220 repo.dirstate.setbranch(label)
1219 1221 ui.status(_('marked working directory as branch %s\n') % label)
1220 1222
1221 1223 # find any open named branches aside from default
1222 1224 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1223 1225 if n != "default" and not c]
1224 1226 if not others:
1225 1227 ui.status(_('(branches are permanent and global, '
1226 1228 'did you want a bookmark?)\n'))
1227 1229
1228 1230 @command('branches',
1229 1231 [('a', 'active', False,
1230 1232 _('show only branches that have unmerged heads (DEPRECATED)')),
1231 1233 ('c', 'closed', False, _('show normal and closed branches')),
1232 1234 ] + formatteropts,
1233 1235 _('[-ac]'))
1234 1236 def branches(ui, repo, active=False, closed=False, **opts):
1235 1237 """list repository named branches
1236 1238
1237 1239 List the repository's named branches, indicating which ones are
1238 1240 inactive. If -c/--closed is specified, also list branches which have
1239 1241 been marked closed (see :hg:`commit --close-branch`).
1240 1242
1241 1243 Use the command :hg:`update` to switch to an existing branch.
1242 1244
1243 1245 Returns 0.
1244 1246 """
1245 1247
1246 1248 fm = ui.formatter('branches', opts)
1247 1249 hexfunc = fm.hexfunc
1248 1250
1249 1251 allheads = set(repo.heads())
1250 1252 branches = []
1251 1253 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1252 1254 isactive = not isclosed and bool(set(heads) & allheads)
1253 1255 branches.append((tag, repo[tip], isactive, not isclosed))
1254 1256 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1255 1257 reverse=True)
1256 1258
1257 1259 for tag, ctx, isactive, isopen in branches:
1258 1260 if active and not isactive:
1259 1261 continue
1260 1262 if isactive:
1261 1263 label = 'branches.active'
1262 1264 notice = ''
1263 1265 elif not isopen:
1264 1266 if not closed:
1265 1267 continue
1266 1268 label = 'branches.closed'
1267 1269 notice = _(' (closed)')
1268 1270 else:
1269 1271 label = 'branches.inactive'
1270 1272 notice = _(' (inactive)')
1271 1273 current = (tag == repo.dirstate.branch())
1272 1274 if current:
1273 1275 label = 'branches.current'
1274 1276
1275 1277 fm.startitem()
1276 1278 fm.write('branch', '%s', tag, label=label)
1277 1279 rev = ctx.rev()
1278 1280 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1279 1281 fmt = ' ' * padsize + ' %d:%s'
1280 1282 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1281 1283 label='log.changeset changeset.%s' % ctx.phasestr())
1282 1284 fm.data(active=isactive, closed=not isopen, current=current)
1283 1285 if not ui.quiet:
1284 1286 fm.plain(notice)
1285 1287 fm.plain('\n')
1286 1288 fm.end()
1287 1289
1288 1290 @command('bundle',
1289 1291 [('f', 'force', None, _('run even when the destination is unrelated')),
1290 1292 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1291 1293 _('REV')),
1292 1294 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1293 1295 _('BRANCH')),
1294 1296 ('', 'base', [],
1295 1297 _('a base changeset assumed to be available at the destination'),
1296 1298 _('REV')),
1297 1299 ('a', 'all', None, _('bundle all changesets in the repository')),
1298 1300 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1299 1301 ] + remoteopts,
1300 1302 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1301 1303 def bundle(ui, repo, fname, dest=None, **opts):
1302 1304 """create a changegroup file
1303 1305
1304 1306 Generate a changegroup file collecting changesets to be added
1305 1307 to a repository.
1306 1308
1307 1309 To create a bundle containing all changesets, use -a/--all
1308 1310 (or --base null). Otherwise, hg assumes the destination will have
1309 1311 all the nodes you specify with --base parameters. Otherwise, hg
1310 1312 will assume the repository has all the nodes in destination, or
1311 1313 default-push/default if no destination is specified.
1312 1314
1313 1315 You can change bundle format with the -t/--type option. You can
1314 1316 specify a compression, a bundle version or both using a dash
1315 1317 (comp-version). The available compression methods are: none, bzip2,
1316 1318 and gzip (by default, bundles are compressed using bzip2). The
1317 1319 available formats are: v1, v2 (default to most suitable).
1318 1320
1319 1321 The bundle file can then be transferred using conventional means
1320 1322 and applied to another repository with the unbundle or pull
1321 1323 command. This is useful when direct push and pull are not
1322 1324 available or when exporting an entire repository is undesirable.
1323 1325
1324 1326 Applying bundles preserves all changeset contents including
1325 1327 permissions, copy/rename information, and revision history.
1326 1328
1327 1329 Returns 0 on success, 1 if no changes found.
1328 1330 """
1329 1331 revs = None
1330 1332 if 'rev' in opts:
1331 1333 revstrings = opts['rev']
1332 1334 revs = scmutil.revrange(repo, revstrings)
1333 1335 if revstrings and not revs:
1334 1336 raise error.Abort(_('no commits to bundle'))
1335 1337
1336 1338 bundletype = opts.get('type', 'bzip2').lower()
1337 1339 try:
1338 1340 bcompression, cgversion, params = exchange.parsebundlespec(
1339 1341 repo, bundletype, strict=False)
1340 1342 except error.UnsupportedBundleSpecification as e:
1341 1343 raise error.Abort(str(e),
1342 1344 hint=_('see "hg help bundle" for supported '
1343 1345 'values for --type'))
1344 1346
1345 1347 # Packed bundles are a pseudo bundle format for now.
1346 1348 if cgversion == 's1':
1347 1349 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1348 1350 hint=_('use "hg debugcreatestreamclonebundle"'))
1349 1351
1350 1352 if opts.get('all'):
1351 1353 if dest:
1352 1354 raise error.Abort(_("--all is incompatible with specifying "
1353 1355 "a destination"))
1354 1356 if opts.get('base'):
1355 1357 ui.warn(_("ignoring --base because --all was specified\n"))
1356 1358 base = ['null']
1357 1359 else:
1358 1360 base = scmutil.revrange(repo, opts.get('base'))
1359 1361 # TODO: get desired bundlecaps from command line.
1360 1362 bundlecaps = None
1361 1363 if base:
1362 1364 if dest:
1363 1365 raise error.Abort(_("--base is incompatible with specifying "
1364 1366 "a destination"))
1365 1367 common = [repo.lookup(rev) for rev in base]
1366 1368 heads = revs and map(repo.lookup, revs) or revs
1367 1369 cg = changegroup.getchangegroup(repo, 'bundle', heads=heads,
1368 1370 common=common, bundlecaps=bundlecaps,
1369 1371 version=cgversion)
1370 1372 outgoing = None
1371 1373 else:
1372 1374 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1373 1375 dest, branches = hg.parseurl(dest, opts.get('branch'))
1374 1376 other = hg.peer(repo, opts, dest)
1375 1377 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1376 1378 heads = revs and map(repo.lookup, revs) or revs
1377 1379 outgoing = discovery.findcommonoutgoing(repo, other,
1378 1380 onlyheads=heads,
1379 1381 force=opts.get('force'),
1380 1382 portable=True)
1381 1383 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1382 1384 bundlecaps, version=cgversion)
1383 1385 if not cg:
1384 1386 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1385 1387 return 1
1386 1388
1387 1389 if cgversion == '01': #bundle1
1388 1390 if bcompression is None:
1389 1391 bcompression = 'UN'
1390 1392 bversion = 'HG10' + bcompression
1391 1393 bcompression = None
1392 1394 else:
1393 1395 assert cgversion == '02'
1394 1396 bversion = 'HG20'
1395 1397
1396 1398
1397 1399 changegroup.writebundle(ui, cg, fname, bversion, compression=bcompression)
1398 1400
1399 1401 @command('cat',
1400 1402 [('o', 'output', '',
1401 1403 _('print output to file with formatted name'), _('FORMAT')),
1402 1404 ('r', 'rev', '', _('print the given revision'), _('REV')),
1403 1405 ('', 'decode', None, _('apply any matching decode filter')),
1404 1406 ] + walkopts,
1405 1407 _('[OPTION]... FILE...'),
1406 1408 inferrepo=True)
1407 1409 def cat(ui, repo, file1, *pats, **opts):
1408 1410 """output the current or given revision of files
1409 1411
1410 1412 Print the specified files as they were at the given revision. If
1411 1413 no revision is given, the parent of the working directory is used.
1412 1414
1413 1415 Output may be to a file, in which case the name of the file is
1414 1416 given using a format string. The formatting rules as follows:
1415 1417
1416 1418 :``%%``: literal "%" character
1417 1419 :``%s``: basename of file being printed
1418 1420 :``%d``: dirname of file being printed, or '.' if in repository root
1419 1421 :``%p``: root-relative path name of file being printed
1420 1422 :``%H``: changeset hash (40 hexadecimal digits)
1421 1423 :``%R``: changeset revision number
1422 1424 :``%h``: short-form changeset hash (12 hexadecimal digits)
1423 1425 :``%r``: zero-padded changeset revision number
1424 1426 :``%b``: basename of the exporting repository
1425 1427
1426 1428 Returns 0 on success.
1427 1429 """
1428 1430 ctx = scmutil.revsingle(repo, opts.get('rev'))
1429 1431 m = scmutil.match(ctx, (file1,) + pats, opts)
1430 1432
1431 1433 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1432 1434
1433 1435 @command('^clone',
1434 1436 [('U', 'noupdate', None, _('the clone will include an empty working '
1435 1437 'directory (only a repository)')),
1436 1438 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1437 1439 _('REV')),
1438 1440 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1439 1441 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1440 1442 ('', 'pull', None, _('use pull protocol to copy metadata')),
1441 1443 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1442 1444 ] + remoteopts,
1443 1445 _('[OPTION]... SOURCE [DEST]'),
1444 1446 norepo=True)
1445 1447 def clone(ui, source, dest=None, **opts):
1446 1448 """make a copy of an existing repository
1447 1449
1448 1450 Create a copy of an existing repository in a new directory.
1449 1451
1450 1452 If no destination directory name is specified, it defaults to the
1451 1453 basename of the source.
1452 1454
1453 1455 The location of the source is added to the new repository's
1454 1456 ``.hg/hgrc`` file, as the default to be used for future pulls.
1455 1457
1456 1458 Only local paths and ``ssh://`` URLs are supported as
1457 1459 destinations. For ``ssh://`` destinations, no working directory or
1458 1460 ``.hg/hgrc`` will be created on the remote side.
1459 1461
1460 1462 If the source repository has a bookmark called '@' set, that
1461 1463 revision will be checked out in the new repository by default.
1462 1464
1463 1465 To check out a particular version, use -u/--update, or
1464 1466 -U/--noupdate to create a clone with no working directory.
1465 1467
1466 1468 To pull only a subset of changesets, specify one or more revisions
1467 1469 identifiers with -r/--rev or branches with -b/--branch. The
1468 1470 resulting clone will contain only the specified changesets and
1469 1471 their ancestors. These options (or 'clone src#rev dest') imply
1470 1472 --pull, even for local source repositories.
1471 1473
1472 1474 .. note::
1473 1475
1474 1476 Specifying a tag will include the tagged changeset but not the
1475 1477 changeset containing the tag.
1476 1478
1477 1479 .. container:: verbose
1478 1480
1479 1481 For efficiency, hardlinks are used for cloning whenever the
1480 1482 source and destination are on the same filesystem (note this
1481 1483 applies only to the repository data, not to the working
1482 1484 directory). Some filesystems, such as AFS, implement hardlinking
1483 1485 incorrectly, but do not report errors. In these cases, use the
1484 1486 --pull option to avoid hardlinking.
1485 1487
1486 1488 In some cases, you can clone repositories and the working
1487 1489 directory using full hardlinks with ::
1488 1490
1489 1491 $ cp -al REPO REPOCLONE
1490 1492
1491 1493 This is the fastest way to clone, but it is not always safe. The
1492 1494 operation is not atomic (making sure REPO is not modified during
1493 1495 the operation is up to you) and you have to make sure your
1494 1496 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1495 1497 so). Also, this is not compatible with certain extensions that
1496 1498 place their metadata under the .hg directory, such as mq.
1497 1499
1498 1500 Mercurial will update the working directory to the first applicable
1499 1501 revision from this list:
1500 1502
1501 1503 a) null if -U or the source repository has no changesets
1502 1504 b) if -u . and the source repository is local, the first parent of
1503 1505 the source repository's working directory
1504 1506 c) the changeset specified with -u (if a branch name, this means the
1505 1507 latest head of that branch)
1506 1508 d) the changeset specified with -r
1507 1509 e) the tipmost head specified with -b
1508 1510 f) the tipmost head specified with the url#branch source syntax
1509 1511 g) the revision marked with the '@' bookmark, if present
1510 1512 h) the tipmost head of the default branch
1511 1513 i) tip
1512 1514
1513 1515 When cloning from servers that support it, Mercurial may fetch
1514 1516 pre-generated data from a server-advertised URL. When this is done,
1515 1517 hooks operating on incoming changesets and changegroups may fire twice,
1516 1518 once for the bundle fetched from the URL and another for any additional
1517 1519 data not fetched from this URL. In addition, if an error occurs, the
1518 1520 repository may be rolled back to a partial clone. This behavior may
1519 1521 change in future releases. See :hg:`help -e clonebundles` for more.
1520 1522
1521 1523 Examples:
1522 1524
1523 1525 - clone a remote repository to a new directory named hg/::
1524 1526
1525 1527 hg clone http://selenic.com/hg
1526 1528
1527 1529 - create a lightweight local clone::
1528 1530
1529 1531 hg clone project/ project-feature/
1530 1532
1531 1533 - clone from an absolute path on an ssh server (note double-slash)::
1532 1534
1533 1535 hg clone ssh://user@server//home/projects/alpha/
1534 1536
1535 1537 - do a high-speed clone over a LAN while checking out a
1536 1538 specified version::
1537 1539
1538 1540 hg clone --uncompressed http://server/repo -u 1.5
1539 1541
1540 1542 - create a repository without changesets after a particular revision::
1541 1543
1542 1544 hg clone -r 04e544 experimental/ good/
1543 1545
1544 1546 - clone (and track) a particular named branch::
1545 1547
1546 1548 hg clone http://selenic.com/hg#stable
1547 1549
1548 1550 See :hg:`help urls` for details on specifying URLs.
1549 1551
1550 1552 Returns 0 on success.
1551 1553 """
1552 1554 if opts.get('noupdate') and opts.get('updaterev'):
1553 1555 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1554 1556
1555 1557 r = hg.clone(ui, opts, source, dest,
1556 1558 pull=opts.get('pull'),
1557 1559 stream=opts.get('uncompressed'),
1558 1560 rev=opts.get('rev'),
1559 1561 update=opts.get('updaterev') or not opts.get('noupdate'),
1560 1562 branch=opts.get('branch'),
1561 1563 shareopts=opts.get('shareopts'))
1562 1564
1563 1565 return r is None
1564 1566
1565 1567 @command('^commit|ci',
1566 1568 [('A', 'addremove', None,
1567 1569 _('mark new/missing files as added/removed before committing')),
1568 1570 ('', 'close-branch', None,
1569 1571 _('mark a branch head as closed')),
1570 1572 ('', 'amend', None, _('amend the parent of the working directory')),
1571 1573 ('s', 'secret', None, _('use the secret phase for committing')),
1572 1574 ('e', 'edit', None, _('invoke editor on commit messages')),
1573 1575 ('i', 'interactive', None, _('use interactive mode')),
1574 1576 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1575 1577 _('[OPTION]... [FILE]...'),
1576 1578 inferrepo=True)
1577 1579 def commit(ui, repo, *pats, **opts):
1578 1580 """commit the specified files or all outstanding changes
1579 1581
1580 1582 Commit changes to the given files into the repository. Unlike a
1581 1583 centralized SCM, this operation is a local operation. See
1582 1584 :hg:`push` for a way to actively distribute your changes.
1583 1585
1584 1586 If a list of files is omitted, all changes reported by :hg:`status`
1585 1587 will be committed.
1586 1588
1587 1589 If you are committing the result of a merge, do not provide any
1588 1590 filenames or -I/-X filters.
1589 1591
1590 1592 If no commit message is specified, Mercurial starts your
1591 1593 configured editor where you can enter a message. In case your
1592 1594 commit fails, you will find a backup of your message in
1593 1595 ``.hg/last-message.txt``.
1594 1596
1595 1597 The --close-branch flag can be used to mark the current branch
1596 1598 head closed. When all heads of a branch are closed, the branch
1597 1599 will be considered closed and no longer listed.
1598 1600
1599 1601 The --amend flag can be used to amend the parent of the
1600 1602 working directory with a new commit that contains the changes
1601 1603 in the parent in addition to those currently reported by :hg:`status`,
1602 1604 if there are any. The old commit is stored in a backup bundle in
1603 1605 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1604 1606 on how to restore it).
1605 1607
1606 1608 Message, user and date are taken from the amended commit unless
1607 1609 specified. When a message isn't specified on the command line,
1608 1610 the editor will open with the message of the amended commit.
1609 1611
1610 1612 It is not possible to amend public changesets (see :hg:`help phases`)
1611 1613 or changesets that have children.
1612 1614
1613 1615 See :hg:`help dates` for a list of formats valid for -d/--date.
1614 1616
1615 1617 Returns 0 on success, 1 if nothing changed.
1616 1618
1617 1619 .. container:: verbose
1618 1620
1619 1621 Examples:
1620 1622
1621 1623 - commit all files ending in .py::
1622 1624
1623 1625 hg commit --include "set:**.py"
1624 1626
1625 1627 - commit all non-binary files::
1626 1628
1627 1629 hg commit --exclude "set:binary()"
1628 1630
1629 1631 - amend the current commit and set the date to now::
1630 1632
1631 1633 hg commit --amend --date now
1632 1634 """
1633 1635 wlock = lock = None
1634 1636 try:
1635 1637 wlock = repo.wlock()
1636 1638 lock = repo.lock()
1637 1639 return _docommit(ui, repo, *pats, **opts)
1638 1640 finally:
1639 1641 release(lock, wlock)
1640 1642
1641 1643 def _docommit(ui, repo, *pats, **opts):
1642 1644 if opts.get('interactive'):
1643 1645 opts.pop('interactive')
1644 1646 cmdutil.dorecord(ui, repo, commit, None, False,
1645 1647 cmdutil.recordfilter, *pats, **opts)
1646 1648 return
1647 1649
1648 1650 if opts.get('subrepos'):
1649 1651 if opts.get('amend'):
1650 1652 raise error.Abort(_('cannot amend with --subrepos'))
1651 1653 # Let --subrepos on the command line override config setting.
1652 1654 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1653 1655
1654 1656 cmdutil.checkunfinished(repo, commit=True)
1655 1657
1656 1658 branch = repo[None].branch()
1657 1659 bheads = repo.branchheads(branch)
1658 1660
1659 1661 extra = {}
1660 1662 if opts.get('close_branch'):
1661 1663 extra['close'] = 1
1662 1664
1663 1665 if not bheads:
1664 1666 raise error.Abort(_('can only close branch heads'))
1665 1667 elif opts.get('amend'):
1666 1668 if repo[None].parents()[0].p1().branch() != branch and \
1667 1669 repo[None].parents()[0].p2().branch() != branch:
1668 1670 raise error.Abort(_('can only close branch heads'))
1669 1671
1670 1672 if opts.get('amend'):
1671 1673 if ui.configbool('ui', 'commitsubrepos'):
1672 1674 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1673 1675
1674 1676 old = repo['.']
1675 1677 if not old.mutable():
1676 1678 raise error.Abort(_('cannot amend public changesets'))
1677 1679 if len(repo[None].parents()) > 1:
1678 1680 raise error.Abort(_('cannot amend while merging'))
1679 1681 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1680 1682 if not allowunstable and old.children():
1681 1683 raise error.Abort(_('cannot amend changeset with children'))
1682 1684
1683 1685 newextra = extra.copy()
1684 1686 newextra['branch'] = branch
1685 1687 extra = newextra
1686 1688 # commitfunc is used only for temporary amend commit by cmdutil.amend
1687 1689 def commitfunc(ui, repo, message, match, opts):
1688 1690 return repo.commit(message,
1689 1691 opts.get('user') or old.user(),
1690 1692 opts.get('date') or old.date(),
1691 1693 match,
1692 1694 extra=extra)
1693 1695
1694 1696 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1695 1697 if node == old.node():
1696 1698 ui.status(_("nothing changed\n"))
1697 1699 return 1
1698 1700 else:
1699 1701 def commitfunc(ui, repo, message, match, opts):
1700 1702 backup = ui.backupconfig('phases', 'new-commit')
1701 1703 baseui = repo.baseui
1702 1704 basebackup = baseui.backupconfig('phases', 'new-commit')
1703 1705 try:
1704 1706 if opts.get('secret'):
1705 1707 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1706 1708 # Propagate to subrepos
1707 1709 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1708 1710
1709 1711 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1710 1712 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1711 1713 return repo.commit(message, opts.get('user'), opts.get('date'),
1712 1714 match,
1713 1715 editor=editor,
1714 1716 extra=extra)
1715 1717 finally:
1716 1718 ui.restoreconfig(backup)
1717 1719 repo.baseui.restoreconfig(basebackup)
1718 1720
1719 1721
1720 1722 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1721 1723
1722 1724 if not node:
1723 1725 stat = cmdutil.postcommitstatus(repo, pats, opts)
1724 1726 if stat[3]:
1725 1727 ui.status(_("nothing changed (%d missing files, see "
1726 1728 "'hg status')\n") % len(stat[3]))
1727 1729 else:
1728 1730 ui.status(_("nothing changed\n"))
1729 1731 return 1
1730 1732
1731 1733 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1732 1734
1733 1735 @command('config|showconfig|debugconfig',
1734 1736 [('u', 'untrusted', None, _('show untrusted configuration options')),
1735 1737 ('e', 'edit', None, _('edit user config')),
1736 1738 ('l', 'local', None, _('edit repository config')),
1737 1739 ('g', 'global', None, _('edit global config'))],
1738 1740 _('[-u] [NAME]...'),
1739 1741 optionalrepo=True)
1740 1742 def config(ui, repo, *values, **opts):
1741 1743 """show combined config settings from all hgrc files
1742 1744
1743 1745 With no arguments, print names and values of all config items.
1744 1746
1745 1747 With one argument of the form section.name, print just the value
1746 1748 of that config item.
1747 1749
1748 1750 With multiple arguments, print names and values of all config
1749 1751 items with matching section names.
1750 1752
1751 1753 With --edit, start an editor on the user-level config file. With
1752 1754 --global, edit the system-wide config file. With --local, edit the
1753 1755 repository-level config file.
1754 1756
1755 1757 With --debug, the source (filename and line number) is printed
1756 1758 for each config item.
1757 1759
1758 1760 See :hg:`help config` for more information about config files.
1759 1761
1760 1762 Returns 0 on success, 1 if NAME does not exist.
1761 1763
1762 1764 """
1763 1765
1764 1766 if opts.get('edit') or opts.get('local') or opts.get('global'):
1765 1767 if opts.get('local') and opts.get('global'):
1766 1768 raise error.Abort(_("can't use --local and --global together"))
1767 1769
1768 1770 if opts.get('local'):
1769 1771 if not repo:
1770 1772 raise error.Abort(_("can't use --local outside a repository"))
1771 1773 paths = [repo.join('hgrc')]
1772 1774 elif opts.get('global'):
1773 1775 paths = scmutil.systemrcpath()
1774 1776 else:
1775 1777 paths = scmutil.userrcpath()
1776 1778
1777 1779 for f in paths:
1778 1780 if os.path.exists(f):
1779 1781 break
1780 1782 else:
1781 1783 if opts.get('global'):
1782 1784 samplehgrc = uimod.samplehgrcs['global']
1783 1785 elif opts.get('local'):
1784 1786 samplehgrc = uimod.samplehgrcs['local']
1785 1787 else:
1786 1788 samplehgrc = uimod.samplehgrcs['user']
1787 1789
1788 1790 f = paths[0]
1789 1791 fp = open(f, "w")
1790 1792 fp.write(samplehgrc)
1791 1793 fp.close()
1792 1794
1793 1795 editor = ui.geteditor()
1794 1796 ui.system("%s \"%s\"" % (editor, f),
1795 1797 onerr=error.Abort, errprefix=_("edit failed"))
1796 1798 return
1797 1799
1798 1800 for f in scmutil.rcpath():
1799 1801 ui.debug('read config from: %s\n' % f)
1800 1802 untrusted = bool(opts.get('untrusted'))
1801 1803 if values:
1802 1804 sections = [v for v in values if '.' not in v]
1803 1805 items = [v for v in values if '.' in v]
1804 1806 if len(items) > 1 or items and sections:
1805 1807 raise error.Abort(_('only one config item permitted'))
1806 1808 matched = False
1807 1809 for section, name, value in ui.walkconfig(untrusted=untrusted):
1808 1810 value = str(value).replace('\n', '\\n')
1809 1811 sectname = section + '.' + name
1810 1812 if values:
1811 1813 for v in values:
1812 1814 if v == section:
1813 1815 ui.debug('%s: ' %
1814 1816 ui.configsource(section, name, untrusted))
1815 1817 ui.write('%s=%s\n' % (sectname, value))
1816 1818 matched = True
1817 1819 elif v == sectname:
1818 1820 ui.debug('%s: ' %
1819 1821 ui.configsource(section, name, untrusted))
1820 1822 ui.write(value, '\n')
1821 1823 matched = True
1822 1824 else:
1823 1825 ui.debug('%s: ' %
1824 1826 ui.configsource(section, name, untrusted))
1825 1827 ui.write('%s=%s\n' % (sectname, value))
1826 1828 matched = True
1827 1829 if matched:
1828 1830 return 0
1829 1831 return 1
1830 1832
1831 1833 @command('copy|cp',
1832 1834 [('A', 'after', None, _('record a copy that has already occurred')),
1833 1835 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1834 1836 ] + walkopts + dryrunopts,
1835 1837 _('[OPTION]... [SOURCE]... DEST'))
1836 1838 def copy(ui, repo, *pats, **opts):
1837 1839 """mark files as copied for the next commit
1838 1840
1839 1841 Mark dest as having copies of source files. If dest is a
1840 1842 directory, copies are put in that directory. If dest is a file,
1841 1843 the source must be a single file.
1842 1844
1843 1845 By default, this command copies the contents of files as they
1844 1846 exist in the working directory. If invoked with -A/--after, the
1845 1847 operation is recorded, but no copying is performed.
1846 1848
1847 1849 This command takes effect with the next commit. To undo a copy
1848 1850 before that, see :hg:`revert`.
1849 1851
1850 1852 Returns 0 on success, 1 if errors are encountered.
1851 1853 """
1852 1854 with repo.wlock(False):
1853 1855 return cmdutil.copy(ui, repo, pats, opts)
1854 1856
1855 1857 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1856 1858 def debugancestor(ui, repo, *args):
1857 1859 """find the ancestor revision of two revisions in a given index"""
1858 1860 if len(args) == 3:
1859 1861 index, rev1, rev2 = args
1860 1862 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1861 1863 lookup = r.lookup
1862 1864 elif len(args) == 2:
1863 1865 if not repo:
1864 1866 raise error.Abort(_("there is no Mercurial repository here "
1865 1867 "(.hg not found)"))
1866 1868 rev1, rev2 = args
1867 1869 r = repo.changelog
1868 1870 lookup = repo.lookup
1869 1871 else:
1870 1872 raise error.Abort(_('either two or three arguments required'))
1871 1873 a = r.ancestor(lookup(rev1), lookup(rev2))
1872 1874 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1873 1875
1874 1876 @command('debugbuilddag',
1875 1877 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1876 1878 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1877 1879 ('n', 'new-file', None, _('add new file at each rev'))],
1878 1880 _('[OPTION]... [TEXT]'))
1879 1881 def debugbuilddag(ui, repo, text=None,
1880 1882 mergeable_file=False,
1881 1883 overwritten_file=False,
1882 1884 new_file=False):
1883 1885 """builds a repo with a given DAG from scratch in the current empty repo
1884 1886
1885 1887 The description of the DAG is read from stdin if not given on the
1886 1888 command line.
1887 1889
1888 1890 Elements:
1889 1891
1890 1892 - "+n" is a linear run of n nodes based on the current default parent
1891 1893 - "." is a single node based on the current default parent
1892 1894 - "$" resets the default parent to null (implied at the start);
1893 1895 otherwise the default parent is always the last node created
1894 1896 - "<p" sets the default parent to the backref p
1895 1897 - "*p" is a fork at parent p, which is a backref
1896 1898 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1897 1899 - "/p2" is a merge of the preceding node and p2
1898 1900 - ":tag" defines a local tag for the preceding node
1899 1901 - "@branch" sets the named branch for subsequent nodes
1900 1902 - "#...\\n" is a comment up to the end of the line
1901 1903
1902 1904 Whitespace between the above elements is ignored.
1903 1905
1904 1906 A backref is either
1905 1907
1906 1908 - a number n, which references the node curr-n, where curr is the current
1907 1909 node, or
1908 1910 - the name of a local tag you placed earlier using ":tag", or
1909 1911 - empty to denote the default parent.
1910 1912
1911 1913 All string valued-elements are either strictly alphanumeric, or must
1912 1914 be enclosed in double quotes ("..."), with "\\" as escape character.
1913 1915 """
1914 1916
1915 1917 if text is None:
1916 1918 ui.status(_("reading DAG from stdin\n"))
1917 1919 text = ui.fin.read()
1918 1920
1919 1921 cl = repo.changelog
1920 1922 if len(cl) > 0:
1921 1923 raise error.Abort(_('repository is not empty'))
1922 1924
1923 1925 # determine number of revs in DAG
1924 1926 total = 0
1925 1927 for type, data in dagparser.parsedag(text):
1926 1928 if type == 'n':
1927 1929 total += 1
1928 1930
1929 1931 if mergeable_file:
1930 1932 linesperrev = 2
1931 1933 # make a file with k lines per rev
1932 1934 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1933 1935 initialmergedlines.append("")
1934 1936
1935 1937 tags = []
1936 1938
1937 1939 lock = tr = None
1938 1940 try:
1939 1941 lock = repo.lock()
1940 1942 tr = repo.transaction("builddag")
1941 1943
1942 1944 at = -1
1943 1945 atbranch = 'default'
1944 1946 nodeids = []
1945 1947 id = 0
1946 1948 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1947 1949 for type, data in dagparser.parsedag(text):
1948 1950 if type == 'n':
1949 1951 ui.note(('node %s\n' % str(data)))
1950 1952 id, ps = data
1951 1953
1952 1954 files = []
1953 1955 fctxs = {}
1954 1956
1955 1957 p2 = None
1956 1958 if mergeable_file:
1957 1959 fn = "mf"
1958 1960 p1 = repo[ps[0]]
1959 1961 if len(ps) > 1:
1960 1962 p2 = repo[ps[1]]
1961 1963 pa = p1.ancestor(p2)
1962 1964 base, local, other = [x[fn].data() for x in (pa, p1,
1963 1965 p2)]
1964 1966 m3 = simplemerge.Merge3Text(base, local, other)
1965 1967 ml = [l.strip() for l in m3.merge_lines()]
1966 1968 ml.append("")
1967 1969 elif at > 0:
1968 1970 ml = p1[fn].data().split("\n")
1969 1971 else:
1970 1972 ml = initialmergedlines
1971 1973 ml[id * linesperrev] += " r%i" % id
1972 1974 mergedtext = "\n".join(ml)
1973 1975 files.append(fn)
1974 1976 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
1975 1977
1976 1978 if overwritten_file:
1977 1979 fn = "of"
1978 1980 files.append(fn)
1979 1981 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1980 1982
1981 1983 if new_file:
1982 1984 fn = "nf%i" % id
1983 1985 files.append(fn)
1984 1986 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1985 1987 if len(ps) > 1:
1986 1988 if not p2:
1987 1989 p2 = repo[ps[1]]
1988 1990 for fn in p2:
1989 1991 if fn.startswith("nf"):
1990 1992 files.append(fn)
1991 1993 fctxs[fn] = p2[fn]
1992 1994
1993 1995 def fctxfn(repo, cx, path):
1994 1996 return fctxs.get(path)
1995 1997
1996 1998 if len(ps) == 0 or ps[0] < 0:
1997 1999 pars = [None, None]
1998 2000 elif len(ps) == 1:
1999 2001 pars = [nodeids[ps[0]], None]
2000 2002 else:
2001 2003 pars = [nodeids[p] for p in ps]
2002 2004 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
2003 2005 date=(id, 0),
2004 2006 user="debugbuilddag",
2005 2007 extra={'branch': atbranch})
2006 2008 nodeid = repo.commitctx(cx)
2007 2009 nodeids.append(nodeid)
2008 2010 at = id
2009 2011 elif type == 'l':
2010 2012 id, name = data
2011 2013 ui.note(('tag %s\n' % name))
2012 2014 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
2013 2015 elif type == 'a':
2014 2016 ui.note(('branch %s\n' % data))
2015 2017 atbranch = data
2016 2018 ui.progress(_('building'), id, unit=_('revisions'), total=total)
2017 2019 tr.close()
2018 2020
2019 2021 if tags:
2020 2022 repo.vfs.write("localtags", "".join(tags))
2021 2023 finally:
2022 2024 ui.progress(_('building'), None)
2023 2025 release(tr, lock)
2024 2026
2025 2027 @command('debugbundle',
2026 2028 [('a', 'all', None, _('show all details')),
2027 2029 ('', 'spec', None, _('print the bundlespec of the bundle'))],
2028 2030 _('FILE'),
2029 2031 norepo=True)
2030 2032 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
2031 2033 """lists the contents of a bundle"""
2032 2034 with hg.openpath(ui, bundlepath) as f:
2033 2035 if spec:
2034 2036 spec = exchange.getbundlespec(ui, f)
2035 2037 ui.write('%s\n' % spec)
2036 2038 return
2037 2039
2038 2040 gen = exchange.readbundle(ui, f, bundlepath)
2039 2041 if isinstance(gen, bundle2.unbundle20):
2040 2042 return _debugbundle2(ui, gen, all=all, **opts)
2041 2043 if all:
2042 2044 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
2043 2045
2044 2046 def showchunks(named):
2045 2047 ui.write("\n%s\n" % named)
2046 2048 chain = None
2047 2049 while True:
2048 2050 chunkdata = gen.deltachunk(chain)
2049 2051 if not chunkdata:
2050 2052 break
2051 2053 node = chunkdata['node']
2052 2054 p1 = chunkdata['p1']
2053 2055 p2 = chunkdata['p2']
2054 2056 cs = chunkdata['cs']
2055 2057 deltabase = chunkdata['deltabase']
2056 2058 delta = chunkdata['delta']
2057 2059 ui.write("%s %s %s %s %s %s\n" %
2058 2060 (hex(node), hex(p1), hex(p2),
2059 2061 hex(cs), hex(deltabase), len(delta)))
2060 2062 chain = node
2061 2063
2062 2064 chunkdata = gen.changelogheader()
2063 2065 showchunks("changelog")
2064 2066 chunkdata = gen.manifestheader()
2065 2067 showchunks("manifest")
2066 2068 while True:
2067 2069 chunkdata = gen.filelogheader()
2068 2070 if not chunkdata:
2069 2071 break
2070 2072 fname = chunkdata['filename']
2071 2073 showchunks(fname)
2072 2074 else:
2073 2075 if isinstance(gen, bundle2.unbundle20):
2074 2076 raise error.Abort(_('use debugbundle2 for this file'))
2075 2077 chunkdata = gen.changelogheader()
2076 2078 chain = None
2077 2079 while True:
2078 2080 chunkdata = gen.deltachunk(chain)
2079 2081 if not chunkdata:
2080 2082 break
2081 2083 node = chunkdata['node']
2082 2084 ui.write("%s\n" % hex(node))
2083 2085 chain = node
2084 2086
2085 2087 def _debugbundle2(ui, gen, **opts):
2086 2088 """lists the contents of a bundle2"""
2087 2089 if not isinstance(gen, bundle2.unbundle20):
2088 2090 raise error.Abort(_('not a bundle2 file'))
2089 2091 ui.write(('Stream params: %s\n' % repr(gen.params)))
2090 2092 for part in gen.iterparts():
2091 2093 ui.write('%s -- %r\n' % (part.type, repr(part.params)))
2092 2094 if part.type == 'changegroup':
2093 2095 version = part.params.get('version', '01')
2094 2096 cg = changegroup.getunbundler(version, part, 'UN')
2095 2097 chunkdata = cg.changelogheader()
2096 2098 chain = None
2097 2099 while True:
2098 2100 chunkdata = cg.deltachunk(chain)
2099 2101 if not chunkdata:
2100 2102 break
2101 2103 node = chunkdata['node']
2102 2104 ui.write(" %s\n" % hex(node))
2103 2105 chain = node
2104 2106
2105 2107 @command('debugcreatestreamclonebundle', [], 'FILE')
2106 2108 def debugcreatestreamclonebundle(ui, repo, fname):
2107 2109 """create a stream clone bundle file
2108 2110
2109 2111 Stream bundles are special bundles that are essentially archives of
2110 2112 revlog files. They are commonly used for cloning very quickly.
2111 2113 """
2112 2114 requirements, gen = streamclone.generatebundlev1(repo)
2113 2115 changegroup.writechunks(ui, gen, fname)
2114 2116
2115 2117 ui.write(_('bundle requirements: %s\n') % ', '.join(sorted(requirements)))
2116 2118
2117 2119 @command('debugapplystreamclonebundle', [], 'FILE')
2118 2120 def debugapplystreamclonebundle(ui, repo, fname):
2119 2121 """apply a stream clone bundle file"""
2120 2122 f = hg.openpath(ui, fname)
2121 2123 gen = exchange.readbundle(ui, f, fname)
2122 2124 gen.apply(repo)
2123 2125
2124 2126 @command('debugcheckstate', [], '')
2125 2127 def debugcheckstate(ui, repo):
2126 2128 """validate the correctness of the current dirstate"""
2127 2129 parent1, parent2 = repo.dirstate.parents()
2128 2130 m1 = repo[parent1].manifest()
2129 2131 m2 = repo[parent2].manifest()
2130 2132 errors = 0
2131 2133 for f in repo.dirstate:
2132 2134 state = repo.dirstate[f]
2133 2135 if state in "nr" and f not in m1:
2134 2136 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
2135 2137 errors += 1
2136 2138 if state in "a" and f in m1:
2137 2139 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
2138 2140 errors += 1
2139 2141 if state in "m" and f not in m1 and f not in m2:
2140 2142 ui.warn(_("%s in state %s, but not in either manifest\n") %
2141 2143 (f, state))
2142 2144 errors += 1
2143 2145 for f in m1:
2144 2146 state = repo.dirstate[f]
2145 2147 if state not in "nrm":
2146 2148 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
2147 2149 errors += 1
2148 2150 if errors:
2149 2151 error = _(".hg/dirstate inconsistent with current parent's manifest")
2150 2152 raise error.Abort(error)
2151 2153
2152 2154 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
2153 2155 def debugcommands(ui, cmd='', *args):
2154 2156 """list all available commands and options"""
2155 2157 for cmd, vals in sorted(table.iteritems()):
2156 2158 cmd = cmd.split('|')[0].strip('^')
2157 2159 opts = ', '.join([i[1] for i in vals[1]])
2158 2160 ui.write('%s: %s\n' % (cmd, opts))
2159 2161
2160 2162 @command('debugcomplete',
2161 2163 [('o', 'options', None, _('show the command options'))],
2162 2164 _('[-o] CMD'),
2163 2165 norepo=True)
2164 2166 def debugcomplete(ui, cmd='', **opts):
2165 2167 """returns the completion list associated with the given command"""
2166 2168
2167 2169 if opts.get('options'):
2168 2170 options = []
2169 2171 otables = [globalopts]
2170 2172 if cmd:
2171 2173 aliases, entry = cmdutil.findcmd(cmd, table, False)
2172 2174 otables.append(entry[1])
2173 2175 for t in otables:
2174 2176 for o in t:
2175 2177 if "(DEPRECATED)" in o[3]:
2176 2178 continue
2177 2179 if o[0]:
2178 2180 options.append('-%s' % o[0])
2179 2181 options.append('--%s' % o[1])
2180 2182 ui.write("%s\n" % "\n".join(options))
2181 2183 return
2182 2184
2183 2185 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2184 2186 if ui.verbose:
2185 2187 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
2186 2188 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
2187 2189
2188 2190 @command('debugdag',
2189 2191 [('t', 'tags', None, _('use tags as labels')),
2190 2192 ('b', 'branches', None, _('annotate with branch names')),
2191 2193 ('', 'dots', None, _('use dots for runs')),
2192 2194 ('s', 'spaces', None, _('separate elements by spaces'))],
2193 2195 _('[OPTION]... [FILE [REV]...]'),
2194 2196 optionalrepo=True)
2195 2197 def debugdag(ui, repo, file_=None, *revs, **opts):
2196 2198 """format the changelog or an index DAG as a concise textual description
2197 2199
2198 2200 If you pass a revlog index, the revlog's DAG is emitted. If you list
2199 2201 revision numbers, they get labeled in the output as rN.
2200 2202
2201 2203 Otherwise, the changelog DAG of the current repo is emitted.
2202 2204 """
2203 2205 spaces = opts.get('spaces')
2204 2206 dots = opts.get('dots')
2205 2207 if file_:
2206 2208 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2207 2209 revs = set((int(r) for r in revs))
2208 2210 def events():
2209 2211 for r in rlog:
2210 2212 yield 'n', (r, list(p for p in rlog.parentrevs(r)
2211 2213 if p != -1))
2212 2214 if r in revs:
2213 2215 yield 'l', (r, "r%i" % r)
2214 2216 elif repo:
2215 2217 cl = repo.changelog
2216 2218 tags = opts.get('tags')
2217 2219 branches = opts.get('branches')
2218 2220 if tags:
2219 2221 labels = {}
2220 2222 for l, n in repo.tags().items():
2221 2223 labels.setdefault(cl.rev(n), []).append(l)
2222 2224 def events():
2223 2225 b = "default"
2224 2226 for r in cl:
2225 2227 if branches:
2226 2228 newb = cl.read(cl.node(r))[5]['branch']
2227 2229 if newb != b:
2228 2230 yield 'a', newb
2229 2231 b = newb
2230 2232 yield 'n', (r, list(p for p in cl.parentrevs(r)
2231 2233 if p != -1))
2232 2234 if tags:
2233 2235 ls = labels.get(r)
2234 2236 if ls:
2235 2237 for l in ls:
2236 2238 yield 'l', (r, l)
2237 2239 else:
2238 2240 raise error.Abort(_('need repo for changelog dag'))
2239 2241
2240 2242 for line in dagparser.dagtextlines(events(),
2241 2243 addspaces=spaces,
2242 2244 wraplabels=True,
2243 2245 wrapannotations=True,
2244 2246 wrapnonlinear=dots,
2245 2247 usedots=dots,
2246 2248 maxlinewidth=70):
2247 2249 ui.write(line)
2248 2250 ui.write("\n")
2249 2251
2250 2252 @command('debugdata', debugrevlogopts, _('-c|-m|FILE REV'))
2251 2253 def debugdata(ui, repo, file_, rev=None, **opts):
2252 2254 """dump the contents of a data file revision"""
2253 2255 if opts.get('changelog') or opts.get('manifest'):
2254 2256 file_, rev = None, file_
2255 2257 elif rev is None:
2256 2258 raise error.CommandError('debugdata', _('invalid arguments'))
2257 2259 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
2258 2260 try:
2259 2261 ui.write(r.revision(r.lookup(rev)))
2260 2262 except KeyError:
2261 2263 raise error.Abort(_('invalid revision identifier %s') % rev)
2262 2264
2263 2265 @command('debugdate',
2264 2266 [('e', 'extended', None, _('try extended date formats'))],
2265 2267 _('[-e] DATE [RANGE]'),
2266 2268 norepo=True, optionalrepo=True)
2267 2269 def debugdate(ui, date, range=None, **opts):
2268 2270 """parse and display a date"""
2269 2271 if opts["extended"]:
2270 2272 d = util.parsedate(date, util.extendeddateformats)
2271 2273 else:
2272 2274 d = util.parsedate(date)
2273 2275 ui.write(("internal: %s %s\n") % d)
2274 2276 ui.write(("standard: %s\n") % util.datestr(d))
2275 2277 if range:
2276 2278 m = util.matchdate(range)
2277 2279 ui.write(("match: %s\n") % m(d[0]))
2278 2280
2279 2281 @command('debugdiscovery',
2280 2282 [('', 'old', None, _('use old-style discovery')),
2281 2283 ('', 'nonheads', None,
2282 2284 _('use old-style discovery with non-heads included')),
2283 2285 ] + remoteopts,
2284 2286 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
2285 2287 def debugdiscovery(ui, repo, remoteurl="default", **opts):
2286 2288 """runs the changeset discovery protocol in isolation"""
2287 2289 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
2288 2290 opts.get('branch'))
2289 2291 remote = hg.peer(repo, opts, remoteurl)
2290 2292 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
2291 2293
2292 2294 # make sure tests are repeatable
2293 2295 random.seed(12323)
2294 2296
2295 2297 def doit(localheads, remoteheads, remote=remote):
2296 2298 if opts.get('old'):
2297 2299 if localheads:
2298 2300 raise error.Abort('cannot use localheads with old style '
2299 2301 'discovery')
2300 2302 if not util.safehasattr(remote, 'branches'):
2301 2303 # enable in-client legacy support
2302 2304 remote = localrepo.locallegacypeer(remote.local())
2303 2305 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2304 2306 force=True)
2305 2307 common = set(common)
2306 2308 if not opts.get('nonheads'):
2307 2309 ui.write(("unpruned common: %s\n") %
2308 2310 " ".join(sorted(short(n) for n in common)))
2309 2311 dag = dagutil.revlogdag(repo.changelog)
2310 2312 all = dag.ancestorset(dag.internalizeall(common))
2311 2313 common = dag.externalizeall(dag.headsetofconnecteds(all))
2312 2314 else:
2313 2315 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2314 2316 common = set(common)
2315 2317 rheads = set(hds)
2316 2318 lheads = set(repo.heads())
2317 2319 ui.write(("common heads: %s\n") %
2318 2320 " ".join(sorted(short(n) for n in common)))
2319 2321 if lheads <= common:
2320 2322 ui.write(("local is subset\n"))
2321 2323 elif rheads <= common:
2322 2324 ui.write(("remote is subset\n"))
2323 2325
2324 2326 serverlogs = opts.get('serverlog')
2325 2327 if serverlogs:
2326 2328 for filename in serverlogs:
2327 2329 with open(filename, 'r') as logfile:
2328 2330 line = logfile.readline()
2329 2331 while line:
2330 2332 parts = line.strip().split(';')
2331 2333 op = parts[1]
2332 2334 if op == 'cg':
2333 2335 pass
2334 2336 elif op == 'cgss':
2335 2337 doit(parts[2].split(' '), parts[3].split(' '))
2336 2338 elif op == 'unb':
2337 2339 doit(parts[3].split(' '), parts[2].split(' '))
2338 2340 line = logfile.readline()
2339 2341 else:
2340 2342 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2341 2343 opts.get('remote_head'))
2342 2344 localrevs = opts.get('local_head')
2343 2345 doit(localrevs, remoterevs)
2344 2346
2345 2347 @command('debugextensions', formatteropts, [], norepo=True)
2346 2348 def debugextensions(ui, **opts):
2347 2349 '''show information about active extensions'''
2348 2350 exts = extensions.extensions(ui)
2349 2351 fm = ui.formatter('debugextensions', opts)
2350 2352 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
2351 2353 extsource = extmod.__file__
2352 2354 exttestedwith = getattr(extmod, 'testedwith', None)
2353 2355 if exttestedwith is not None:
2354 2356 exttestedwith = exttestedwith.split()
2355 2357 extbuglink = getattr(extmod, 'buglink', None)
2356 2358
2357 2359 fm.startitem()
2358 2360
2359 2361 if ui.quiet or ui.verbose:
2360 2362 fm.write('name', '%s\n', extname)
2361 2363 else:
2362 2364 fm.write('name', '%s', extname)
2363 2365 if not exttestedwith:
2364 2366 fm.plain(_(' (untested!)\n'))
2365 2367 else:
2366 2368 if exttestedwith == ['internal'] or \
2367 2369 util.version() in exttestedwith:
2368 2370 fm.plain('\n')
2369 2371 else:
2370 2372 lasttestedversion = exttestedwith[-1]
2371 2373 fm.plain(' (%s!)\n' % lasttestedversion)
2372 2374
2373 2375 fm.condwrite(ui.verbose and extsource, 'source',
2374 2376 _(' location: %s\n'), extsource or "")
2375 2377
2376 2378 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
2377 2379 _(' tested with: %s\n'), ' '.join(exttestedwith or []))
2378 2380
2379 2381 fm.condwrite(ui.verbose and extbuglink, 'buglink',
2380 2382 _(' bug reporting: %s\n'), extbuglink or "")
2381 2383
2382 2384 fm.end()
2383 2385
2384 2386 @command('debugfileset',
2385 2387 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2386 2388 _('[-r REV] FILESPEC'))
2387 2389 def debugfileset(ui, repo, expr, **opts):
2388 2390 '''parse and apply a fileset specification'''
2389 2391 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2390 2392 if ui.verbose:
2391 2393 tree = fileset.parse(expr)
2392 2394 ui.note(fileset.prettyformat(tree), "\n")
2393 2395
2394 2396 for f in ctx.getfileset(expr):
2395 2397 ui.write("%s\n" % f)
2396 2398
2397 2399 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2398 2400 def debugfsinfo(ui, path="."):
2399 2401 """show information detected about current filesystem"""
2400 2402 util.writefile('.debugfsinfo', '')
2401 2403 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2402 2404 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2403 2405 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2404 2406 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2405 2407 and 'yes' or 'no'))
2406 2408 os.unlink('.debugfsinfo')
2407 2409
2408 2410 @command('debuggetbundle',
2409 2411 [('H', 'head', [], _('id of head node'), _('ID')),
2410 2412 ('C', 'common', [], _('id of common node'), _('ID')),
2411 2413 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2412 2414 _('REPO FILE [-H|-C ID]...'),
2413 2415 norepo=True)
2414 2416 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2415 2417 """retrieves a bundle from a repo
2416 2418
2417 2419 Every ID must be a full-length hex node id string. Saves the bundle to the
2418 2420 given file.
2419 2421 """
2420 2422 repo = hg.peer(ui, opts, repopath)
2421 2423 if not repo.capable('getbundle'):
2422 2424 raise error.Abort("getbundle() not supported by target repository")
2423 2425 args = {}
2424 2426 if common:
2425 2427 args['common'] = [bin(s) for s in common]
2426 2428 if head:
2427 2429 args['heads'] = [bin(s) for s in head]
2428 2430 # TODO: get desired bundlecaps from command line.
2429 2431 args['bundlecaps'] = None
2430 2432 bundle = repo.getbundle('debug', **args)
2431 2433
2432 2434 bundletype = opts.get('type', 'bzip2').lower()
2433 2435 btypes = {'none': 'HG10UN',
2434 2436 'bzip2': 'HG10BZ',
2435 2437 'gzip': 'HG10GZ',
2436 2438 'bundle2': 'HG20'}
2437 2439 bundletype = btypes.get(bundletype)
2438 2440 if bundletype not in changegroup.bundletypes:
2439 2441 raise error.Abort(_('unknown bundle type specified with --type'))
2440 2442 changegroup.writebundle(ui, bundle, bundlepath, bundletype)
2441 2443
2442 2444 @command('debugignore', [], '[FILE]')
2443 2445 def debugignore(ui, repo, *files, **opts):
2444 2446 """display the combined ignore pattern and information about ignored files
2445 2447
2446 2448 With no argument display the combined ignore pattern.
2447 2449
2448 2450 Given space separated file names, shows if the given file is ignored and
2449 2451 if so, show the ignore rule (file and line number) that matched it.
2450 2452 """
2451 2453 ignore = repo.dirstate._ignore
2452 2454 if not files:
2453 2455 # Show all the patterns
2454 2456 includepat = getattr(ignore, 'includepat', None)
2455 2457 if includepat is not None:
2456 2458 ui.write("%s\n" % includepat)
2457 2459 else:
2458 2460 raise error.Abort(_("no ignore patterns found"))
2459 2461 else:
2460 2462 for f in files:
2461 2463 ignored = None
2462 2464 ignoredata = None
2463 2465 if f != '.':
2464 2466 if ignore(f):
2465 2467 ignored = f
2466 2468 ignoredata = repo.dirstate._ignorefileandline(f)
2467 2469 else:
2468 2470 for p in util.finddirs(f):
2469 2471 if ignore(p):
2470 2472 ignored = p
2471 2473 ignoredata = repo.dirstate._ignorefileandline(p)
2472 2474 break
2473 2475 if ignored:
2474 2476 if ignored == f:
2475 2477 ui.write("%s is ignored\n" % f)
2476 2478 else:
2477 2479 ui.write("%s is ignored because of containing folder %s\n"
2478 2480 % (f, ignored))
2479 2481 ignorefile, lineno, line = ignoredata
2480 2482 ui.write("(ignore rule in %s, line %d: '%s')\n"
2481 2483 % (ignorefile, lineno, line))
2482 2484 else:
2483 2485 ui.write("%s is not ignored\n" % f)
2484 2486
2485 2487 @command('debugindex', debugrevlogopts +
2486 2488 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2487 2489 _('[-f FORMAT] -c|-m|FILE'),
2488 2490 optionalrepo=True)
2489 2491 def debugindex(ui, repo, file_=None, **opts):
2490 2492 """dump the contents of an index file"""
2491 2493 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2492 2494 format = opts.get('format', 0)
2493 2495 if format not in (0, 1):
2494 2496 raise error.Abort(_("unknown format %d") % format)
2495 2497
2496 2498 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2497 2499 if generaldelta:
2498 2500 basehdr = ' delta'
2499 2501 else:
2500 2502 basehdr = ' base'
2501 2503
2502 2504 if ui.debugflag:
2503 2505 shortfn = hex
2504 2506 else:
2505 2507 shortfn = short
2506 2508
2507 2509 # There might not be anything in r, so have a sane default
2508 2510 idlen = 12
2509 2511 for i in r:
2510 2512 idlen = len(shortfn(r.node(i)))
2511 2513 break
2512 2514
2513 2515 if format == 0:
2514 2516 ui.write(" rev offset length " + basehdr + " linkrev"
2515 2517 " %s %s p2\n" % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2516 2518 elif format == 1:
2517 2519 ui.write(" rev flag offset length"
2518 2520 " size " + basehdr + " link p1 p2"
2519 2521 " %s\n" % "nodeid".rjust(idlen))
2520 2522
2521 2523 for i in r:
2522 2524 node = r.node(i)
2523 2525 if generaldelta:
2524 2526 base = r.deltaparent(i)
2525 2527 else:
2526 2528 base = r.chainbase(i)
2527 2529 if format == 0:
2528 2530 try:
2529 2531 pp = r.parents(node)
2530 2532 except Exception:
2531 2533 pp = [nullid, nullid]
2532 2534 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2533 2535 i, r.start(i), r.length(i), base, r.linkrev(i),
2534 2536 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2535 2537 elif format == 1:
2536 2538 pr = r.parentrevs(i)
2537 2539 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2538 2540 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2539 2541 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2540 2542
2541 2543 @command('debugindexdot', debugrevlogopts,
2542 2544 _('-c|-m|FILE'), optionalrepo=True)
2543 2545 def debugindexdot(ui, repo, file_=None, **opts):
2544 2546 """dump an index DAG as a graphviz dot file"""
2545 2547 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
2546 2548 ui.write(("digraph G {\n"))
2547 2549 for i in r:
2548 2550 node = r.node(i)
2549 2551 pp = r.parents(node)
2550 2552 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2551 2553 if pp[1] != nullid:
2552 2554 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2553 2555 ui.write("}\n")
2554 2556
2555 2557 @command('debugdeltachain',
2556 2558 debugrevlogopts + formatteropts,
2557 2559 _('-c|-m|FILE'),
2558 2560 optionalrepo=True)
2559 2561 def debugdeltachain(ui, repo, file_=None, **opts):
2560 2562 """dump information about delta chains in a revlog
2561 2563
2562 2564 Output can be templatized. Available template keywords are:
2563 2565
2564 2566 rev revision number
2565 2567 chainid delta chain identifier (numbered by unique base)
2566 2568 chainlen delta chain length to this revision
2567 2569 prevrev previous revision in delta chain
2568 2570 deltatype role of delta / how it was computed
2569 2571 compsize compressed size of revision
2570 2572 uncompsize uncompressed size of revision
2571 2573 chainsize total size of compressed revisions in chain
2572 2574 chainratio total chain size divided by uncompressed revision size
2573 2575 (new delta chains typically start at ratio 2.00)
2574 2576 lindist linear distance from base revision in delta chain to end
2575 2577 of this revision
2576 2578 extradist total size of revisions not part of this delta chain from
2577 2579 base of delta chain to end of this revision; a measurement
2578 2580 of how much extra data we need to read/seek across to read
2579 2581 the delta chain for this revision
2580 2582 extraratio extradist divided by chainsize; another representation of
2581 2583 how much unrelated data is needed to load this delta chain
2582 2584 """
2583 2585 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
2584 2586 index = r.index
2585 2587 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2586 2588
2587 2589 def revinfo(rev):
2588 2590 e = index[rev]
2589 2591 compsize = e[1]
2590 2592 uncompsize = e[2]
2591 2593 chainsize = 0
2592 2594
2593 2595 if generaldelta:
2594 2596 if e[3] == e[5]:
2595 2597 deltatype = 'p1'
2596 2598 elif e[3] == e[6]:
2597 2599 deltatype = 'p2'
2598 2600 elif e[3] == rev - 1:
2599 2601 deltatype = 'prev'
2600 2602 elif e[3] == rev:
2601 2603 deltatype = 'base'
2602 2604 else:
2603 2605 deltatype = 'other'
2604 2606 else:
2605 2607 if e[3] == rev:
2606 2608 deltatype = 'base'
2607 2609 else:
2608 2610 deltatype = 'prev'
2609 2611
2610 2612 chain = r._deltachain(rev)[0]
2611 2613 for iterrev in chain:
2612 2614 e = index[iterrev]
2613 2615 chainsize += e[1]
2614 2616
2615 2617 return compsize, uncompsize, deltatype, chain, chainsize
2616 2618
2617 2619 fm = ui.formatter('debugdeltachain', opts)
2618 2620
2619 2621 fm.plain(' rev chain# chainlen prev delta '
2620 2622 'size rawsize chainsize ratio lindist extradist '
2621 2623 'extraratio\n')
2622 2624
2623 2625 chainbases = {}
2624 2626 for rev in r:
2625 2627 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
2626 2628 chainbase = chain[0]
2627 2629 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
2628 2630 basestart = r.start(chainbase)
2629 2631 revstart = r.start(rev)
2630 2632 lineardist = revstart + comp - basestart
2631 2633 extradist = lineardist - chainsize
2632 2634 try:
2633 2635 prevrev = chain[-2]
2634 2636 except IndexError:
2635 2637 prevrev = -1
2636 2638
2637 2639 chainratio = float(chainsize) / float(uncomp)
2638 2640 extraratio = float(extradist) / float(chainsize)
2639 2641
2640 2642 fm.startitem()
2641 2643 fm.write('rev chainid chainlen prevrev deltatype compsize '
2642 2644 'uncompsize chainsize chainratio lindist extradist '
2643 2645 'extraratio',
2644 2646 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f\n',
2645 2647 rev, chainid, len(chain), prevrev, deltatype, comp,
2646 2648 uncomp, chainsize, chainratio, lineardist, extradist,
2647 2649 extraratio,
2648 2650 rev=rev, chainid=chainid, chainlen=len(chain),
2649 2651 prevrev=prevrev, deltatype=deltatype, compsize=comp,
2650 2652 uncompsize=uncomp, chainsize=chainsize,
2651 2653 chainratio=chainratio, lindist=lineardist,
2652 2654 extradist=extradist, extraratio=extraratio)
2653 2655
2654 2656 fm.end()
2655 2657
2656 2658 @command('debuginstall', [], '', norepo=True)
2657 2659 def debuginstall(ui):
2658 2660 '''test Mercurial installation
2659 2661
2660 2662 Returns 0 on success.
2661 2663 '''
2662 2664
2663 2665 def writetemp(contents):
2664 2666 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2665 2667 f = os.fdopen(fd, "wb")
2666 2668 f.write(contents)
2667 2669 f.close()
2668 2670 return name
2669 2671
2670 2672 problems = 0
2671 2673
2672 2674 # encoding
2673 2675 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2674 2676 try:
2675 2677 encoding.fromlocal("test")
2676 2678 except error.Abort as inst:
2677 2679 ui.write(" %s\n" % inst)
2678 2680 ui.write(_(" (check that your locale is properly set)\n"))
2679 2681 problems += 1
2680 2682
2681 2683 # Python
2682 2684 ui.status(_("checking Python executable (%s)\n") % sys.executable)
2683 2685 ui.status(_("checking Python version (%s)\n")
2684 2686 % ("%s.%s.%s" % sys.version_info[:3]))
2685 2687 ui.status(_("checking Python lib (%s)...\n")
2686 2688 % os.path.dirname(os.__file__))
2687 2689
2688 2690 # compiled modules
2689 2691 ui.status(_("checking installed modules (%s)...\n")
2690 2692 % os.path.dirname(__file__))
2691 2693 try:
2692 2694 import bdiff, mpatch, base85, osutil
2693 2695 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2694 2696 except Exception as inst:
2695 2697 ui.write(" %s\n" % inst)
2696 2698 ui.write(_(" One or more extensions could not be found"))
2697 2699 ui.write(_(" (check that you compiled the extensions)\n"))
2698 2700 problems += 1
2699 2701
2700 2702 # templates
2701 2703 import templater
2702 2704 p = templater.templatepaths()
2703 2705 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2704 2706 if p:
2705 2707 m = templater.templatepath("map-cmdline.default")
2706 2708 if m:
2707 2709 # template found, check if it is working
2708 2710 try:
2709 2711 templater.templater(m)
2710 2712 except Exception as inst:
2711 2713 ui.write(" %s\n" % inst)
2712 2714 p = None
2713 2715 else:
2714 2716 ui.write(_(" template 'default' not found\n"))
2715 2717 p = None
2716 2718 else:
2717 2719 ui.write(_(" no template directories found\n"))
2718 2720 if not p:
2719 2721 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2720 2722 problems += 1
2721 2723
2722 2724 # editor
2723 2725 ui.status(_("checking commit editor...\n"))
2724 2726 editor = ui.geteditor()
2725 2727 editor = util.expandpath(editor)
2726 2728 cmdpath = util.findexe(shlex.split(editor)[0])
2727 2729 if not cmdpath:
2728 2730 if editor == 'vi':
2729 2731 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2730 2732 ui.write(_(" (specify a commit editor in your configuration"
2731 2733 " file)\n"))
2732 2734 else:
2733 2735 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2734 2736 ui.write(_(" (specify a commit editor in your configuration"
2735 2737 " file)\n"))
2736 2738 problems += 1
2737 2739
2738 2740 # check username
2739 2741 ui.status(_("checking username...\n"))
2740 2742 try:
2741 2743 ui.username()
2742 2744 except error.Abort as e:
2743 2745 ui.write(" %s\n" % e)
2744 2746 ui.write(_(" (specify a username in your configuration file)\n"))
2745 2747 problems += 1
2746 2748
2747 2749 if not problems:
2748 2750 ui.status(_("no problems detected\n"))
2749 2751 else:
2750 2752 ui.write(_("%s problems detected,"
2751 2753 " please check your install!\n") % problems)
2752 2754
2753 2755 return problems
2754 2756
2755 2757 @command('debugknown', [], _('REPO ID...'), norepo=True)
2756 2758 def debugknown(ui, repopath, *ids, **opts):
2757 2759 """test whether node ids are known to a repo
2758 2760
2759 2761 Every ID must be a full-length hex node id string. Returns a list of 0s
2760 2762 and 1s indicating unknown/known.
2761 2763 """
2762 2764 repo = hg.peer(ui, opts, repopath)
2763 2765 if not repo.capable('known'):
2764 2766 raise error.Abort("known() not supported by target repository")
2765 2767 flags = repo.known([bin(s) for s in ids])
2766 2768 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2767 2769
2768 2770 @command('debuglabelcomplete', [], _('LABEL...'))
2769 2771 def debuglabelcomplete(ui, repo, *args):
2770 2772 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2771 2773 debugnamecomplete(ui, repo, *args)
2772 2774
2773 2775 @command('debugmergestate', [], '')
2774 2776 def debugmergestate(ui, repo, *args):
2775 2777 """print merge state
2776 2778
2777 2779 Use --verbose to print out information about whether v1 or v2 merge state
2778 2780 was chosen."""
2779 2781 def _hashornull(h):
2780 2782 if h == nullhex:
2781 2783 return 'null'
2782 2784 else:
2783 2785 return h
2784 2786
2785 2787 def printrecords(version):
2786 2788 ui.write(('* version %s records\n') % version)
2787 2789 if version == 1:
2788 2790 records = v1records
2789 2791 else:
2790 2792 records = v2records
2791 2793
2792 2794 for rtype, record in records:
2793 2795 # pretty print some record types
2794 2796 if rtype == 'L':
2795 2797 ui.write(('local: %s\n') % record)
2796 2798 elif rtype == 'O':
2797 2799 ui.write(('other: %s\n') % record)
2798 2800 elif rtype == 'm':
2799 2801 driver, mdstate = record.split('\0', 1)
2800 2802 ui.write(('merge driver: %s (state "%s")\n')
2801 2803 % (driver, mdstate))
2802 2804 elif rtype in 'FDC':
2803 2805 r = record.split('\0')
2804 2806 f, state, hash, lfile, afile, anode, ofile = r[0:7]
2805 2807 if version == 1:
2806 2808 onode = 'not stored in v1 format'
2807 2809 flags = r[7]
2808 2810 else:
2809 2811 onode, flags = r[7:9]
2810 2812 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
2811 2813 % (f, rtype, state, _hashornull(hash)))
2812 2814 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
2813 2815 ui.write((' ancestor path: %s (node %s)\n')
2814 2816 % (afile, _hashornull(anode)))
2815 2817 ui.write((' other path: %s (node %s)\n')
2816 2818 % (ofile, _hashornull(onode)))
2817 2819 else:
2818 2820 ui.write(('unrecognized entry: %s\t%s\n')
2819 2821 % (rtype, record.replace('\0', '\t')))
2820 2822
2821 2823 # Avoid mergestate.read() since it may raise an exception for unsupported
2822 2824 # merge state records. We shouldn't be doing this, but this is OK since this
2823 2825 # command is pretty low-level.
2824 2826 ms = mergemod.mergestate(repo)
2825 2827
2826 2828 # sort so that reasonable information is on top
2827 2829 v1records = ms._readrecordsv1()
2828 2830 v2records = ms._readrecordsv2()
2829 2831 order = 'LOm'
2830 2832 def key(r):
2831 2833 idx = order.find(r[0])
2832 2834 if idx == -1:
2833 2835 return (1, r[1])
2834 2836 else:
2835 2837 return (0, idx)
2836 2838 v1records.sort(key=key)
2837 2839 v2records.sort(key=key)
2838 2840
2839 2841 if not v1records and not v2records:
2840 2842 ui.write(('no merge state found\n'))
2841 2843 elif not v2records:
2842 2844 ui.note(('no version 2 merge state\n'))
2843 2845 printrecords(1)
2844 2846 elif ms._v1v2match(v1records, v2records):
2845 2847 ui.note(('v1 and v2 states match: using v2\n'))
2846 2848 printrecords(2)
2847 2849 else:
2848 2850 ui.note(('v1 and v2 states mismatch: using v1\n'))
2849 2851 printrecords(1)
2850 2852 if ui.verbose:
2851 2853 printrecords(2)
2852 2854
2853 2855 @command('debugnamecomplete', [], _('NAME...'))
2854 2856 def debugnamecomplete(ui, repo, *args):
2855 2857 '''complete "names" - tags, open branch names, bookmark names'''
2856 2858
2857 2859 names = set()
2858 2860 # since we previously only listed open branches, we will handle that
2859 2861 # specially (after this for loop)
2860 2862 for name, ns in repo.names.iteritems():
2861 2863 if name != 'branches':
2862 2864 names.update(ns.listnames(repo))
2863 2865 names.update(tag for (tag, heads, tip, closed)
2864 2866 in repo.branchmap().iterbranches() if not closed)
2865 2867 completions = set()
2866 2868 if not args:
2867 2869 args = ['']
2868 2870 for a in args:
2869 2871 completions.update(n for n in names if n.startswith(a))
2870 2872 ui.write('\n'.join(sorted(completions)))
2871 2873 ui.write('\n')
2872 2874
2873 2875 @command('debuglocks',
2874 2876 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2875 2877 ('W', 'force-wlock', None,
2876 2878 _('free the working state lock (DANGEROUS)'))],
2877 2879 _('[OPTION]...'))
2878 2880 def debuglocks(ui, repo, **opts):
2879 2881 """show or modify state of locks
2880 2882
2881 2883 By default, this command will show which locks are held. This
2882 2884 includes the user and process holding the lock, the amount of time
2883 2885 the lock has been held, and the machine name where the process is
2884 2886 running if it's not local.
2885 2887
2886 2888 Locks protect the integrity of Mercurial's data, so should be
2887 2889 treated with care. System crashes or other interruptions may cause
2888 2890 locks to not be properly released, though Mercurial will usually
2889 2891 detect and remove such stale locks automatically.
2890 2892
2891 2893 However, detecting stale locks may not always be possible (for
2892 2894 instance, on a shared filesystem). Removing locks may also be
2893 2895 blocked by filesystem permissions.
2894 2896
2895 2897 Returns 0 if no locks are held.
2896 2898
2897 2899 """
2898 2900
2899 2901 if opts.get('force_lock'):
2900 2902 repo.svfs.unlink('lock')
2901 2903 if opts.get('force_wlock'):
2902 2904 repo.vfs.unlink('wlock')
2903 2905 if opts.get('force_lock') or opts.get('force_lock'):
2904 2906 return 0
2905 2907
2906 2908 now = time.time()
2907 2909 held = 0
2908 2910
2909 2911 def report(vfs, name, method):
2910 2912 # this causes stale locks to get reaped for more accurate reporting
2911 2913 try:
2912 2914 l = method(False)
2913 2915 except error.LockHeld:
2914 2916 l = None
2915 2917
2916 2918 if l:
2917 2919 l.release()
2918 2920 else:
2919 2921 try:
2920 2922 stat = vfs.lstat(name)
2921 2923 age = now - stat.st_mtime
2922 2924 user = util.username(stat.st_uid)
2923 2925 locker = vfs.readlock(name)
2924 2926 if ":" in locker:
2925 2927 host, pid = locker.split(':')
2926 2928 if host == socket.gethostname():
2927 2929 locker = 'user %s, process %s' % (user, pid)
2928 2930 else:
2929 2931 locker = 'user %s, process %s, host %s' \
2930 2932 % (user, pid, host)
2931 2933 ui.write("%-6s %s (%ds)\n" % (name + ":", locker, age))
2932 2934 return 1
2933 2935 except OSError as e:
2934 2936 if e.errno != errno.ENOENT:
2935 2937 raise
2936 2938
2937 2939 ui.write("%-6s free\n" % (name + ":"))
2938 2940 return 0
2939 2941
2940 2942 held += report(repo.svfs, "lock", repo.lock)
2941 2943 held += report(repo.vfs, "wlock", repo.wlock)
2942 2944
2943 2945 return held
2944 2946
2945 2947 @command('debugobsolete',
2946 2948 [('', 'flags', 0, _('markers flag')),
2947 2949 ('', 'record-parents', False,
2948 2950 _('record parent information for the precursor')),
2949 2951 ('r', 'rev', [], _('display markers relevant to REV')),
2950 2952 ] + commitopts2,
2951 2953 _('[OBSOLETED [REPLACEMENT ...]]'))
2952 2954 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2953 2955 """create arbitrary obsolete marker
2954 2956
2955 2957 With no arguments, displays the list of obsolescence markers."""
2956 2958
2957 2959 def parsenodeid(s):
2958 2960 try:
2959 2961 # We do not use revsingle/revrange functions here to accept
2960 2962 # arbitrary node identifiers, possibly not present in the
2961 2963 # local repository.
2962 2964 n = bin(s)
2963 2965 if len(n) != len(nullid):
2964 2966 raise TypeError()
2965 2967 return n
2966 2968 except TypeError:
2967 2969 raise error.Abort('changeset references must be full hexadecimal '
2968 2970 'node identifiers')
2969 2971
2970 2972 if precursor is not None:
2971 2973 if opts['rev']:
2972 2974 raise error.Abort('cannot select revision when creating marker')
2973 2975 metadata = {}
2974 2976 metadata['user'] = opts['user'] or ui.username()
2975 2977 succs = tuple(parsenodeid(succ) for succ in successors)
2976 2978 l = repo.lock()
2977 2979 try:
2978 2980 tr = repo.transaction('debugobsolete')
2979 2981 try:
2980 2982 date = opts.get('date')
2981 2983 if date:
2982 2984 date = util.parsedate(date)
2983 2985 else:
2984 2986 date = None
2985 2987 prec = parsenodeid(precursor)
2986 2988 parents = None
2987 2989 if opts['record_parents']:
2988 2990 if prec not in repo.unfiltered():
2989 2991 raise error.Abort('cannot used --record-parents on '
2990 2992 'unknown changesets')
2991 2993 parents = repo.unfiltered()[prec].parents()
2992 2994 parents = tuple(p.node() for p in parents)
2993 2995 repo.obsstore.create(tr, prec, succs, opts['flags'],
2994 2996 parents=parents, date=date,
2995 2997 metadata=metadata)
2996 2998 tr.close()
2997 2999 except ValueError as exc:
2998 3000 raise error.Abort(_('bad obsmarker input: %s') % exc)
2999 3001 finally:
3000 3002 tr.release()
3001 3003 finally:
3002 3004 l.release()
3003 3005 else:
3004 3006 if opts['rev']:
3005 3007 revs = scmutil.revrange(repo, opts['rev'])
3006 3008 nodes = [repo[r].node() for r in revs]
3007 3009 markers = list(obsolete.getmarkers(repo, nodes=nodes))
3008 3010 markers.sort(key=lambda x: x._data)
3009 3011 else:
3010 3012 markers = obsolete.getmarkers(repo)
3011 3013
3012 3014 for m in markers:
3013 3015 cmdutil.showmarker(ui, m)
3014 3016
3015 3017 @command('debugpathcomplete',
3016 3018 [('f', 'full', None, _('complete an entire path')),
3017 3019 ('n', 'normal', None, _('show only normal files')),
3018 3020 ('a', 'added', None, _('show only added files')),
3019 3021 ('r', 'removed', None, _('show only removed files'))],
3020 3022 _('FILESPEC...'))
3021 3023 def debugpathcomplete(ui, repo, *specs, **opts):
3022 3024 '''complete part or all of a tracked path
3023 3025
3024 3026 This command supports shells that offer path name completion. It
3025 3027 currently completes only files already known to the dirstate.
3026 3028
3027 3029 Completion extends only to the next path segment unless
3028 3030 --full is specified, in which case entire paths are used.'''
3029 3031
3030 3032 def complete(path, acceptable):
3031 3033 dirstate = repo.dirstate
3032 3034 spec = os.path.normpath(os.path.join(os.getcwd(), path))
3033 3035 rootdir = repo.root + os.sep
3034 3036 if spec != repo.root and not spec.startswith(rootdir):
3035 3037 return [], []
3036 3038 if os.path.isdir(spec):
3037 3039 spec += '/'
3038 3040 spec = spec[len(rootdir):]
3039 3041 fixpaths = os.sep != '/'
3040 3042 if fixpaths:
3041 3043 spec = spec.replace(os.sep, '/')
3042 3044 speclen = len(spec)
3043 3045 fullpaths = opts['full']
3044 3046 files, dirs = set(), set()
3045 3047 adddir, addfile = dirs.add, files.add
3046 3048 for f, st in dirstate.iteritems():
3047 3049 if f.startswith(spec) and st[0] in acceptable:
3048 3050 if fixpaths:
3049 3051 f = f.replace('/', os.sep)
3050 3052 if fullpaths:
3051 3053 addfile(f)
3052 3054 continue
3053 3055 s = f.find(os.sep, speclen)
3054 3056 if s >= 0:
3055 3057 adddir(f[:s])
3056 3058 else:
3057 3059 addfile(f)
3058 3060 return files, dirs
3059 3061
3060 3062 acceptable = ''
3061 3063 if opts['normal']:
3062 3064 acceptable += 'nm'
3063 3065 if opts['added']:
3064 3066 acceptable += 'a'
3065 3067 if opts['removed']:
3066 3068 acceptable += 'r'
3067 3069 cwd = repo.getcwd()
3068 3070 if not specs:
3069 3071 specs = ['.']
3070 3072
3071 3073 files, dirs = set(), set()
3072 3074 for spec in specs:
3073 3075 f, d = complete(spec, acceptable or 'nmar')
3074 3076 files.update(f)
3075 3077 dirs.update(d)
3076 3078 files.update(dirs)
3077 3079 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
3078 3080 ui.write('\n')
3079 3081
3080 3082 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
3081 3083 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
3082 3084 '''access the pushkey key/value protocol
3083 3085
3084 3086 With two args, list the keys in the given namespace.
3085 3087
3086 3088 With five args, set a key to new if it currently is set to old.
3087 3089 Reports success or failure.
3088 3090 '''
3089 3091
3090 3092 target = hg.peer(ui, {}, repopath)
3091 3093 if keyinfo:
3092 3094 key, old, new = keyinfo
3093 3095 r = target.pushkey(namespace, key, old, new)
3094 3096 ui.status(str(r) + '\n')
3095 3097 return not r
3096 3098 else:
3097 3099 for k, v in sorted(target.listkeys(namespace).iteritems()):
3098 3100 ui.write("%s\t%s\n" % (k.encode('string-escape'),
3099 3101 v.encode('string-escape')))
3100 3102
3101 3103 @command('debugpvec', [], _('A B'))
3102 3104 def debugpvec(ui, repo, a, b=None):
3103 3105 ca = scmutil.revsingle(repo, a)
3104 3106 cb = scmutil.revsingle(repo, b)
3105 3107 pa = pvec.ctxpvec(ca)
3106 3108 pb = pvec.ctxpvec(cb)
3107 3109 if pa == pb:
3108 3110 rel = "="
3109 3111 elif pa > pb:
3110 3112 rel = ">"
3111 3113 elif pa < pb:
3112 3114 rel = "<"
3113 3115 elif pa | pb:
3114 3116 rel = "|"
3115 3117 ui.write(_("a: %s\n") % pa)
3116 3118 ui.write(_("b: %s\n") % pb)
3117 3119 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
3118 3120 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
3119 3121 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
3120 3122 pa.distance(pb), rel))
3121 3123
3122 3124 @command('debugrebuilddirstate|debugrebuildstate',
3123 3125 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
3124 3126 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
3125 3127 'the working copy parent')),
3126 3128 ],
3127 3129 _('[-r REV]'))
3128 3130 def debugrebuilddirstate(ui, repo, rev, **opts):
3129 3131 """rebuild the dirstate as it would look like for the given revision
3130 3132
3131 3133 If no revision is specified the first current parent will be used.
3132 3134
3133 3135 The dirstate will be set to the files of the given revision.
3134 3136 The actual working directory content or existing dirstate
3135 3137 information such as adds or removes is not considered.
3136 3138
3137 3139 ``minimal`` will only rebuild the dirstate status for files that claim to be
3138 3140 tracked but are not in the parent manifest, or that exist in the parent
3139 3141 manifest but are not in the dirstate. It will not change adds, removes, or
3140 3142 modified files that are in the working copy parent.
3141 3143
3142 3144 One use of this command is to make the next :hg:`status` invocation
3143 3145 check the actual file content.
3144 3146 """
3145 3147 ctx = scmutil.revsingle(repo, rev)
3146 3148 with repo.wlock():
3147 3149 dirstate = repo.dirstate
3148 3150 changedfiles = None
3149 3151 # See command doc for what minimal does.
3150 3152 if opts.get('minimal'):
3151 3153 manifestfiles = set(ctx.manifest().keys())
3152 3154 dirstatefiles = set(dirstate)
3153 3155 manifestonly = manifestfiles - dirstatefiles
3154 3156 dsonly = dirstatefiles - manifestfiles
3155 3157 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
3156 3158 changedfiles = manifestonly | dsnotadded
3157 3159
3158 3160 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
3159 3161
3160 3162 @command('debugrebuildfncache', [], '')
3161 3163 def debugrebuildfncache(ui, repo):
3162 3164 """rebuild the fncache file"""
3163 3165 repair.rebuildfncache(ui, repo)
3164 3166
3165 3167 @command('debugrename',
3166 3168 [('r', 'rev', '', _('revision to debug'), _('REV'))],
3167 3169 _('[-r REV] FILE'))
3168 3170 def debugrename(ui, repo, file1, *pats, **opts):
3169 3171 """dump rename information"""
3170 3172
3171 3173 ctx = scmutil.revsingle(repo, opts.get('rev'))
3172 3174 m = scmutil.match(ctx, (file1,) + pats, opts)
3173 3175 for abs in ctx.walk(m):
3174 3176 fctx = ctx[abs]
3175 3177 o = fctx.filelog().renamed(fctx.filenode())
3176 3178 rel = m.rel(abs)
3177 3179 if o:
3178 3180 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
3179 3181 else:
3180 3182 ui.write(_("%s not renamed\n") % rel)
3181 3183
3182 3184 @command('debugrevlog', debugrevlogopts +
3183 3185 [('d', 'dump', False, _('dump index data'))],
3184 3186 _('-c|-m|FILE'),
3185 3187 optionalrepo=True)
3186 3188 def debugrevlog(ui, repo, file_=None, **opts):
3187 3189 """show data and statistics about a revlog"""
3188 3190 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
3189 3191
3190 3192 if opts.get("dump"):
3191 3193 numrevs = len(r)
3192 3194 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
3193 3195 " rawsize totalsize compression heads chainlen\n")
3194 3196 ts = 0
3195 3197 heads = set()
3196 3198
3197 3199 for rev in xrange(numrevs):
3198 3200 dbase = r.deltaparent(rev)
3199 3201 if dbase == -1:
3200 3202 dbase = rev
3201 3203 cbase = r.chainbase(rev)
3202 3204 clen = r.chainlen(rev)
3203 3205 p1, p2 = r.parentrevs(rev)
3204 3206 rs = r.rawsize(rev)
3205 3207 ts = ts + rs
3206 3208 heads -= set(r.parentrevs(rev))
3207 3209 heads.add(rev)
3208 3210 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
3209 3211 "%11d %5d %8d\n" %
3210 3212 (rev, p1, p2, r.start(rev), r.end(rev),
3211 3213 r.start(dbase), r.start(cbase),
3212 3214 r.start(p1), r.start(p2),
3213 3215 rs, ts, ts / r.end(rev), len(heads), clen))
3214 3216 return 0
3215 3217
3216 3218 v = r.version
3217 3219 format = v & 0xFFFF
3218 3220 flags = []
3219 3221 gdelta = False
3220 3222 if v & revlog.REVLOGNGINLINEDATA:
3221 3223 flags.append('inline')
3222 3224 if v & revlog.REVLOGGENERALDELTA:
3223 3225 gdelta = True
3224 3226 flags.append('generaldelta')
3225 3227 if not flags:
3226 3228 flags = ['(none)']
3227 3229
3228 3230 nummerges = 0
3229 3231 numfull = 0
3230 3232 numprev = 0
3231 3233 nump1 = 0
3232 3234 nump2 = 0
3233 3235 numother = 0
3234 3236 nump1prev = 0
3235 3237 nump2prev = 0
3236 3238 chainlengths = []
3237 3239
3238 3240 datasize = [None, 0, 0L]
3239 3241 fullsize = [None, 0, 0L]
3240 3242 deltasize = [None, 0, 0L]
3241 3243
3242 3244 def addsize(size, l):
3243 3245 if l[0] is None or size < l[0]:
3244 3246 l[0] = size
3245 3247 if size > l[1]:
3246 3248 l[1] = size
3247 3249 l[2] += size
3248 3250
3249 3251 numrevs = len(r)
3250 3252 for rev in xrange(numrevs):
3251 3253 p1, p2 = r.parentrevs(rev)
3252 3254 delta = r.deltaparent(rev)
3253 3255 if format > 0:
3254 3256 addsize(r.rawsize(rev), datasize)
3255 3257 if p2 != nullrev:
3256 3258 nummerges += 1
3257 3259 size = r.length(rev)
3258 3260 if delta == nullrev:
3259 3261 chainlengths.append(0)
3260 3262 numfull += 1
3261 3263 addsize(size, fullsize)
3262 3264 else:
3263 3265 chainlengths.append(chainlengths[delta] + 1)
3264 3266 addsize(size, deltasize)
3265 3267 if delta == rev - 1:
3266 3268 numprev += 1
3267 3269 if delta == p1:
3268 3270 nump1prev += 1
3269 3271 elif delta == p2:
3270 3272 nump2prev += 1
3271 3273 elif delta == p1:
3272 3274 nump1 += 1
3273 3275 elif delta == p2:
3274 3276 nump2 += 1
3275 3277 elif delta != nullrev:
3276 3278 numother += 1
3277 3279
3278 3280 # Adjust size min value for empty cases
3279 3281 for size in (datasize, fullsize, deltasize):
3280 3282 if size[0] is None:
3281 3283 size[0] = 0
3282 3284
3283 3285 numdeltas = numrevs - numfull
3284 3286 numoprev = numprev - nump1prev - nump2prev
3285 3287 totalrawsize = datasize[2]
3286 3288 datasize[2] /= numrevs
3287 3289 fulltotal = fullsize[2]
3288 3290 fullsize[2] /= numfull
3289 3291 deltatotal = deltasize[2]
3290 3292 if numrevs - numfull > 0:
3291 3293 deltasize[2] /= numrevs - numfull
3292 3294 totalsize = fulltotal + deltatotal
3293 3295 avgchainlen = sum(chainlengths) / numrevs
3294 3296 maxchainlen = max(chainlengths)
3295 3297 compratio = 1
3296 3298 if totalsize:
3297 3299 compratio = totalrawsize / totalsize
3298 3300
3299 3301 basedfmtstr = '%%%dd\n'
3300 3302 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
3301 3303
3302 3304 def dfmtstr(max):
3303 3305 return basedfmtstr % len(str(max))
3304 3306 def pcfmtstr(max, padding=0):
3305 3307 return basepcfmtstr % (len(str(max)), ' ' * padding)
3306 3308
3307 3309 def pcfmt(value, total):
3308 3310 if total:
3309 3311 return (value, 100 * float(value) / total)
3310 3312 else:
3311 3313 return value, 100.0
3312 3314
3313 3315 ui.write(('format : %d\n') % format)
3314 3316 ui.write(('flags : %s\n') % ', '.join(flags))
3315 3317
3316 3318 ui.write('\n')
3317 3319 fmt = pcfmtstr(totalsize)
3318 3320 fmt2 = dfmtstr(totalsize)
3319 3321 ui.write(('revisions : ') + fmt2 % numrevs)
3320 3322 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
3321 3323 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
3322 3324 ui.write(('revisions : ') + fmt2 % numrevs)
3323 3325 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
3324 3326 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
3325 3327 ui.write(('revision size : ') + fmt2 % totalsize)
3326 3328 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
3327 3329 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
3328 3330
3329 3331 ui.write('\n')
3330 3332 fmt = dfmtstr(max(avgchainlen, compratio))
3331 3333 ui.write(('avg chain length : ') + fmt % avgchainlen)
3332 3334 ui.write(('max chain length : ') + fmt % maxchainlen)
3333 3335 ui.write(('compression ratio : ') + fmt % compratio)
3334 3336
3335 3337 if format > 0:
3336 3338 ui.write('\n')
3337 3339 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
3338 3340 % tuple(datasize))
3339 3341 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
3340 3342 % tuple(fullsize))
3341 3343 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
3342 3344 % tuple(deltasize))
3343 3345
3344 3346 if numdeltas > 0:
3345 3347 ui.write('\n')
3346 3348 fmt = pcfmtstr(numdeltas)
3347 3349 fmt2 = pcfmtstr(numdeltas, 4)
3348 3350 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
3349 3351 if numprev > 0:
3350 3352 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
3351 3353 numprev))
3352 3354 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
3353 3355 numprev))
3354 3356 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
3355 3357 numprev))
3356 3358 if gdelta:
3357 3359 ui.write(('deltas against p1 : ')
3358 3360 + fmt % pcfmt(nump1, numdeltas))
3359 3361 ui.write(('deltas against p2 : ')
3360 3362 + fmt % pcfmt(nump2, numdeltas))
3361 3363 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
3362 3364 numdeltas))
3363 3365
3364 3366 @command('debugrevspec',
3365 3367 [('', 'optimize', None, _('print parsed tree after optimizing'))],
3366 3368 ('REVSPEC'))
3367 3369 def debugrevspec(ui, repo, expr, **opts):
3368 3370 """parse and apply a revision specification
3369 3371
3370 3372 Use --verbose to print the parsed tree before and after aliases
3371 3373 expansion.
3372 3374 """
3373 3375 if ui.verbose:
3374 3376 tree = revset.parse(expr, lookup=repo.__contains__)
3375 3377 ui.note(revset.prettyformat(tree), "\n")
3376 3378 newtree = revset.findaliases(ui, tree)
3377 3379 if newtree != tree:
3378 3380 ui.note(revset.prettyformat(newtree), "\n")
3379 3381 tree = newtree
3380 3382 newtree = revset.foldconcat(tree)
3381 3383 if newtree != tree:
3382 3384 ui.note(revset.prettyformat(newtree), "\n")
3383 3385 if opts["optimize"]:
3384 3386 weight, optimizedtree = revset.optimize(newtree, True)
3385 3387 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
3386 3388 func = revset.match(ui, expr, repo)
3387 3389 revs = func(repo)
3388 3390 if ui.verbose:
3389 3391 ui.note("* set:\n", revset.prettyformatset(revs), "\n")
3390 3392 for c in revs:
3391 3393 ui.write("%s\n" % c)
3392 3394
3393 3395 @command('debugsetparents', [], _('REV1 [REV2]'))
3394 3396 def debugsetparents(ui, repo, rev1, rev2=None):
3395 3397 """manually set the parents of the current working directory
3396 3398
3397 3399 This is useful for writing repository conversion tools, but should
3398 3400 be used with care. For example, neither the working directory nor the
3399 3401 dirstate is updated, so file status may be incorrect after running this
3400 3402 command.
3401 3403
3402 3404 Returns 0 on success.
3403 3405 """
3404 3406
3405 3407 r1 = scmutil.revsingle(repo, rev1).node()
3406 3408 r2 = scmutil.revsingle(repo, rev2, 'null').node()
3407 3409
3408 3410 with repo.wlock():
3409 3411 repo.dirstate.beginparentchange()
3410 3412 repo.setparents(r1, r2)
3411 3413 repo.dirstate.endparentchange()
3412 3414
3413 3415 @command('debugdirstate|debugstate',
3414 3416 [('', 'nodates', None, _('do not display the saved mtime')),
3415 3417 ('', 'datesort', None, _('sort by saved mtime'))],
3416 3418 _('[OPTION]...'))
3417 3419 def debugstate(ui, repo, **opts):
3418 3420 """show the contents of the current dirstate"""
3419 3421
3420 3422 nodates = opts.get('nodates')
3421 3423 datesort = opts.get('datesort')
3422 3424
3423 3425 timestr = ""
3424 3426 if datesort:
3425 3427 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
3426 3428 else:
3427 3429 keyfunc = None # sort by filename
3428 3430 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
3429 3431 if ent[3] == -1:
3430 3432 timestr = 'unset '
3431 3433 elif nodates:
3432 3434 timestr = 'set '
3433 3435 else:
3434 3436 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
3435 3437 time.localtime(ent[3]))
3436 3438 if ent[1] & 0o20000:
3437 3439 mode = 'lnk'
3438 3440 else:
3439 3441 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
3440 3442 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
3441 3443 for f in repo.dirstate.copies():
3442 3444 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
3443 3445
3444 3446 @command('debugsub',
3445 3447 [('r', 'rev', '',
3446 3448 _('revision to check'), _('REV'))],
3447 3449 _('[-r REV] [REV]'))
3448 3450 def debugsub(ui, repo, rev=None):
3449 3451 ctx = scmutil.revsingle(repo, rev, None)
3450 3452 for k, v in sorted(ctx.substate.items()):
3451 3453 ui.write(('path %s\n') % k)
3452 3454 ui.write((' source %s\n') % v[0])
3453 3455 ui.write((' revision %s\n') % v[1])
3454 3456
3455 3457 @command('debugsuccessorssets',
3456 3458 [],
3457 3459 _('[REV]'))
3458 3460 def debugsuccessorssets(ui, repo, *revs):
3459 3461 """show set of successors for revision
3460 3462
3461 3463 A successors set of changeset A is a consistent group of revisions that
3462 3464 succeed A. It contains non-obsolete changesets only.
3463 3465
3464 3466 In most cases a changeset A has a single successors set containing a single
3465 3467 successor (changeset A replaced by A').
3466 3468
3467 3469 A changeset that is made obsolete with no successors are called "pruned".
3468 3470 Such changesets have no successors sets at all.
3469 3471
3470 3472 A changeset that has been "split" will have a successors set containing
3471 3473 more than one successor.
3472 3474
3473 3475 A changeset that has been rewritten in multiple different ways is called
3474 3476 "divergent". Such changesets have multiple successor sets (each of which
3475 3477 may also be split, i.e. have multiple successors).
3476 3478
3477 3479 Results are displayed as follows::
3478 3480
3479 3481 <rev1>
3480 3482 <successors-1A>
3481 3483 <rev2>
3482 3484 <successors-2A>
3483 3485 <successors-2B1> <successors-2B2> <successors-2B3>
3484 3486
3485 3487 Here rev2 has two possible (i.e. divergent) successors sets. The first
3486 3488 holds one element, whereas the second holds three (i.e. the changeset has
3487 3489 been split).
3488 3490 """
3489 3491 # passed to successorssets caching computation from one call to another
3490 3492 cache = {}
3491 3493 ctx2str = str
3492 3494 node2str = short
3493 3495 if ui.debug():
3494 3496 def ctx2str(ctx):
3495 3497 return ctx.hex()
3496 3498 node2str = hex
3497 3499 for rev in scmutil.revrange(repo, revs):
3498 3500 ctx = repo[rev]
3499 3501 ui.write('%s\n'% ctx2str(ctx))
3500 3502 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
3501 3503 if succsset:
3502 3504 ui.write(' ')
3503 3505 ui.write(node2str(succsset[0]))
3504 3506 for node in succsset[1:]:
3505 3507 ui.write(' ')
3506 3508 ui.write(node2str(node))
3507 3509 ui.write('\n')
3508 3510
3509 3511 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
3510 3512 def debugwalk(ui, repo, *pats, **opts):
3511 3513 """show how files match on given patterns"""
3512 3514 m = scmutil.match(repo[None], pats, opts)
3513 3515 items = list(repo.walk(m))
3514 3516 if not items:
3515 3517 return
3516 3518 f = lambda fn: fn
3517 3519 if ui.configbool('ui', 'slash') and os.sep != '/':
3518 3520 f = lambda fn: util.normpath(fn)
3519 3521 fmt = 'f %%-%ds %%-%ds %%s' % (
3520 3522 max([len(abs) for abs in items]),
3521 3523 max([len(m.rel(abs)) for abs in items]))
3522 3524 for abs in items:
3523 3525 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3524 3526 ui.write("%s\n" % line.rstrip())
3525 3527
3526 3528 @command('debugwireargs',
3527 3529 [('', 'three', '', 'three'),
3528 3530 ('', 'four', '', 'four'),
3529 3531 ('', 'five', '', 'five'),
3530 3532 ] + remoteopts,
3531 3533 _('REPO [OPTIONS]... [ONE [TWO]]'),
3532 3534 norepo=True)
3533 3535 def debugwireargs(ui, repopath, *vals, **opts):
3534 3536 repo = hg.peer(ui, opts, repopath)
3535 3537 for opt in remoteopts:
3536 3538 del opts[opt[1]]
3537 3539 args = {}
3538 3540 for k, v in opts.iteritems():
3539 3541 if v:
3540 3542 args[k] = v
3541 3543 # run twice to check that we don't mess up the stream for the next command
3542 3544 res1 = repo.debugwireargs(*vals, **args)
3543 3545 res2 = repo.debugwireargs(*vals, **args)
3544 3546 ui.write("%s\n" % res1)
3545 3547 if res1 != res2:
3546 3548 ui.warn("%s\n" % res2)
3547 3549
3548 3550 @command('^diff',
3549 3551 [('r', 'rev', [], _('revision'), _('REV')),
3550 3552 ('c', 'change', '', _('change made by revision'), _('REV'))
3551 3553 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3552 3554 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3553 3555 inferrepo=True)
3554 3556 def diff(ui, repo, *pats, **opts):
3555 3557 """diff repository (or selected files)
3556 3558
3557 3559 Show differences between revisions for the specified files.
3558 3560
3559 3561 Differences between files are shown using the unified diff format.
3560 3562
3561 3563 .. note::
3562 3564
3563 3565 :hg:`diff` may generate unexpected results for merges, as it will
3564 3566 default to comparing against the working directory's first
3565 3567 parent changeset if no revisions are specified.
3566 3568
3567 3569 When two revision arguments are given, then changes are shown
3568 3570 between those revisions. If only one revision is specified then
3569 3571 that revision is compared to the working directory, and, when no
3570 3572 revisions are specified, the working directory files are compared
3571 3573 to its first parent.
3572 3574
3573 3575 Alternatively you can specify -c/--change with a revision to see
3574 3576 the changes in that changeset relative to its first parent.
3575 3577
3576 3578 Without the -a/--text option, diff will avoid generating diffs of
3577 3579 files it detects as binary. With -a, diff will generate a diff
3578 3580 anyway, probably with undesirable results.
3579 3581
3580 3582 Use the -g/--git option to generate diffs in the git extended diff
3581 3583 format. For more information, read :hg:`help diffs`.
3582 3584
3583 3585 .. container:: verbose
3584 3586
3585 3587 Examples:
3586 3588
3587 3589 - compare a file in the current working directory to its parent::
3588 3590
3589 3591 hg diff foo.c
3590 3592
3591 3593 - compare two historical versions of a directory, with rename info::
3592 3594
3593 3595 hg diff --git -r 1.0:1.2 lib/
3594 3596
3595 3597 - get change stats relative to the last change on some date::
3596 3598
3597 3599 hg diff --stat -r "date('may 2')"
3598 3600
3599 3601 - diff all newly-added files that contain a keyword::
3600 3602
3601 3603 hg diff "set:added() and grep(GNU)"
3602 3604
3603 3605 - compare a revision and its parents::
3604 3606
3605 3607 hg diff -c 9353 # compare against first parent
3606 3608 hg diff -r 9353^:9353 # same using revset syntax
3607 3609 hg diff -r 9353^2:9353 # compare against the second parent
3608 3610
3609 3611 Returns 0 on success.
3610 3612 """
3611 3613
3612 3614 revs = opts.get('rev')
3613 3615 change = opts.get('change')
3614 3616 stat = opts.get('stat')
3615 3617 reverse = opts.get('reverse')
3616 3618
3617 3619 if revs and change:
3618 3620 msg = _('cannot specify --rev and --change at the same time')
3619 3621 raise error.Abort(msg)
3620 3622 elif change:
3621 3623 node2 = scmutil.revsingle(repo, change, None).node()
3622 3624 node1 = repo[node2].p1().node()
3623 3625 else:
3624 3626 node1, node2 = scmutil.revpair(repo, revs)
3625 3627
3626 3628 if reverse:
3627 3629 node1, node2 = node2, node1
3628 3630
3629 3631 diffopts = patch.diffallopts(ui, opts)
3630 3632 m = scmutil.match(repo[node2], pats, opts)
3631 3633 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3632 3634 listsubrepos=opts.get('subrepos'),
3633 3635 root=opts.get('root'))
3634 3636
3635 3637 @command('^export',
3636 3638 [('o', 'output', '',
3637 3639 _('print output to file with formatted name'), _('FORMAT')),
3638 3640 ('', 'switch-parent', None, _('diff against the second parent')),
3639 3641 ('r', 'rev', [], _('revisions to export'), _('REV')),
3640 3642 ] + diffopts,
3641 3643 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3642 3644 def export(ui, repo, *changesets, **opts):
3643 3645 """dump the header and diffs for one or more changesets
3644 3646
3645 3647 Print the changeset header and diffs for one or more revisions.
3646 3648 If no revision is given, the parent of the working directory is used.
3647 3649
3648 3650 The information shown in the changeset header is: author, date,
3649 3651 branch name (if non-default), changeset hash, parent(s) and commit
3650 3652 comment.
3651 3653
3652 3654 .. note::
3653 3655
3654 3656 :hg:`export` may generate unexpected diff output for merge
3655 3657 changesets, as it will compare the merge changeset against its
3656 3658 first parent only.
3657 3659
3658 3660 Output may be to a file, in which case the name of the file is
3659 3661 given using a format string. The formatting rules are as follows:
3660 3662
3661 3663 :``%%``: literal "%" character
3662 3664 :``%H``: changeset hash (40 hexadecimal digits)
3663 3665 :``%N``: number of patches being generated
3664 3666 :``%R``: changeset revision number
3665 3667 :``%b``: basename of the exporting repository
3666 3668 :``%h``: short-form changeset hash (12 hexadecimal digits)
3667 3669 :``%m``: first line of the commit message (only alphanumeric characters)
3668 3670 :``%n``: zero-padded sequence number, starting at 1
3669 3671 :``%r``: zero-padded changeset revision number
3670 3672
3671 3673 Without the -a/--text option, export will avoid generating diffs
3672 3674 of files it detects as binary. With -a, export will generate a
3673 3675 diff anyway, probably with undesirable results.
3674 3676
3675 3677 Use the -g/--git option to generate diffs in the git extended diff
3676 3678 format. See :hg:`help diffs` for more information.
3677 3679
3678 3680 With the --switch-parent option, the diff will be against the
3679 3681 second parent. It can be useful to review a merge.
3680 3682
3681 3683 .. container:: verbose
3682 3684
3683 3685 Examples:
3684 3686
3685 3687 - use export and import to transplant a bugfix to the current
3686 3688 branch::
3687 3689
3688 3690 hg export -r 9353 | hg import -
3689 3691
3690 3692 - export all the changesets between two revisions to a file with
3691 3693 rename information::
3692 3694
3693 3695 hg export --git -r 123:150 > changes.txt
3694 3696
3695 3697 - split outgoing changes into a series of patches with
3696 3698 descriptive names::
3697 3699
3698 3700 hg export -r "outgoing()" -o "%n-%m.patch"
3699 3701
3700 3702 Returns 0 on success.
3701 3703 """
3702 3704 changesets += tuple(opts.get('rev', []))
3703 3705 if not changesets:
3704 3706 changesets = ['.']
3705 3707 revs = scmutil.revrange(repo, changesets)
3706 3708 if not revs:
3707 3709 raise error.Abort(_("export requires at least one changeset"))
3708 3710 if len(revs) > 1:
3709 3711 ui.note(_('exporting patches:\n'))
3710 3712 else:
3711 3713 ui.note(_('exporting patch:\n'))
3712 3714 cmdutil.export(repo, revs, template=opts.get('output'),
3713 3715 switch_parent=opts.get('switch_parent'),
3714 3716 opts=patch.diffallopts(ui, opts))
3715 3717
3716 3718 @command('files',
3717 3719 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3718 3720 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3719 3721 ] + walkopts + formatteropts + subrepoopts,
3720 3722 _('[OPTION]... [PATTERN]...'))
3721 3723 def files(ui, repo, *pats, **opts):
3722 3724 """list tracked files
3723 3725
3724 3726 Print files under Mercurial control in the working directory or
3725 3727 specified revision whose names match the given patterns (excluding
3726 3728 removed files).
3727 3729
3728 3730 If no patterns are given to match, this command prints the names
3729 3731 of all files under Mercurial control in the working directory.
3730 3732
3731 3733 .. container:: verbose
3732 3734
3733 3735 Examples:
3734 3736
3735 3737 - list all files under the current directory::
3736 3738
3737 3739 hg files .
3738 3740
3739 3741 - shows sizes and flags for current revision::
3740 3742
3741 3743 hg files -vr .
3742 3744
3743 3745 - list all files named README::
3744 3746
3745 3747 hg files -I "**/README"
3746 3748
3747 3749 - list all binary files::
3748 3750
3749 3751 hg files "set:binary()"
3750 3752
3751 3753 - find files containing a regular expression::
3752 3754
3753 3755 hg files "set:grep('bob')"
3754 3756
3755 3757 - search tracked file contents with xargs and grep::
3756 3758
3757 3759 hg files -0 | xargs -0 grep foo
3758 3760
3759 3761 See :hg:`help patterns` and :hg:`help filesets` for more information
3760 3762 on specifying file patterns.
3761 3763
3762 3764 Returns 0 if a match is found, 1 otherwise.
3763 3765
3764 3766 """
3765 3767 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3766 3768
3767 3769 end = '\n'
3768 3770 if opts.get('print0'):
3769 3771 end = '\0'
3770 3772 fm = ui.formatter('files', opts)
3771 3773 fmt = '%s' + end
3772 3774
3773 3775 m = scmutil.match(ctx, pats, opts)
3774 3776 ret = cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
3775 3777
3776 3778 fm.end()
3777 3779
3778 3780 return ret
3779 3781
3780 3782 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3781 3783 def forget(ui, repo, *pats, **opts):
3782 3784 """forget the specified files on the next commit
3783 3785
3784 3786 Mark the specified files so they will no longer be tracked
3785 3787 after the next commit.
3786 3788
3787 3789 This only removes files from the current branch, not from the
3788 3790 entire project history, and it does not delete them from the
3789 3791 working directory.
3790 3792
3791 3793 To delete the file from the working directory, see :hg:`remove`.
3792 3794
3793 3795 To undo a forget before the next commit, see :hg:`add`.
3794 3796
3795 3797 .. container:: verbose
3796 3798
3797 3799 Examples:
3798 3800
3799 3801 - forget newly-added binary files::
3800 3802
3801 3803 hg forget "set:added() and binary()"
3802 3804
3803 3805 - forget files that would be excluded by .hgignore::
3804 3806
3805 3807 hg forget "set:hgignore()"
3806 3808
3807 3809 Returns 0 on success.
3808 3810 """
3809 3811
3810 3812 if not pats:
3811 3813 raise error.Abort(_('no files specified'))
3812 3814
3813 3815 m = scmutil.match(repo[None], pats, opts)
3814 3816 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3815 3817 return rejected and 1 or 0
3816 3818
3817 3819 @command(
3818 3820 'graft',
3819 3821 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3820 3822 ('c', 'continue', False, _('resume interrupted graft')),
3821 3823 ('e', 'edit', False, _('invoke editor on commit messages')),
3822 3824 ('', 'log', None, _('append graft info to log message')),
3823 3825 ('f', 'force', False, _('force graft')),
3824 3826 ('D', 'currentdate', False,
3825 3827 _('record the current date as commit date')),
3826 3828 ('U', 'currentuser', False,
3827 3829 _('record the current user as committer'), _('DATE'))]
3828 3830 + commitopts2 + mergetoolopts + dryrunopts,
3829 3831 _('[OPTION]... [-r REV]... REV...'))
3830 3832 def graft(ui, repo, *revs, **opts):
3831 3833 '''copy changes from other branches onto the current branch
3832 3834
3833 3835 This command uses Mercurial's merge logic to copy individual
3834 3836 changes from other branches without merging branches in the
3835 3837 history graph. This is sometimes known as 'backporting' or
3836 3838 'cherry-picking'. By default, graft will copy user, date, and
3837 3839 description from the source changesets.
3838 3840
3839 3841 Changesets that are ancestors of the current revision, that have
3840 3842 already been grafted, or that are merges will be skipped.
3841 3843
3842 3844 If --log is specified, log messages will have a comment appended
3843 3845 of the form::
3844 3846
3845 3847 (grafted from CHANGESETHASH)
3846 3848
3847 3849 If --force is specified, revisions will be grafted even if they
3848 3850 are already ancestors of or have been grafted to the destination.
3849 3851 This is useful when the revisions have since been backed out.
3850 3852
3851 3853 If a graft merge results in conflicts, the graft process is
3852 3854 interrupted so that the current merge can be manually resolved.
3853 3855 Once all conflicts are addressed, the graft process can be
3854 3856 continued with the -c/--continue option.
3855 3857
3856 3858 .. note::
3857 3859
3858 3860 The -c/--continue option does not reapply earlier options, except
3859 3861 for --force.
3860 3862
3861 3863 .. container:: verbose
3862 3864
3863 3865 Examples:
3864 3866
3865 3867 - copy a single change to the stable branch and edit its description::
3866 3868
3867 3869 hg update stable
3868 3870 hg graft --edit 9393
3869 3871
3870 3872 - graft a range of changesets with one exception, updating dates::
3871 3873
3872 3874 hg graft -D "2085::2093 and not 2091"
3873 3875
3874 3876 - continue a graft after resolving conflicts::
3875 3877
3876 3878 hg graft -c
3877 3879
3878 3880 - show the source of a grafted changeset::
3879 3881
3880 3882 hg log --debug -r .
3881 3883
3882 3884 - show revisions sorted by date::
3883 3885
3884 3886 hg log -r 'sort(all(), date)'
3885 3887
3886 3888 See :hg:`help revisions` and :hg:`help revsets` for more about
3887 3889 specifying revisions.
3888 3890
3889 3891 Returns 0 on successful completion.
3890 3892 '''
3891 3893 with repo.wlock():
3892 3894 return _dograft(ui, repo, *revs, **opts)
3893 3895
3894 3896 def _dograft(ui, repo, *revs, **opts):
3895 3897 if revs and opts['rev']:
3896 3898 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
3897 3899 'revision ordering!\n'))
3898 3900
3899 3901 revs = list(revs)
3900 3902 revs.extend(opts['rev'])
3901 3903
3902 3904 if not opts.get('user') and opts.get('currentuser'):
3903 3905 opts['user'] = ui.username()
3904 3906 if not opts.get('date') and opts.get('currentdate'):
3905 3907 opts['date'] = "%d %d" % util.makedate()
3906 3908
3907 3909 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3908 3910
3909 3911 cont = False
3910 3912 if opts['continue']:
3911 3913 cont = True
3912 3914 if revs:
3913 3915 raise error.Abort(_("can't specify --continue and revisions"))
3914 3916 # read in unfinished revisions
3915 3917 try:
3916 3918 nodes = repo.vfs.read('graftstate').splitlines()
3917 3919 revs = [repo[node].rev() for node in nodes]
3918 3920 except IOError as inst:
3919 3921 if inst.errno != errno.ENOENT:
3920 3922 raise
3921 3923 raise error.Abort(_("no graft state found, can't continue"))
3922 3924 else:
3923 3925 cmdutil.checkunfinished(repo)
3924 3926 cmdutil.bailifchanged(repo)
3925 3927 if not revs:
3926 3928 raise error.Abort(_('no revisions specified'))
3927 3929 revs = scmutil.revrange(repo, revs)
3928 3930
3929 3931 skipped = set()
3930 3932 # check for merges
3931 3933 for rev in repo.revs('%ld and merge()', revs):
3932 3934 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3933 3935 skipped.add(rev)
3934 3936 revs = [r for r in revs if r not in skipped]
3935 3937 if not revs:
3936 3938 return -1
3937 3939
3938 3940 # Don't check in the --continue case, in effect retaining --force across
3939 3941 # --continues. That's because without --force, any revisions we decided to
3940 3942 # skip would have been filtered out here, so they wouldn't have made their
3941 3943 # way to the graftstate. With --force, any revisions we would have otherwise
3942 3944 # skipped would not have been filtered out, and if they hadn't been applied
3943 3945 # already, they'd have been in the graftstate.
3944 3946 if not (cont or opts.get('force')):
3945 3947 # check for ancestors of dest branch
3946 3948 crev = repo['.'].rev()
3947 3949 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3948 3950 # Cannot use x.remove(y) on smart set, this has to be a list.
3949 3951 # XXX make this lazy in the future
3950 3952 revs = list(revs)
3951 3953 # don't mutate while iterating, create a copy
3952 3954 for rev in list(revs):
3953 3955 if rev in ancestors:
3954 3956 ui.warn(_('skipping ancestor revision %d:%s\n') %
3955 3957 (rev, repo[rev]))
3956 3958 # XXX remove on list is slow
3957 3959 revs.remove(rev)
3958 3960 if not revs:
3959 3961 return -1
3960 3962
3961 3963 # analyze revs for earlier grafts
3962 3964 ids = {}
3963 3965 for ctx in repo.set("%ld", revs):
3964 3966 ids[ctx.hex()] = ctx.rev()
3965 3967 n = ctx.extra().get('source')
3966 3968 if n:
3967 3969 ids[n] = ctx.rev()
3968 3970
3969 3971 # check ancestors for earlier grafts
3970 3972 ui.debug('scanning for duplicate grafts\n')
3971 3973
3972 3974 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3973 3975 ctx = repo[rev]
3974 3976 n = ctx.extra().get('source')
3975 3977 if n in ids:
3976 3978 try:
3977 3979 r = repo[n].rev()
3978 3980 except error.RepoLookupError:
3979 3981 r = None
3980 3982 if r in revs:
3981 3983 ui.warn(_('skipping revision %d:%s '
3982 3984 '(already grafted to %d:%s)\n')
3983 3985 % (r, repo[r], rev, ctx))
3984 3986 revs.remove(r)
3985 3987 elif ids[n] in revs:
3986 3988 if r is None:
3987 3989 ui.warn(_('skipping already grafted revision %d:%s '
3988 3990 '(%d:%s also has unknown origin %s)\n')
3989 3991 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3990 3992 else:
3991 3993 ui.warn(_('skipping already grafted revision %d:%s '
3992 3994 '(%d:%s also has origin %d:%s)\n')
3993 3995 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3994 3996 revs.remove(ids[n])
3995 3997 elif ctx.hex() in ids:
3996 3998 r = ids[ctx.hex()]
3997 3999 ui.warn(_('skipping already grafted revision %d:%s '
3998 4000 '(was grafted from %d:%s)\n') %
3999 4001 (r, repo[r], rev, ctx))
4000 4002 revs.remove(r)
4001 4003 if not revs:
4002 4004 return -1
4003 4005
4004 4006 for pos, ctx in enumerate(repo.set("%ld", revs)):
4005 4007 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
4006 4008 ctx.description().split('\n', 1)[0])
4007 4009 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
4008 4010 if names:
4009 4011 desc += ' (%s)' % ' '.join(names)
4010 4012 ui.status(_('grafting %s\n') % desc)
4011 4013 if opts.get('dry_run'):
4012 4014 continue
4013 4015
4014 4016 extra = ctx.extra().copy()
4015 4017 del extra['branch']
4016 4018 source = extra.get('source')
4017 4019 if source:
4018 4020 extra['intermediate-source'] = ctx.hex()
4019 4021 else:
4020 4022 extra['source'] = ctx.hex()
4021 4023 user = ctx.user()
4022 4024 if opts.get('user'):
4023 4025 user = opts['user']
4024 4026 date = ctx.date()
4025 4027 if opts.get('date'):
4026 4028 date = opts['date']
4027 4029 message = ctx.description()
4028 4030 if opts.get('log'):
4029 4031 message += '\n(grafted from %s)' % ctx.hex()
4030 4032
4031 4033 # we don't merge the first commit when continuing
4032 4034 if not cont:
4033 4035 # perform the graft merge with p1(rev) as 'ancestor'
4034 4036 try:
4035 4037 # ui.forcemerge is an internal variable, do not document
4036 4038 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4037 4039 'graft')
4038 4040 stats = mergemod.graft(repo, ctx, ctx.p1(),
4039 4041 ['local', 'graft'])
4040 4042 finally:
4041 4043 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
4042 4044 # report any conflicts
4043 4045 if stats and stats[3] > 0:
4044 4046 # write out state for --continue
4045 4047 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
4046 4048 repo.vfs.write('graftstate', ''.join(nodelines))
4047 4049 extra = ''
4048 4050 if opts.get('user'):
4049 4051 extra += ' --user %s' % opts['user']
4050 4052 if opts.get('date'):
4051 4053 extra += ' --date %s' % opts['date']
4052 4054 if opts.get('log'):
4053 4055 extra += ' --log'
4054 4056 hint=_('use hg resolve and hg graft --continue%s') % extra
4055 4057 raise error.Abort(
4056 4058 _("unresolved conflicts, can't continue"),
4057 4059 hint=hint)
4058 4060 else:
4059 4061 cont = False
4060 4062
4061 4063 # commit
4062 4064 node = repo.commit(text=message, user=user,
4063 4065 date=date, extra=extra, editor=editor)
4064 4066 if node is None:
4065 4067 ui.warn(
4066 4068 _('note: graft of %d:%s created no changes to commit\n') %
4067 4069 (ctx.rev(), ctx))
4068 4070
4069 4071 # remove state when we complete successfully
4070 4072 if not opts.get('dry_run'):
4071 4073 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
4072 4074
4073 4075 return 0
4074 4076
4075 4077 @command('grep',
4076 4078 [('0', 'print0', None, _('end fields with NUL')),
4077 4079 ('', 'all', None, _('print all revisions that match')),
4078 4080 ('a', 'text', None, _('treat all files as text')),
4079 4081 ('f', 'follow', None,
4080 4082 _('follow changeset history,'
4081 4083 ' or file history across copies and renames')),
4082 4084 ('i', 'ignore-case', None, _('ignore case when matching')),
4083 4085 ('l', 'files-with-matches', None,
4084 4086 _('print only filenames and revisions that match')),
4085 4087 ('n', 'line-number', None, _('print matching line numbers')),
4086 4088 ('r', 'rev', [],
4087 4089 _('only search files changed within revision range'), _('REV')),
4088 4090 ('u', 'user', None, _('list the author (long with -v)')),
4089 4091 ('d', 'date', None, _('list the date (short with -q)')),
4090 4092 ] + walkopts,
4091 4093 _('[OPTION]... PATTERN [FILE]...'),
4092 4094 inferrepo=True)
4093 4095 def grep(ui, repo, pattern, *pats, **opts):
4094 4096 """search for a pattern in specified files and revisions
4095 4097
4096 4098 Search revisions of files for a regular expression.
4097 4099
4098 4100 This command behaves differently than Unix grep. It only accepts
4099 4101 Python/Perl regexps. It searches repository history, not the
4100 4102 working directory. It always prints the revision number in which a
4101 4103 match appears.
4102 4104
4103 4105 By default, grep only prints output for the first revision of a
4104 4106 file in which it finds a match. To get it to print every revision
4105 4107 that contains a change in match status ("-" for a match that
4106 4108 becomes a non-match, or "+" for a non-match that becomes a match),
4107 4109 use the --all flag.
4108 4110
4109 4111 Returns 0 if a match is found, 1 otherwise.
4110 4112 """
4111 4113 reflags = re.M
4112 4114 if opts.get('ignore_case'):
4113 4115 reflags |= re.I
4114 4116 try:
4115 4117 regexp = util.re.compile(pattern, reflags)
4116 4118 except re.error as inst:
4117 4119 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
4118 4120 return 1
4119 4121 sep, eol = ':', '\n'
4120 4122 if opts.get('print0'):
4121 4123 sep = eol = '\0'
4122 4124
4123 4125 getfile = util.lrucachefunc(repo.file)
4124 4126
4125 4127 def matchlines(body):
4126 4128 begin = 0
4127 4129 linenum = 0
4128 4130 while begin < len(body):
4129 4131 match = regexp.search(body, begin)
4130 4132 if not match:
4131 4133 break
4132 4134 mstart, mend = match.span()
4133 4135 linenum += body.count('\n', begin, mstart) + 1
4134 4136 lstart = body.rfind('\n', begin, mstart) + 1 or begin
4135 4137 begin = body.find('\n', mend) + 1 or len(body) + 1
4136 4138 lend = begin - 1
4137 4139 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
4138 4140
4139 4141 class linestate(object):
4140 4142 def __init__(self, line, linenum, colstart, colend):
4141 4143 self.line = line
4142 4144 self.linenum = linenum
4143 4145 self.colstart = colstart
4144 4146 self.colend = colend
4145 4147
4146 4148 def __hash__(self):
4147 4149 return hash((self.linenum, self.line))
4148 4150
4149 4151 def __eq__(self, other):
4150 4152 return self.line == other.line
4151 4153
4152 4154 def __iter__(self):
4153 4155 yield (self.line[:self.colstart], '')
4154 4156 yield (self.line[self.colstart:self.colend], 'grep.match')
4155 4157 rest = self.line[self.colend:]
4156 4158 while rest != '':
4157 4159 match = regexp.search(rest)
4158 4160 if not match:
4159 4161 yield (rest, '')
4160 4162 break
4161 4163 mstart, mend = match.span()
4162 4164 yield (rest[:mstart], '')
4163 4165 yield (rest[mstart:mend], 'grep.match')
4164 4166 rest = rest[mend:]
4165 4167
4166 4168 matches = {}
4167 4169 copies = {}
4168 4170 def grepbody(fn, rev, body):
4169 4171 matches[rev].setdefault(fn, [])
4170 4172 m = matches[rev][fn]
4171 4173 for lnum, cstart, cend, line in matchlines(body):
4172 4174 s = linestate(line, lnum, cstart, cend)
4173 4175 m.append(s)
4174 4176
4175 4177 def difflinestates(a, b):
4176 4178 sm = difflib.SequenceMatcher(None, a, b)
4177 4179 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
4178 4180 if tag == 'insert':
4179 4181 for i in xrange(blo, bhi):
4180 4182 yield ('+', b[i])
4181 4183 elif tag == 'delete':
4182 4184 for i in xrange(alo, ahi):
4183 4185 yield ('-', a[i])
4184 4186 elif tag == 'replace':
4185 4187 for i in xrange(alo, ahi):
4186 4188 yield ('-', a[i])
4187 4189 for i in xrange(blo, bhi):
4188 4190 yield ('+', b[i])
4189 4191
4190 4192 def display(fn, ctx, pstates, states):
4191 4193 rev = ctx.rev()
4192 4194 if ui.quiet:
4193 4195 datefunc = util.shortdate
4194 4196 else:
4195 4197 datefunc = util.datestr
4196 4198 found = False
4197 4199 @util.cachefunc
4198 4200 def binary():
4199 4201 flog = getfile(fn)
4200 4202 return util.binary(flog.read(ctx.filenode(fn)))
4201 4203
4202 4204 if opts.get('all'):
4203 4205 iter = difflinestates(pstates, states)
4204 4206 else:
4205 4207 iter = [('', l) for l in states]
4206 4208 for change, l in iter:
4207 4209 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
4208 4210
4209 4211 if opts.get('line_number'):
4210 4212 cols.append((str(l.linenum), 'grep.linenumber'))
4211 4213 if opts.get('all'):
4212 4214 cols.append((change, 'grep.change'))
4213 4215 if opts.get('user'):
4214 4216 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
4215 4217 if opts.get('date'):
4216 4218 cols.append((datefunc(ctx.date()), 'grep.date'))
4217 4219 for col, label in cols[:-1]:
4218 4220 ui.write(col, label=label)
4219 4221 ui.write(sep, label='grep.sep')
4220 4222 ui.write(cols[-1][0], label=cols[-1][1])
4221 4223 if not opts.get('files_with_matches'):
4222 4224 ui.write(sep, label='grep.sep')
4223 4225 if not opts.get('text') and binary():
4224 4226 ui.write(" Binary file matches")
4225 4227 else:
4226 4228 for s, label in l:
4227 4229 ui.write(s, label=label)
4228 4230 ui.write(eol)
4229 4231 found = True
4230 4232 if opts.get('files_with_matches'):
4231 4233 break
4232 4234 return found
4233 4235
4234 4236 skip = {}
4235 4237 revfiles = {}
4236 4238 matchfn = scmutil.match(repo[None], pats, opts)
4237 4239 found = False
4238 4240 follow = opts.get('follow')
4239 4241
4240 4242 def prep(ctx, fns):
4241 4243 rev = ctx.rev()
4242 4244 pctx = ctx.p1()
4243 4245 parent = pctx.rev()
4244 4246 matches.setdefault(rev, {})
4245 4247 matches.setdefault(parent, {})
4246 4248 files = revfiles.setdefault(rev, [])
4247 4249 for fn in fns:
4248 4250 flog = getfile(fn)
4249 4251 try:
4250 4252 fnode = ctx.filenode(fn)
4251 4253 except error.LookupError:
4252 4254 continue
4253 4255
4254 4256 copied = flog.renamed(fnode)
4255 4257 copy = follow and copied and copied[0]
4256 4258 if copy:
4257 4259 copies.setdefault(rev, {})[fn] = copy
4258 4260 if fn in skip:
4259 4261 if copy:
4260 4262 skip[copy] = True
4261 4263 continue
4262 4264 files.append(fn)
4263 4265
4264 4266 if fn not in matches[rev]:
4265 4267 grepbody(fn, rev, flog.read(fnode))
4266 4268
4267 4269 pfn = copy or fn
4268 4270 if pfn not in matches[parent]:
4269 4271 try:
4270 4272 fnode = pctx.filenode(pfn)
4271 4273 grepbody(pfn, parent, flog.read(fnode))
4272 4274 except error.LookupError:
4273 4275 pass
4274 4276
4275 4277 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
4276 4278 rev = ctx.rev()
4277 4279 parent = ctx.p1().rev()
4278 4280 for fn in sorted(revfiles.get(rev, [])):
4279 4281 states = matches[rev][fn]
4280 4282 copy = copies.get(rev, {}).get(fn)
4281 4283 if fn in skip:
4282 4284 if copy:
4283 4285 skip[copy] = True
4284 4286 continue
4285 4287 pstates = matches.get(parent, {}).get(copy or fn, [])
4286 4288 if pstates or states:
4287 4289 r = display(fn, ctx, pstates, states)
4288 4290 found = found or r
4289 4291 if r and not opts.get('all'):
4290 4292 skip[fn] = True
4291 4293 if copy:
4292 4294 skip[copy] = True
4293 4295 del matches[rev]
4294 4296 del revfiles[rev]
4295 4297
4296 4298 return not found
4297 4299
4298 4300 @command('heads',
4299 4301 [('r', 'rev', '',
4300 4302 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
4301 4303 ('t', 'topo', False, _('show topological heads only')),
4302 4304 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
4303 4305 ('c', 'closed', False, _('show normal and closed branch heads')),
4304 4306 ] + templateopts,
4305 4307 _('[-ct] [-r STARTREV] [REV]...'))
4306 4308 def heads(ui, repo, *branchrevs, **opts):
4307 4309 """show branch heads
4308 4310
4309 4311 With no arguments, show all open branch heads in the repository.
4310 4312 Branch heads are changesets that have no descendants on the
4311 4313 same branch. They are where development generally takes place and
4312 4314 are the usual targets for update and merge operations.
4313 4315
4314 4316 If one or more REVs are given, only open branch heads on the
4315 4317 branches associated with the specified changesets are shown. This
4316 4318 means that you can use :hg:`heads .` to see the heads on the
4317 4319 currently checked-out branch.
4318 4320
4319 4321 If -c/--closed is specified, also show branch heads marked closed
4320 4322 (see :hg:`commit --close-branch`).
4321 4323
4322 4324 If STARTREV is specified, only those heads that are descendants of
4323 4325 STARTREV will be displayed.
4324 4326
4325 4327 If -t/--topo is specified, named branch mechanics will be ignored and only
4326 4328 topological heads (changesets with no children) will be shown.
4327 4329
4328 4330 Returns 0 if matching heads are found, 1 if not.
4329 4331 """
4330 4332
4331 4333 start = None
4332 4334 if 'rev' in opts:
4333 4335 start = scmutil.revsingle(repo, opts['rev'], None).node()
4334 4336
4335 4337 if opts.get('topo'):
4336 4338 heads = [repo[h] for h in repo.heads(start)]
4337 4339 else:
4338 4340 heads = []
4339 4341 for branch in repo.branchmap():
4340 4342 heads += repo.branchheads(branch, start, opts.get('closed'))
4341 4343 heads = [repo[h] for h in heads]
4342 4344
4343 4345 if branchrevs:
4344 4346 branches = set(repo[br].branch() for br in branchrevs)
4345 4347 heads = [h for h in heads if h.branch() in branches]
4346 4348
4347 4349 if opts.get('active') and branchrevs:
4348 4350 dagheads = repo.heads(start)
4349 4351 heads = [h for h in heads if h.node() in dagheads]
4350 4352
4351 4353 if branchrevs:
4352 4354 haveheads = set(h.branch() for h in heads)
4353 4355 if branches - haveheads:
4354 4356 headless = ', '.join(b for b in branches - haveheads)
4355 4357 msg = _('no open branch heads found on branches %s')
4356 4358 if opts.get('rev'):
4357 4359 msg += _(' (started at %s)') % opts['rev']
4358 4360 ui.warn((msg + '\n') % headless)
4359 4361
4360 4362 if not heads:
4361 4363 return 1
4362 4364
4363 4365 heads = sorted(heads, key=lambda x: -x.rev())
4364 4366 displayer = cmdutil.show_changeset(ui, repo, opts)
4365 4367 for ctx in heads:
4366 4368 displayer.show(ctx)
4367 4369 displayer.close()
4368 4370
4369 4371 @command('help',
4370 4372 [('e', 'extension', None, _('show only help for extensions')),
4371 4373 ('c', 'command', None, _('show only help for commands')),
4372 4374 ('k', 'keyword', None, _('show topics matching keyword')),
4373 4375 ('s', 'system', [], _('show help for specific platform(s)')),
4374 4376 ],
4375 4377 _('[-ecks] [TOPIC]'),
4376 4378 norepo=True)
4377 4379 def help_(ui, name=None, **opts):
4378 4380 """show help for a given topic or a help overview
4379 4381
4380 4382 With no arguments, print a list of commands with short help messages.
4381 4383
4382 4384 Given a topic, extension, or command name, print help for that
4383 4385 topic.
4384 4386
4385 4387 Returns 0 if successful.
4386 4388 """
4387 4389
4388 4390 textwidth = min(ui.termwidth(), 80) - 2
4389 4391
4390 4392 keep = opts.get('system') or []
4391 4393 if len(keep) == 0:
4392 4394 if sys.platform.startswith('win'):
4393 4395 keep.append('windows')
4394 4396 elif sys.platform == 'OpenVMS':
4395 4397 keep.append('vms')
4396 4398 elif sys.platform == 'plan9':
4397 4399 keep.append('plan9')
4398 4400 else:
4399 4401 keep.append('unix')
4400 4402 keep.append(sys.platform.lower())
4401 4403 if ui.verbose:
4402 4404 keep.append('verbose')
4403 4405
4404 4406 section = None
4405 4407 subtopic = None
4406 4408 if name and '.' in name:
4407 4409 name, section = name.split('.', 1)
4408 4410 section = section.lower()
4409 4411 if '.' in section:
4410 4412 subtopic, section = section.split('.', 1)
4411 4413 else:
4412 4414 subtopic = section
4413 4415
4414 4416 text = help.help_(ui, name, subtopic=subtopic, **opts)
4415 4417
4416 4418 formatted, pruned = minirst.format(text, textwidth, keep=keep,
4417 4419 section=section)
4418 4420
4419 4421 # We could have been given a weird ".foo" section without a name
4420 4422 # to look for, or we could have simply failed to found "foo.bar"
4421 4423 # because bar isn't a section of foo
4422 4424 if section and not (formatted and name):
4423 4425 raise error.Abort(_("help section not found"))
4424 4426
4425 4427 if 'verbose' in pruned:
4426 4428 keep.append('omitted')
4427 4429 else:
4428 4430 keep.append('notomitted')
4429 4431 formatted, pruned = minirst.format(text, textwidth, keep=keep,
4430 4432 section=section)
4431 4433 ui.write(formatted)
4432 4434
4433 4435
4434 4436 @command('identify|id',
4435 4437 [('r', 'rev', '',
4436 4438 _('identify the specified revision'), _('REV')),
4437 4439 ('n', 'num', None, _('show local revision number')),
4438 4440 ('i', 'id', None, _('show global revision id')),
4439 4441 ('b', 'branch', None, _('show branch')),
4440 4442 ('t', 'tags', None, _('show tags')),
4441 4443 ('B', 'bookmarks', None, _('show bookmarks')),
4442 4444 ] + remoteopts,
4443 4445 _('[-nibtB] [-r REV] [SOURCE]'),
4444 4446 optionalrepo=True)
4445 4447 def identify(ui, repo, source=None, rev=None,
4446 4448 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
4447 4449 """identify the working directory or specified revision
4448 4450
4449 4451 Print a summary identifying the repository state at REV using one or
4450 4452 two parent hash identifiers, followed by a "+" if the working
4451 4453 directory has uncommitted changes, the branch name (if not default),
4452 4454 a list of tags, and a list of bookmarks.
4453 4455
4454 4456 When REV is not given, print a summary of the current state of the
4455 4457 repository.
4456 4458
4457 4459 Specifying a path to a repository root or Mercurial bundle will
4458 4460 cause lookup to operate on that repository/bundle.
4459 4461
4460 4462 .. container:: verbose
4461 4463
4462 4464 Examples:
4463 4465
4464 4466 - generate a build identifier for the working directory::
4465 4467
4466 4468 hg id --id > build-id.dat
4467 4469
4468 4470 - find the revision corresponding to a tag::
4469 4471
4470 4472 hg id -n -r 1.3
4471 4473
4472 4474 - check the most recent revision of a remote repository::
4473 4475
4474 4476 hg id -r tip http://selenic.com/hg/
4475 4477
4476 4478 See :hg:`log` for generating more information about specific revisions,
4477 4479 including full hash identifiers.
4478 4480
4479 4481 Returns 0 if successful.
4480 4482 """
4481 4483
4482 4484 if not repo and not source:
4483 4485 raise error.Abort(_("there is no Mercurial repository here "
4484 4486 "(.hg not found)"))
4485 4487
4486 4488 if ui.debugflag:
4487 4489 hexfunc = hex
4488 4490 else:
4489 4491 hexfunc = short
4490 4492 default = not (num or id or branch or tags or bookmarks)
4491 4493 output = []
4492 4494 revs = []
4493 4495
4494 4496 if source:
4495 4497 source, branches = hg.parseurl(ui.expandpath(source))
4496 4498 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
4497 4499 repo = peer.local()
4498 4500 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
4499 4501
4500 4502 if not repo:
4501 4503 if num or branch or tags:
4502 4504 raise error.Abort(
4503 4505 _("can't query remote revision number, branch, or tags"))
4504 4506 if not rev and revs:
4505 4507 rev = revs[0]
4506 4508 if not rev:
4507 4509 rev = "tip"
4508 4510
4509 4511 remoterev = peer.lookup(rev)
4510 4512 if default or id:
4511 4513 output = [hexfunc(remoterev)]
4512 4514
4513 4515 def getbms():
4514 4516 bms = []
4515 4517
4516 4518 if 'bookmarks' in peer.listkeys('namespaces'):
4517 4519 hexremoterev = hex(remoterev)
4518 4520 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
4519 4521 if bmr == hexremoterev]
4520 4522
4521 4523 return sorted(bms)
4522 4524
4523 4525 if bookmarks:
4524 4526 output.extend(getbms())
4525 4527 elif default and not ui.quiet:
4526 4528 # multiple bookmarks for a single parent separated by '/'
4527 4529 bm = '/'.join(getbms())
4528 4530 if bm:
4529 4531 output.append(bm)
4530 4532 else:
4531 4533 ctx = scmutil.revsingle(repo, rev, None)
4532 4534
4533 4535 if ctx.rev() is None:
4534 4536 ctx = repo[None]
4535 4537 parents = ctx.parents()
4536 4538 taglist = []
4537 4539 for p in parents:
4538 4540 taglist.extend(p.tags())
4539 4541
4540 4542 changed = ""
4541 4543 if default or id or num:
4542 4544 if (any(repo.status())
4543 4545 or any(ctx.sub(s).dirty() for s in ctx.substate)):
4544 4546 changed = '+'
4545 4547 if default or id:
4546 4548 output = ["%s%s" %
4547 4549 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4548 4550 if num:
4549 4551 output.append("%s%s" %
4550 4552 ('+'.join([str(p.rev()) for p in parents]), changed))
4551 4553 else:
4552 4554 if default or id:
4553 4555 output = [hexfunc(ctx.node())]
4554 4556 if num:
4555 4557 output.append(str(ctx.rev()))
4556 4558 taglist = ctx.tags()
4557 4559
4558 4560 if default and not ui.quiet:
4559 4561 b = ctx.branch()
4560 4562 if b != 'default':
4561 4563 output.append("(%s)" % b)
4562 4564
4563 4565 # multiple tags for a single parent separated by '/'
4564 4566 t = '/'.join(taglist)
4565 4567 if t:
4566 4568 output.append(t)
4567 4569
4568 4570 # multiple bookmarks for a single parent separated by '/'
4569 4571 bm = '/'.join(ctx.bookmarks())
4570 4572 if bm:
4571 4573 output.append(bm)
4572 4574 else:
4573 4575 if branch:
4574 4576 output.append(ctx.branch())
4575 4577
4576 4578 if tags:
4577 4579 output.extend(taglist)
4578 4580
4579 4581 if bookmarks:
4580 4582 output.extend(ctx.bookmarks())
4581 4583
4582 4584 ui.write("%s\n" % ' '.join(output))
4583 4585
4584 4586 @command('import|patch',
4585 4587 [('p', 'strip', 1,
4586 4588 _('directory strip option for patch. This has the same '
4587 4589 'meaning as the corresponding patch option'), _('NUM')),
4588 4590 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4589 4591 ('e', 'edit', False, _('invoke editor on commit messages')),
4590 4592 ('f', 'force', None,
4591 4593 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4592 4594 ('', 'no-commit', None,
4593 4595 _("don't commit, just update the working directory")),
4594 4596 ('', 'bypass', None,
4595 4597 _("apply patch without touching the working directory")),
4596 4598 ('', 'partial', None,
4597 4599 _('commit even if some hunks fail')),
4598 4600 ('', 'exact', None,
4599 4601 _('apply patch to the nodes from which it was generated')),
4600 4602 ('', 'prefix', '',
4601 4603 _('apply patch to subdirectory'), _('DIR')),
4602 4604 ('', 'import-branch', None,
4603 4605 _('use any branch information in patch (implied by --exact)'))] +
4604 4606 commitopts + commitopts2 + similarityopts,
4605 4607 _('[OPTION]... PATCH...'))
4606 4608 def import_(ui, repo, patch1=None, *patches, **opts):
4607 4609 """import an ordered set of patches
4608 4610
4609 4611 Import a list of patches and commit them individually (unless
4610 4612 --no-commit is specified).
4611 4613
4612 4614 To read a patch from standard input, use "-" as the patch name. If
4613 4615 a URL is specified, the patch will be downloaded from there.
4614 4616
4615 4617 Import first applies changes to the working directory (unless
4616 4618 --bypass is specified), import will abort if there are outstanding
4617 4619 changes.
4618 4620
4619 4621 Use --bypass to apply and commit patches directly to the
4620 4622 repository, without affecting the working directory. Without
4621 4623 --exact, patches will be applied on top of the working directory
4622 4624 parent revision.
4623 4625
4624 4626 You can import a patch straight from a mail message. Even patches
4625 4627 as attachments work (to use the body part, it must have type
4626 4628 text/plain or text/x-patch). From and Subject headers of email
4627 4629 message are used as default committer and commit message. All
4628 4630 text/plain body parts before first diff are added to the commit
4629 4631 message.
4630 4632
4631 4633 If the imported patch was generated by :hg:`export`, user and
4632 4634 description from patch override values from message headers and
4633 4635 body. Values given on command line with -m/--message and -u/--user
4634 4636 override these.
4635 4637
4636 4638 If --exact is specified, import will set the working directory to
4637 4639 the parent of each patch before applying it, and will abort if the
4638 4640 resulting changeset has a different ID than the one recorded in
4639 4641 the patch. This may happen due to character set problems or other
4640 4642 deficiencies in the text patch format.
4641 4643
4642 4644 Use --partial to ensure a changeset will be created from the patch
4643 4645 even if some hunks fail to apply. Hunks that fail to apply will be
4644 4646 written to a <target-file>.rej file. Conflicts can then be resolved
4645 4647 by hand before :hg:`commit --amend` is run to update the created
4646 4648 changeset. This flag exists to let people import patches that
4647 4649 partially apply without losing the associated metadata (author,
4648 4650 date, description, ...).
4649 4651
4650 4652 .. note::
4651 4653
4652 4654 When no hunks apply cleanly, :hg:`import --partial` will create
4653 4655 an empty changeset, importing only the patch metadata.
4654 4656
4655 4657 With -s/--similarity, hg will attempt to discover renames and
4656 4658 copies in the patch in the same way as :hg:`addremove`.
4657 4659
4658 4660 It is possible to use external patch programs to perform the patch
4659 4661 by setting the ``ui.patch`` configuration option. For the default
4660 4662 internal tool, the fuzz can also be configured via ``patch.fuzz``.
4661 4663 See :hg:`help config` for more information about configuration
4662 4664 files and how to use these options.
4663 4665
4664 4666 See :hg:`help dates` for a list of formats valid for -d/--date.
4665 4667
4666 4668 .. container:: verbose
4667 4669
4668 4670 Examples:
4669 4671
4670 4672 - import a traditional patch from a website and detect renames::
4671 4673
4672 4674 hg import -s 80 http://example.com/bugfix.patch
4673 4675
4674 4676 - import a changeset from an hgweb server::
4675 4677
4676 4678 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
4677 4679
4678 4680 - import all the patches in an Unix-style mbox::
4679 4681
4680 4682 hg import incoming-patches.mbox
4681 4683
4682 4684 - attempt to exactly restore an exported changeset (not always
4683 4685 possible)::
4684 4686
4685 4687 hg import --exact proposed-fix.patch
4686 4688
4687 4689 - use an external tool to apply a patch which is too fuzzy for
4688 4690 the default internal tool.
4689 4691
4690 4692 hg import --config ui.patch="patch --merge" fuzzy.patch
4691 4693
4692 4694 - change the default fuzzing from 2 to a less strict 7
4693 4695
4694 4696 hg import --config ui.fuzz=7 fuzz.patch
4695 4697
4696 4698 Returns 0 on success, 1 on partial success (see --partial).
4697 4699 """
4698 4700
4699 4701 if not patch1:
4700 4702 raise error.Abort(_('need at least one patch to import'))
4701 4703
4702 4704 patches = (patch1,) + patches
4703 4705
4704 4706 date = opts.get('date')
4705 4707 if date:
4706 4708 opts['date'] = util.parsedate(date)
4707 4709
4708 4710 exact = opts.get('exact')
4709 4711 update = not opts.get('bypass')
4710 4712 if not update and opts.get('no_commit'):
4711 4713 raise error.Abort(_('cannot use --no-commit with --bypass'))
4712 4714 try:
4713 4715 sim = float(opts.get('similarity') or 0)
4714 4716 except ValueError:
4715 4717 raise error.Abort(_('similarity must be a number'))
4716 4718 if sim < 0 or sim > 100:
4717 4719 raise error.Abort(_('similarity must be between 0 and 100'))
4718 4720 if sim and not update:
4719 4721 raise error.Abort(_('cannot use --similarity with --bypass'))
4720 4722 if exact:
4721 4723 if opts.get('edit'):
4722 4724 raise error.Abort(_('cannot use --exact with --edit'))
4723 4725 if opts.get('prefix'):
4724 4726 raise error.Abort(_('cannot use --exact with --prefix'))
4725 4727
4726 4728 base = opts["base"]
4727 4729 wlock = dsguard = lock = tr = None
4728 4730 msgs = []
4729 4731 ret = 0
4730 4732
4731 4733
4732 4734 try:
4733 4735 wlock = repo.wlock()
4734 4736
4735 4737 if update:
4736 4738 cmdutil.checkunfinished(repo)
4737 4739 if (exact or not opts.get('force')):
4738 4740 cmdutil.bailifchanged(repo)
4739 4741
4740 4742 if not opts.get('no_commit'):
4741 4743 lock = repo.lock()
4742 4744 tr = repo.transaction('import')
4743 4745 else:
4744 4746 dsguard = cmdutil.dirstateguard(repo, 'import')
4745 4747 parents = repo[None].parents()
4746 4748 for patchurl in patches:
4747 4749 if patchurl == '-':
4748 4750 ui.status(_('applying patch from stdin\n'))
4749 4751 patchfile = ui.fin
4750 4752 patchurl = 'stdin' # for error message
4751 4753 else:
4752 4754 patchurl = os.path.join(base, patchurl)
4753 4755 ui.status(_('applying %s\n') % patchurl)
4754 4756 patchfile = hg.openpath(ui, patchurl)
4755 4757
4756 4758 haspatch = False
4757 4759 for hunk in patch.split(patchfile):
4758 4760 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4759 4761 parents, opts,
4760 4762 msgs, hg.clean)
4761 4763 if msg:
4762 4764 haspatch = True
4763 4765 ui.note(msg + '\n')
4764 4766 if update or exact:
4765 4767 parents = repo[None].parents()
4766 4768 else:
4767 4769 parents = [repo[node]]
4768 4770 if rej:
4769 4771 ui.write_err(_("patch applied partially\n"))
4770 4772 ui.write_err(_("(fix the .rej files and run "
4771 4773 "`hg commit --amend`)\n"))
4772 4774 ret = 1
4773 4775 break
4774 4776
4775 4777 if not haspatch:
4776 4778 raise error.Abort(_('%s: no diffs found') % patchurl)
4777 4779
4778 4780 if tr:
4779 4781 tr.close()
4780 4782 if msgs:
4781 4783 repo.savecommitmessage('\n* * *\n'.join(msgs))
4782 4784 if dsguard:
4783 4785 dsguard.close()
4784 4786 return ret
4785 4787 finally:
4786 4788 if tr:
4787 4789 tr.release()
4788 4790 release(lock, dsguard, wlock)
4789 4791
4790 4792 @command('incoming|in',
4791 4793 [('f', 'force', None,
4792 4794 _('run even if remote repository is unrelated')),
4793 4795 ('n', 'newest-first', None, _('show newest record first')),
4794 4796 ('', 'bundle', '',
4795 4797 _('file to store the bundles into'), _('FILE')),
4796 4798 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4797 4799 ('B', 'bookmarks', False, _("compare bookmarks")),
4798 4800 ('b', 'branch', [],
4799 4801 _('a specific branch you would like to pull'), _('BRANCH')),
4800 4802 ] + logopts + remoteopts + subrepoopts,
4801 4803 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4802 4804 def incoming(ui, repo, source="default", **opts):
4803 4805 """show new changesets found in source
4804 4806
4805 4807 Show new changesets found in the specified path/URL or the default
4806 4808 pull location. These are the changesets that would have been pulled
4807 4809 if a pull at the time you issued this command.
4808 4810
4809 4811 See pull for valid source format details.
4810 4812
4811 4813 .. container:: verbose
4812 4814
4813 4815 With -B/--bookmarks, the result of bookmark comparison between
4814 4816 local and remote repositories is displayed. With -v/--verbose,
4815 4817 status is also displayed for each bookmark like below::
4816 4818
4817 4819 BM1 01234567890a added
4818 4820 BM2 1234567890ab advanced
4819 4821 BM3 234567890abc diverged
4820 4822 BM4 34567890abcd changed
4821 4823
4822 4824 The action taken locally when pulling depends on the
4823 4825 status of each bookmark:
4824 4826
4825 4827 :``added``: pull will create it
4826 4828 :``advanced``: pull will update it
4827 4829 :``diverged``: pull will create a divergent bookmark
4828 4830 :``changed``: result depends on remote changesets
4829 4831
4830 4832 From the point of view of pulling behavior, bookmark
4831 4833 existing only in the remote repository are treated as ``added``,
4832 4834 even if it is in fact locally deleted.
4833 4835
4834 4836 .. container:: verbose
4835 4837
4836 4838 For remote repository, using --bundle avoids downloading the
4837 4839 changesets twice if the incoming is followed by a pull.
4838 4840
4839 4841 Examples:
4840 4842
4841 4843 - show incoming changes with patches and full description::
4842 4844
4843 4845 hg incoming -vp
4844 4846
4845 4847 - show incoming changes excluding merges, store a bundle::
4846 4848
4847 4849 hg in -vpM --bundle incoming.hg
4848 4850 hg pull incoming.hg
4849 4851
4850 4852 - briefly list changes inside a bundle::
4851 4853
4852 4854 hg in changes.hg -T "{desc|firstline}\\n"
4853 4855
4854 4856 Returns 0 if there are incoming changes, 1 otherwise.
4855 4857 """
4856 4858 if opts.get('graph'):
4857 4859 cmdutil.checkunsupportedgraphflags([], opts)
4858 4860 def display(other, chlist, displayer):
4859 4861 revdag = cmdutil.graphrevs(other, chlist, opts)
4860 4862 cmdutil.displaygraph(ui, repo, revdag, displayer,
4861 4863 graphmod.asciiedges)
4862 4864
4863 4865 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4864 4866 return 0
4865 4867
4866 4868 if opts.get('bundle') and opts.get('subrepos'):
4867 4869 raise error.Abort(_('cannot combine --bundle and --subrepos'))
4868 4870
4869 4871 if opts.get('bookmarks'):
4870 4872 source, branches = hg.parseurl(ui.expandpath(source),
4871 4873 opts.get('branch'))
4872 4874 other = hg.peer(repo, opts, source)
4873 4875 if 'bookmarks' not in other.listkeys('namespaces'):
4874 4876 ui.warn(_("remote doesn't support bookmarks\n"))
4875 4877 return 0
4876 4878 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4877 4879 return bookmarks.incoming(ui, repo, other)
4878 4880
4879 4881 repo._subtoppath = ui.expandpath(source)
4880 4882 try:
4881 4883 return hg.incoming(ui, repo, source, opts)
4882 4884 finally:
4883 4885 del repo._subtoppath
4884 4886
4885 4887
4886 4888 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4887 4889 norepo=True)
4888 4890 def init(ui, dest=".", **opts):
4889 4891 """create a new repository in the given directory
4890 4892
4891 4893 Initialize a new repository in the given directory. If the given
4892 4894 directory does not exist, it will be created.
4893 4895
4894 4896 If no directory is given, the current directory is used.
4895 4897
4896 4898 It is possible to specify an ``ssh://`` URL as the destination.
4897 4899 See :hg:`help urls` for more information.
4898 4900
4899 4901 Returns 0 on success.
4900 4902 """
4901 4903 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4902 4904
4903 4905 @command('locate',
4904 4906 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4905 4907 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4906 4908 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4907 4909 ] + walkopts,
4908 4910 _('[OPTION]... [PATTERN]...'))
4909 4911 def locate(ui, repo, *pats, **opts):
4910 4912 """locate files matching specific patterns (DEPRECATED)
4911 4913
4912 4914 Print files under Mercurial control in the working directory whose
4913 4915 names match the given patterns.
4914 4916
4915 4917 By default, this command searches all directories in the working
4916 4918 directory. To search just the current directory and its
4917 4919 subdirectories, use "--include .".
4918 4920
4919 4921 If no patterns are given to match, this command prints the names
4920 4922 of all files under Mercurial control in the working directory.
4921 4923
4922 4924 If you want to feed the output of this command into the "xargs"
4923 4925 command, use the -0 option to both this command and "xargs". This
4924 4926 will avoid the problem of "xargs" treating single filenames that
4925 4927 contain whitespace as multiple filenames.
4926 4928
4927 4929 See :hg:`help files` for a more versatile command.
4928 4930
4929 4931 Returns 0 if a match is found, 1 otherwise.
4930 4932 """
4931 4933 if opts.get('print0'):
4932 4934 end = '\0'
4933 4935 else:
4934 4936 end = '\n'
4935 4937 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4936 4938
4937 4939 ret = 1
4938 4940 ctx = repo[rev]
4939 4941 m = scmutil.match(ctx, pats, opts, default='relglob',
4940 4942 badfn=lambda x, y: False)
4941 4943
4942 4944 for abs in ctx.matches(m):
4943 4945 if opts.get('fullpath'):
4944 4946 ui.write(repo.wjoin(abs), end)
4945 4947 else:
4946 4948 ui.write(((pats and m.rel(abs)) or abs), end)
4947 4949 ret = 0
4948 4950
4949 4951 return ret
4950 4952
4951 4953 @command('^log|history',
4952 4954 [('f', 'follow', None,
4953 4955 _('follow changeset history, or file history across copies and renames')),
4954 4956 ('', 'follow-first', None,
4955 4957 _('only follow the first parent of merge changesets (DEPRECATED)')),
4956 4958 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4957 4959 ('C', 'copies', None, _('show copied files')),
4958 4960 ('k', 'keyword', [],
4959 4961 _('do case-insensitive search for a given text'), _('TEXT')),
4960 4962 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4961 4963 ('', 'removed', None, _('include revisions where files were removed')),
4962 4964 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4963 4965 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4964 4966 ('', 'only-branch', [],
4965 4967 _('show only changesets within the given named branch (DEPRECATED)'),
4966 4968 _('BRANCH')),
4967 4969 ('b', 'branch', [],
4968 4970 _('show changesets within the given named branch'), _('BRANCH')),
4969 4971 ('P', 'prune', [],
4970 4972 _('do not display revision or any of its ancestors'), _('REV')),
4971 4973 ] + logopts + walkopts,
4972 4974 _('[OPTION]... [FILE]'),
4973 4975 inferrepo=True)
4974 4976 def log(ui, repo, *pats, **opts):
4975 4977 """show revision history of entire repository or files
4976 4978
4977 4979 Print the revision history of the specified files or the entire
4978 4980 project.
4979 4981
4980 4982 If no revision range is specified, the default is ``tip:0`` unless
4981 4983 --follow is set, in which case the working directory parent is
4982 4984 used as the starting revision.
4983 4985
4984 4986 File history is shown without following rename or copy history of
4985 4987 files. Use -f/--follow with a filename to follow history across
4986 4988 renames and copies. --follow without a filename will only show
4987 4989 ancestors or descendants of the starting revision.
4988 4990
4989 4991 By default this command prints revision number and changeset id,
4990 4992 tags, non-trivial parents, user, date and time, and a summary for
4991 4993 each commit. When the -v/--verbose switch is used, the list of
4992 4994 changed files and full commit message are shown.
4993 4995
4994 4996 With --graph the revisions are shown as an ASCII art DAG with the most
4995 4997 recent changeset at the top.
4996 4998 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4997 4999 and '+' represents a fork where the changeset from the lines below is a
4998 5000 parent of the 'o' merge on the same line.
4999 5001
5000 5002 .. note::
5001 5003
5002 5004 :hg:`log --patch` may generate unexpected diff output for merge
5003 5005 changesets, as it will only compare the merge changeset against
5004 5006 its first parent. Also, only files different from BOTH parents
5005 5007 will appear in files:.
5006 5008
5007 5009 .. note::
5008 5010
5009 5011 For performance reasons, :hg:`log FILE` may omit duplicate changes
5010 5012 made on branches and will not show removals or mode changes. To
5011 5013 see all such changes, use the --removed switch.
5012 5014
5013 5015 .. container:: verbose
5014 5016
5015 5017 Some examples:
5016 5018
5017 5019 - changesets with full descriptions and file lists::
5018 5020
5019 5021 hg log -v
5020 5022
5021 5023 - changesets ancestral to the working directory::
5022 5024
5023 5025 hg log -f
5024 5026
5025 5027 - last 10 commits on the current branch::
5026 5028
5027 5029 hg log -l 10 -b .
5028 5030
5029 5031 - changesets showing all modifications of a file, including removals::
5030 5032
5031 5033 hg log --removed file.c
5032 5034
5033 5035 - all changesets that touch a directory, with diffs, excluding merges::
5034 5036
5035 5037 hg log -Mp lib/
5036 5038
5037 5039 - all revision numbers that match a keyword::
5038 5040
5039 5041 hg log -k bug --template "{rev}\\n"
5040 5042
5041 5043 - the full hash identifier of the working directory parent::
5042 5044
5043 5045 hg log -r . --template "{node}\\n"
5044 5046
5045 5047 - list available log templates::
5046 5048
5047 5049 hg log -T list
5048 5050
5049 5051 - check if a given changeset is included in a tagged release::
5050 5052
5051 5053 hg log -r "a21ccf and ancestor(1.9)"
5052 5054
5053 5055 - find all changesets by some user in a date range::
5054 5056
5055 5057 hg log -k alice -d "may 2008 to jul 2008"
5056 5058
5057 5059 - summary of all changesets after the last tag::
5058 5060
5059 5061 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
5060 5062
5061 5063 See :hg:`help dates` for a list of formats valid for -d/--date.
5062 5064
5063 5065 See :hg:`help revisions` and :hg:`help revsets` for more about
5064 5066 specifying and ordering revisions.
5065 5067
5066 5068 See :hg:`help templates` for more about pre-packaged styles and
5067 5069 specifying custom templates.
5068 5070
5069 5071 Returns 0 on success.
5070 5072
5071 5073 """
5072 5074 if opts.get('follow') and opts.get('rev'):
5073 5075 opts['rev'] = [revset.formatspec('reverse(::%lr)', opts.get('rev'))]
5074 5076 del opts['follow']
5075 5077
5076 5078 if opts.get('graph'):
5077 5079 return cmdutil.graphlog(ui, repo, *pats, **opts)
5078 5080
5079 5081 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
5080 5082 limit = cmdutil.loglimit(opts)
5081 5083 count = 0
5082 5084
5083 5085 getrenamed = None
5084 5086 if opts.get('copies'):
5085 5087 endrev = None
5086 5088 if opts.get('rev'):
5087 5089 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
5088 5090 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
5089 5091
5090 5092 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
5091 5093 for rev in revs:
5092 5094 if count == limit:
5093 5095 break
5094 5096 ctx = repo[rev]
5095 5097 copies = None
5096 5098 if getrenamed is not None and rev:
5097 5099 copies = []
5098 5100 for fn in ctx.files():
5099 5101 rename = getrenamed(fn, rev)
5100 5102 if rename:
5101 5103 copies.append((fn, rename[0]))
5102 5104 if filematcher:
5103 5105 revmatchfn = filematcher(ctx.rev())
5104 5106 else:
5105 5107 revmatchfn = None
5106 5108 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
5107 5109 if displayer.flush(ctx):
5108 5110 count += 1
5109 5111
5110 5112 displayer.close()
5111 5113
5112 5114 @command('manifest',
5113 5115 [('r', 'rev', '', _('revision to display'), _('REV')),
5114 5116 ('', 'all', False, _("list files from all revisions"))]
5115 5117 + formatteropts,
5116 5118 _('[-r REV]'))
5117 5119 def manifest(ui, repo, node=None, rev=None, **opts):
5118 5120 """output the current or given revision of the project manifest
5119 5121
5120 5122 Print a list of version controlled files for the given revision.
5121 5123 If no revision is given, the first parent of the working directory
5122 5124 is used, or the null revision if no revision is checked out.
5123 5125
5124 5126 With -v, print file permissions, symlink and executable bits.
5125 5127 With --debug, print file revision hashes.
5126 5128
5127 5129 If option --all is specified, the list of all files from all revisions
5128 5130 is printed. This includes deleted and renamed files.
5129 5131
5130 5132 Returns 0 on success.
5131 5133 """
5132 5134
5133 5135 fm = ui.formatter('manifest', opts)
5134 5136
5135 5137 if opts.get('all'):
5136 5138 if rev or node:
5137 5139 raise error.Abort(_("can't specify a revision with --all"))
5138 5140
5139 5141 res = []
5140 5142 prefix = "data/"
5141 5143 suffix = ".i"
5142 5144 plen = len(prefix)
5143 5145 slen = len(suffix)
5144 5146 with repo.lock():
5145 5147 for fn, b, size in repo.store.datafiles():
5146 5148 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
5147 5149 res.append(fn[plen:-slen])
5148 5150 for f in res:
5149 5151 fm.startitem()
5150 5152 fm.write("path", '%s\n', f)
5151 5153 fm.end()
5152 5154 return
5153 5155
5154 5156 if rev and node:
5155 5157 raise error.Abort(_("please specify just one revision"))
5156 5158
5157 5159 if not node:
5158 5160 node = rev
5159 5161
5160 5162 char = {'l': '@', 'x': '*', '': ''}
5161 5163 mode = {'l': '644', 'x': '755', '': '644'}
5162 5164 ctx = scmutil.revsingle(repo, node)
5163 5165 mf = ctx.manifest()
5164 5166 for f in ctx:
5165 5167 fm.startitem()
5166 5168 fl = ctx[f].flags()
5167 5169 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
5168 5170 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
5169 5171 fm.write('path', '%s\n', f)
5170 5172 fm.end()
5171 5173
5172 5174 @command('^merge',
5173 5175 [('f', 'force', None,
5174 5176 _('force a merge including outstanding changes (DEPRECATED)')),
5175 5177 ('r', 'rev', '', _('revision to merge'), _('REV')),
5176 5178 ('P', 'preview', None,
5177 5179 _('review revisions to merge (no merge is performed)'))
5178 5180 ] + mergetoolopts,
5179 5181 _('[-P] [-f] [[-r] REV]'))
5180 5182 def merge(ui, repo, node=None, **opts):
5181 5183 """merge another revision into working directory
5182 5184
5183 5185 The current working directory is updated with all changes made in
5184 5186 the requested revision since the last common predecessor revision.
5185 5187
5186 5188 Files that changed between either parent are marked as changed for
5187 5189 the next commit and a commit must be performed before any further
5188 5190 updates to the repository are allowed. The next commit will have
5189 5191 two parents.
5190 5192
5191 5193 ``--tool`` can be used to specify the merge tool used for file
5192 5194 merges. It overrides the HGMERGE environment variable and your
5193 5195 configuration files. See :hg:`help merge-tools` for options.
5194 5196
5195 5197 If no revision is specified, the working directory's parent is a
5196 5198 head revision, and the current branch contains exactly one other
5197 5199 head, the other head is merged with by default. Otherwise, an
5198 5200 explicit revision with which to merge with must be provided.
5199 5201
5200 5202 See :hg:`help resolve` for information on handling file conflicts.
5201 5203
5202 5204 To undo an uncommitted merge, use :hg:`update --clean .` which
5203 5205 will check out a clean copy of the original merge parent, losing
5204 5206 all changes.
5205 5207
5206 5208 Returns 0 on success, 1 if there are unresolved files.
5207 5209 """
5208 5210
5209 5211 if opts.get('rev') and node:
5210 5212 raise error.Abort(_("please specify just one revision"))
5211 5213 if not node:
5212 5214 node = opts.get('rev')
5213 5215
5214 5216 if node:
5215 5217 node = scmutil.revsingle(repo, node).node()
5216 5218
5217 5219 if not node:
5218 5220 node = repo[destutil.destmerge(repo)].node()
5219 5221
5220 5222 if opts.get('preview'):
5221 5223 # find nodes that are ancestors of p2 but not of p1
5222 5224 p1 = repo.lookup('.')
5223 5225 p2 = repo.lookup(node)
5224 5226 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
5225 5227
5226 5228 displayer = cmdutil.show_changeset(ui, repo, opts)
5227 5229 for node in nodes:
5228 5230 displayer.show(repo[node])
5229 5231 displayer.close()
5230 5232 return 0
5231 5233
5232 5234 try:
5233 5235 # ui.forcemerge is an internal variable, do not document
5234 5236 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
5235 5237 return hg.merge(repo, node, force=opts.get('force'))
5236 5238 finally:
5237 5239 ui.setconfig('ui', 'forcemerge', '', 'merge')
5238 5240
5239 5241 @command('outgoing|out',
5240 5242 [('f', 'force', None, _('run even when the destination is unrelated')),
5241 5243 ('r', 'rev', [],
5242 5244 _('a changeset intended to be included in the destination'), _('REV')),
5243 5245 ('n', 'newest-first', None, _('show newest record first')),
5244 5246 ('B', 'bookmarks', False, _('compare bookmarks')),
5245 5247 ('b', 'branch', [], _('a specific branch you would like to push'),
5246 5248 _('BRANCH')),
5247 5249 ] + logopts + remoteopts + subrepoopts,
5248 5250 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
5249 5251 def outgoing(ui, repo, dest=None, **opts):
5250 5252 """show changesets not found in the destination
5251 5253
5252 5254 Show changesets not found in the specified destination repository
5253 5255 or the default push location. These are the changesets that would
5254 5256 be pushed if a push was requested.
5255 5257
5256 5258 See pull for details of valid destination formats.
5257 5259
5258 5260 .. container:: verbose
5259 5261
5260 5262 With -B/--bookmarks, the result of bookmark comparison between
5261 5263 local and remote repositories is displayed. With -v/--verbose,
5262 5264 status is also displayed for each bookmark like below::
5263 5265
5264 5266 BM1 01234567890a added
5265 5267 BM2 deleted
5266 5268 BM3 234567890abc advanced
5267 5269 BM4 34567890abcd diverged
5268 5270 BM5 4567890abcde changed
5269 5271
5270 5272 The action taken when pushing depends on the
5271 5273 status of each bookmark:
5272 5274
5273 5275 :``added``: push with ``-B`` will create it
5274 5276 :``deleted``: push with ``-B`` will delete it
5275 5277 :``advanced``: push will update it
5276 5278 :``diverged``: push with ``-B`` will update it
5277 5279 :``changed``: push with ``-B`` will update it
5278 5280
5279 5281 From the point of view of pushing behavior, bookmarks
5280 5282 existing only in the remote repository are treated as
5281 5283 ``deleted``, even if it is in fact added remotely.
5282 5284
5283 5285 Returns 0 if there are outgoing changes, 1 otherwise.
5284 5286 """
5285 5287 if opts.get('graph'):
5286 5288 cmdutil.checkunsupportedgraphflags([], opts)
5287 5289 o, other = hg._outgoing(ui, repo, dest, opts)
5288 5290 if not o:
5289 5291 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5290 5292 return
5291 5293
5292 5294 revdag = cmdutil.graphrevs(repo, o, opts)
5293 5295 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
5294 5296 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
5295 5297 cmdutil.outgoinghooks(ui, repo, other, opts, o)
5296 5298 return 0
5297 5299
5298 5300 if opts.get('bookmarks'):
5299 5301 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5300 5302 dest, branches = hg.parseurl(dest, opts.get('branch'))
5301 5303 other = hg.peer(repo, opts, dest)
5302 5304 if 'bookmarks' not in other.listkeys('namespaces'):
5303 5305 ui.warn(_("remote doesn't support bookmarks\n"))
5304 5306 return 0
5305 5307 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
5306 5308 return bookmarks.outgoing(ui, repo, other)
5307 5309
5308 5310 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
5309 5311 try:
5310 5312 return hg.outgoing(ui, repo, dest, opts)
5311 5313 finally:
5312 5314 del repo._subtoppath
5313 5315
5314 5316 @command('parents',
5315 5317 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
5316 5318 ] + templateopts,
5317 5319 _('[-r REV] [FILE]'),
5318 5320 inferrepo=True)
5319 5321 def parents(ui, repo, file_=None, **opts):
5320 5322 """show the parents of the working directory or revision (DEPRECATED)
5321 5323
5322 5324 Print the working directory's parent revisions. If a revision is
5323 5325 given via -r/--rev, the parent of that revision will be printed.
5324 5326 If a file argument is given, the revision in which the file was
5325 5327 last changed (before the working directory revision or the
5326 5328 argument to --rev if given) is printed.
5327 5329
5328 5330 This command is equivalent to::
5329 5331
5330 5332 hg log -r "p1()+p2()" or
5331 5333 hg log -r "p1(REV)+p2(REV)" or
5332 5334 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5333 5335 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5334 5336
5335 5337 See :hg:`summary` and :hg:`help revsets` for related information.
5336 5338
5337 5339 Returns 0 on success.
5338 5340 """
5339 5341
5340 5342 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
5341 5343
5342 5344 if file_:
5343 5345 m = scmutil.match(ctx, (file_,), opts)
5344 5346 if m.anypats() or len(m.files()) != 1:
5345 5347 raise error.Abort(_('can only specify an explicit filename'))
5346 5348 file_ = m.files()[0]
5347 5349 filenodes = []
5348 5350 for cp in ctx.parents():
5349 5351 if not cp:
5350 5352 continue
5351 5353 try:
5352 5354 filenodes.append(cp.filenode(file_))
5353 5355 except error.LookupError:
5354 5356 pass
5355 5357 if not filenodes:
5356 5358 raise error.Abort(_("'%s' not found in manifest!") % file_)
5357 5359 p = []
5358 5360 for fn in filenodes:
5359 5361 fctx = repo.filectx(file_, fileid=fn)
5360 5362 p.append(fctx.node())
5361 5363 else:
5362 5364 p = [cp.node() for cp in ctx.parents()]
5363 5365
5364 5366 displayer = cmdutil.show_changeset(ui, repo, opts)
5365 5367 for n in p:
5366 5368 if n != nullid:
5367 5369 displayer.show(repo[n])
5368 5370 displayer.close()
5369 5371
5370 5372 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
5371 5373 def paths(ui, repo, search=None, **opts):
5372 5374 """show aliases for remote repositories
5373 5375
5374 5376 Show definition of symbolic path name NAME. If no name is given,
5375 5377 show definition of all available names.
5376 5378
5377 5379 Option -q/--quiet suppresses all output when searching for NAME
5378 5380 and shows only the path names when listing all definitions.
5379 5381
5380 5382 Path names are defined in the [paths] section of your
5381 5383 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5382 5384 repository, ``.hg/hgrc`` is used, too.
5383 5385
5384 5386 The path names ``default`` and ``default-push`` have a special
5385 5387 meaning. When performing a push or pull operation, they are used
5386 5388 as fallbacks if no location is specified on the command-line.
5387 5389 When ``default-push`` is set, it will be used for push and
5388 5390 ``default`` will be used for pull; otherwise ``default`` is used
5389 5391 as the fallback for both. When cloning a repository, the clone
5390 5392 source is written as ``default`` in ``.hg/hgrc``.
5391 5393
5392 5394 .. note::
5393 5395
5394 5396 ``default`` and ``default-push`` apply to all inbound (e.g.
5395 5397 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5396 5398 and :hg:`bundle`) operations.
5397 5399
5398 5400 See :hg:`help urls` for more information.
5399 5401
5400 5402 Returns 0 on success.
5401 5403 """
5402 5404 if search:
5403 5405 pathitems = [(name, path) for name, path in ui.paths.iteritems()
5404 5406 if name == search]
5405 5407 else:
5406 5408 pathitems = sorted(ui.paths.iteritems())
5407 5409
5408 5410 fm = ui.formatter('paths', opts)
5409 5411 if fm:
5410 5412 hidepassword = str
5411 5413 else:
5412 5414 hidepassword = util.hidepassword
5413 5415 if ui.quiet:
5414 5416 namefmt = '%s\n'
5415 5417 else:
5416 5418 namefmt = '%s = '
5417 5419 showsubopts = not search and not ui.quiet
5418 5420
5419 5421 for name, path in pathitems:
5420 5422 fm.startitem()
5421 5423 fm.condwrite(not search, 'name', namefmt, name)
5422 5424 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
5423 5425 for subopt, value in sorted(path.suboptions.items()):
5424 5426 assert subopt not in ('name', 'url')
5425 5427 if showsubopts:
5426 5428 fm.plain('%s:%s = ' % (name, subopt))
5427 5429 fm.condwrite(showsubopts, subopt, '%s\n', value)
5428 5430
5429 5431 fm.end()
5430 5432
5431 5433 if search and not pathitems:
5432 5434 if not ui.quiet:
5433 5435 ui.warn(_("not found!\n"))
5434 5436 return 1
5435 5437 else:
5436 5438 return 0
5437 5439
5438 5440 @command('phase',
5439 5441 [('p', 'public', False, _('set changeset phase to public')),
5440 5442 ('d', 'draft', False, _('set changeset phase to draft')),
5441 5443 ('s', 'secret', False, _('set changeset phase to secret')),
5442 5444 ('f', 'force', False, _('allow to move boundary backward')),
5443 5445 ('r', 'rev', [], _('target revision'), _('REV')),
5444 5446 ],
5445 5447 _('[-p|-d|-s] [-f] [-r] [REV...]'))
5446 5448 def phase(ui, repo, *revs, **opts):
5447 5449 """set or show the current phase name
5448 5450
5449 5451 With no argument, show the phase name of the current revision(s).
5450 5452
5451 5453 With one of -p/--public, -d/--draft or -s/--secret, change the
5452 5454 phase value of the specified revisions.
5453 5455
5454 5456 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
5455 5457 lower phase to an higher phase. Phases are ordered as follows::
5456 5458
5457 5459 public < draft < secret
5458 5460
5459 5461 Returns 0 on success, 1 if some phases could not be changed.
5460 5462
5461 5463 (For more information about the phases concept, see :hg:`help phases`.)
5462 5464 """
5463 5465 # search for a unique phase argument
5464 5466 targetphase = None
5465 5467 for idx, name in enumerate(phases.phasenames):
5466 5468 if opts[name]:
5467 5469 if targetphase is not None:
5468 5470 raise error.Abort(_('only one phase can be specified'))
5469 5471 targetphase = idx
5470 5472
5471 5473 # look for specified revision
5472 5474 revs = list(revs)
5473 5475 revs.extend(opts['rev'])
5474 5476 if not revs:
5475 5477 # display both parents as the second parent phase can influence
5476 5478 # the phase of a merge commit
5477 5479 revs = [c.rev() for c in repo[None].parents()]
5478 5480
5479 5481 revs = scmutil.revrange(repo, revs)
5480 5482
5481 5483 lock = None
5482 5484 ret = 0
5483 5485 if targetphase is None:
5484 5486 # display
5485 5487 for r in revs:
5486 5488 ctx = repo[r]
5487 5489 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5488 5490 else:
5489 5491 tr = None
5490 5492 lock = repo.lock()
5491 5493 try:
5492 5494 tr = repo.transaction("phase")
5493 5495 # set phase
5494 5496 if not revs:
5495 5497 raise error.Abort(_('empty revision set'))
5496 5498 nodes = [repo[r].node() for r in revs]
5497 5499 # moving revision from public to draft may hide them
5498 5500 # We have to check result on an unfiltered repository
5499 5501 unfi = repo.unfiltered()
5500 5502 getphase = unfi._phasecache.phase
5501 5503 olddata = [getphase(unfi, r) for r in unfi]
5502 5504 phases.advanceboundary(repo, tr, targetphase, nodes)
5503 5505 if opts['force']:
5504 5506 phases.retractboundary(repo, tr, targetphase, nodes)
5505 5507 tr.close()
5506 5508 finally:
5507 5509 if tr is not None:
5508 5510 tr.release()
5509 5511 lock.release()
5510 5512 getphase = unfi._phasecache.phase
5511 5513 newdata = [getphase(unfi, r) for r in unfi]
5512 5514 changes = sum(newdata[r] != olddata[r] for r in unfi)
5513 5515 cl = unfi.changelog
5514 5516 rejected = [n for n in nodes
5515 5517 if newdata[cl.rev(n)] < targetphase]
5516 5518 if rejected:
5517 5519 ui.warn(_('cannot move %i changesets to a higher '
5518 5520 'phase, use --force\n') % len(rejected))
5519 5521 ret = 1
5520 5522 if changes:
5521 5523 msg = _('phase changed for %i changesets\n') % changes
5522 5524 if ret:
5523 5525 ui.status(msg)
5524 5526 else:
5525 5527 ui.note(msg)
5526 5528 else:
5527 5529 ui.warn(_('no phases changed\n'))
5528 5530 return ret
5529 5531
5530 5532 def postincoming(ui, repo, modheads, optupdate, checkout):
5531 5533 if modheads == 0:
5532 5534 return
5533 5535 if optupdate:
5534 5536 try:
5535 5537 brev = checkout
5536 5538 movemarkfrom = None
5537 5539 if not checkout:
5538 5540 updata = destutil.destupdate(repo)
5539 5541 checkout, movemarkfrom, brev = updata
5540 5542 ret = hg.update(repo, checkout)
5541 5543 except error.UpdateAbort as inst:
5542 5544 msg = _("not updating: %s") % str(inst)
5543 5545 hint = inst.hint
5544 5546 raise error.UpdateAbort(msg, hint=hint)
5545 5547 if not ret and movemarkfrom:
5546 5548 if movemarkfrom == repo['.'].node():
5547 5549 pass # no-op update
5548 5550 elif bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5549 5551 ui.status(_("updating bookmark %s\n") % repo._activebookmark)
5550 5552 return ret
5551 5553 if modheads > 1:
5552 5554 currentbranchheads = len(repo.branchheads())
5553 5555 if currentbranchheads == modheads:
5554 5556 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
5555 5557 elif currentbranchheads > 1:
5556 5558 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
5557 5559 "merge)\n"))
5558 5560 else:
5559 5561 ui.status(_("(run 'hg heads' to see heads)\n"))
5560 5562 else:
5561 5563 ui.status(_("(run 'hg update' to get a working copy)\n"))
5562 5564
5563 5565 @command('^pull',
5564 5566 [('u', 'update', None,
5565 5567 _('update to new branch head if changesets were pulled')),
5566 5568 ('f', 'force', None, _('run even when remote repository is unrelated')),
5567 5569 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
5568 5570 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
5569 5571 ('b', 'branch', [], _('a specific branch you would like to pull'),
5570 5572 _('BRANCH')),
5571 5573 ] + remoteopts,
5572 5574 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
5573 5575 def pull(ui, repo, source="default", **opts):
5574 5576 """pull changes from the specified source
5575 5577
5576 5578 Pull changes from a remote repository to a local one.
5577 5579
5578 5580 This finds all changes from the repository at the specified path
5579 5581 or URL and adds them to a local repository (the current one unless
5580 5582 -R is specified). By default, this does not update the copy of the
5581 5583 project in the working directory.
5582 5584
5583 5585 Use :hg:`incoming` if you want to see what would have been added
5584 5586 by a pull at the time you issued this command. If you then decide
5585 5587 to add those changes to the repository, you should use :hg:`pull
5586 5588 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5587 5589
5588 5590 If SOURCE is omitted, the 'default' path will be used.
5589 5591 See :hg:`help urls` for more information.
5590 5592
5591 5593 Returns 0 on success, 1 if an update had unresolved files.
5592 5594 """
5593 5595 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
5594 5596 ui.status(_('pulling from %s\n') % util.hidepassword(source))
5595 5597 other = hg.peer(repo, opts, source)
5596 5598 try:
5597 5599 revs, checkout = hg.addbranchrevs(repo, other, branches,
5598 5600 opts.get('rev'))
5599 5601
5600 5602
5601 5603 pullopargs = {}
5602 5604 if opts.get('bookmark'):
5603 5605 if not revs:
5604 5606 revs = []
5605 5607 # The list of bookmark used here is not the one used to actually
5606 5608 # update the bookmark name. This can result in the revision pulled
5607 5609 # not ending up with the name of the bookmark because of a race
5608 5610 # condition on the server. (See issue 4689 for details)
5609 5611 remotebookmarks = other.listkeys('bookmarks')
5610 5612 pullopargs['remotebookmarks'] = remotebookmarks
5611 5613 for b in opts['bookmark']:
5612 5614 if b not in remotebookmarks:
5613 5615 raise error.Abort(_('remote bookmark %s not found!') % b)
5614 5616 revs.append(remotebookmarks[b])
5615 5617
5616 5618 if revs:
5617 5619 try:
5618 5620 # When 'rev' is a bookmark name, we cannot guarantee that it
5619 5621 # will be updated with that name because of a race condition
5620 5622 # server side. (See issue 4689 for details)
5621 5623 oldrevs = revs
5622 5624 revs = [] # actually, nodes
5623 5625 for r in oldrevs:
5624 5626 node = other.lookup(r)
5625 5627 revs.append(node)
5626 5628 if r == checkout:
5627 5629 checkout = node
5628 5630 except error.CapabilityError:
5629 5631 err = _("other repository doesn't support revision lookup, "
5630 5632 "so a rev cannot be specified.")
5631 5633 raise error.Abort(err)
5632 5634
5633 5635 pullopargs.update(opts.get('opargs', {}))
5634 5636 modheads = exchange.pull(repo, other, heads=revs,
5635 5637 force=opts.get('force'),
5636 5638 bookmarks=opts.get('bookmark', ()),
5637 5639 opargs=pullopargs).cgresult
5638 5640 if checkout:
5639 5641 checkout = str(repo.changelog.rev(checkout))
5640 5642 repo._subtoppath = source
5641 5643 try:
5642 5644 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
5643 5645
5644 5646 finally:
5645 5647 del repo._subtoppath
5646 5648
5647 5649 finally:
5648 5650 other.close()
5649 5651 return ret
5650 5652
5651 5653 @command('^push',
5652 5654 [('f', 'force', None, _('force push')),
5653 5655 ('r', 'rev', [],
5654 5656 _('a changeset intended to be included in the destination'),
5655 5657 _('REV')),
5656 5658 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5657 5659 ('b', 'branch', [],
5658 5660 _('a specific branch you would like to push'), _('BRANCH')),
5659 5661 ('', 'new-branch', False, _('allow pushing a new branch')),
5660 5662 ] + remoteopts,
5661 5663 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5662 5664 def push(ui, repo, dest=None, **opts):
5663 5665 """push changes to the specified destination
5664 5666
5665 5667 Push changesets from the local repository to the specified
5666 5668 destination.
5667 5669
5668 5670 This operation is symmetrical to pull: it is identical to a pull
5669 5671 in the destination repository from the current one.
5670 5672
5671 5673 By default, push will not allow creation of new heads at the
5672 5674 destination, since multiple heads would make it unclear which head
5673 5675 to use. In this situation, it is recommended to pull and merge
5674 5676 before pushing.
5675 5677
5676 5678 Use --new-branch if you want to allow push to create a new named
5677 5679 branch that is not present at the destination. This allows you to
5678 5680 only create a new branch without forcing other changes.
5679 5681
5680 5682 .. note::
5681 5683
5682 5684 Extra care should be taken with the -f/--force option,
5683 5685 which will push all new heads on all branches, an action which will
5684 5686 almost always cause confusion for collaborators.
5685 5687
5686 5688 If -r/--rev is used, the specified revision and all its ancestors
5687 5689 will be pushed to the remote repository.
5688 5690
5689 5691 If -B/--bookmark is used, the specified bookmarked revision, its
5690 5692 ancestors, and the bookmark will be pushed to the remote
5691 5693 repository.
5692 5694
5693 5695 Please see :hg:`help urls` for important details about ``ssh://``
5694 5696 URLs. If DESTINATION is omitted, a default path will be used.
5695 5697
5696 5698 Returns 0 if push was successful, 1 if nothing to push.
5697 5699 """
5698 5700
5699 5701 if opts.get('bookmark'):
5700 5702 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5701 5703 for b in opts['bookmark']:
5702 5704 # translate -B options to -r so changesets get pushed
5703 5705 if b in repo._bookmarks:
5704 5706 opts.setdefault('rev', []).append(b)
5705 5707 else:
5706 5708 # if we try to push a deleted bookmark, translate it to null
5707 5709 # this lets simultaneous -r, -b options continue working
5708 5710 opts.setdefault('rev', []).append("null")
5709 5711
5710 5712 path = ui.paths.getpath(dest, default=('default-push', 'default'))
5711 5713 if not path:
5712 5714 raise error.Abort(_('default repository not configured!'),
5713 5715 hint=_('see the "path" section in "hg help config"'))
5714 5716 dest = path.pushloc or path.loc
5715 5717 branches = (path.branch, opts.get('branch') or [])
5716 5718 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5717 5719 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5718 5720 other = hg.peer(repo, opts, dest)
5719 5721
5720 5722 if revs:
5721 5723 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5722 5724 if not revs:
5723 5725 raise error.Abort(_("specified revisions evaluate to an empty set"),
5724 5726 hint=_("use different revision arguments"))
5725 5727
5726 5728 repo._subtoppath = dest
5727 5729 try:
5728 5730 # push subrepos depth-first for coherent ordering
5729 5731 c = repo['']
5730 5732 subs = c.substate # only repos that are committed
5731 5733 for s in sorted(subs):
5732 5734 result = c.sub(s).push(opts)
5733 5735 if result == 0:
5734 5736 return not result
5735 5737 finally:
5736 5738 del repo._subtoppath
5737 5739 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5738 5740 newbranch=opts.get('new_branch'),
5739 5741 bookmarks=opts.get('bookmark', ()),
5740 5742 opargs=opts.get('opargs'))
5741 5743
5742 5744 result = not pushop.cgresult
5743 5745
5744 5746 if pushop.bkresult is not None:
5745 5747 if pushop.bkresult == 2:
5746 5748 result = 2
5747 5749 elif not result and pushop.bkresult:
5748 5750 result = 2
5749 5751
5750 5752 return result
5751 5753
5752 5754 @command('recover', [])
5753 5755 def recover(ui, repo):
5754 5756 """roll back an interrupted transaction
5755 5757
5756 5758 Recover from an interrupted commit or pull.
5757 5759
5758 5760 This command tries to fix the repository status after an
5759 5761 interrupted operation. It should only be necessary when Mercurial
5760 5762 suggests it.
5761 5763
5762 5764 Returns 0 if successful, 1 if nothing to recover or verify fails.
5763 5765 """
5764 5766 if repo.recover():
5765 5767 return hg.verify(repo)
5766 5768 return 1
5767 5769
5768 5770 @command('^remove|rm',
5769 5771 [('A', 'after', None, _('record delete for missing files')),
5770 5772 ('f', 'force', None,
5771 5773 _('remove (and delete) file even if added or modified')),
5772 5774 ] + subrepoopts + walkopts,
5773 5775 _('[OPTION]... FILE...'),
5774 5776 inferrepo=True)
5775 5777 def remove(ui, repo, *pats, **opts):
5776 5778 """remove the specified files on the next commit
5777 5779
5778 5780 Schedule the indicated files for removal from the current branch.
5779 5781
5780 5782 This command schedules the files to be removed at the next commit.
5781 5783 To undo a remove before that, see :hg:`revert`. To undo added
5782 5784 files, see :hg:`forget`.
5783 5785
5784 5786 .. container:: verbose
5785 5787
5786 5788 -A/--after can be used to remove only files that have already
5787 5789 been deleted, -f/--force can be used to force deletion, and -Af
5788 5790 can be used to remove files from the next revision without
5789 5791 deleting them from the working directory.
5790 5792
5791 5793 The following table details the behavior of remove for different
5792 5794 file states (columns) and option combinations (rows). The file
5793 5795 states are Added [A], Clean [C], Modified [M] and Missing [!]
5794 5796 (as reported by :hg:`status`). The actions are Warn, Remove
5795 5797 (from branch) and Delete (from disk):
5796 5798
5797 5799 ========= == == == ==
5798 5800 opt/state A C M !
5799 5801 ========= == == == ==
5800 5802 none W RD W R
5801 5803 -f R RD RD R
5802 5804 -A W W W R
5803 5805 -Af R R R R
5804 5806 ========= == == == ==
5805 5807
5806 5808 .. note::
5807 5809
5808 5810 :hg:`remove` never deletes files in Added [A] state from the
5809 5811 working directory, not even if ``--force`` is specified.
5810 5812
5811 5813 Returns 0 on success, 1 if any warnings encountered.
5812 5814 """
5813 5815
5814 5816 after, force = opts.get('after'), opts.get('force')
5815 5817 if not pats and not after:
5816 5818 raise error.Abort(_('no files specified'))
5817 5819
5818 5820 m = scmutil.match(repo[None], pats, opts)
5819 5821 subrepos = opts.get('subrepos')
5820 5822 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5821 5823
5822 5824 @command('rename|move|mv',
5823 5825 [('A', 'after', None, _('record a rename that has already occurred')),
5824 5826 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5825 5827 ] + walkopts + dryrunopts,
5826 5828 _('[OPTION]... SOURCE... DEST'))
5827 5829 def rename(ui, repo, *pats, **opts):
5828 5830 """rename files; equivalent of copy + remove
5829 5831
5830 5832 Mark dest as copies of sources; mark sources for deletion. If dest
5831 5833 is a directory, copies are put in that directory. If dest is a
5832 5834 file, there can only be one source.
5833 5835
5834 5836 By default, this command copies the contents of files as they
5835 5837 exist in the working directory. If invoked with -A/--after, the
5836 5838 operation is recorded, but no copying is performed.
5837 5839
5838 5840 This command takes effect at the next commit. To undo a rename
5839 5841 before that, see :hg:`revert`.
5840 5842
5841 5843 Returns 0 on success, 1 if errors are encountered.
5842 5844 """
5843 5845 with repo.wlock(False):
5844 5846 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5845 5847
5846 5848 @command('resolve',
5847 5849 [('a', 'all', None, _('select all unresolved files')),
5848 5850 ('l', 'list', None, _('list state of files needing merge')),
5849 5851 ('m', 'mark', None, _('mark files as resolved')),
5850 5852 ('u', 'unmark', None, _('mark files as unresolved')),
5851 5853 ('n', 'no-status', None, _('hide status prefix'))]
5852 5854 + mergetoolopts + walkopts + formatteropts,
5853 5855 _('[OPTION]... [FILE]...'),
5854 5856 inferrepo=True)
5855 5857 def resolve(ui, repo, *pats, **opts):
5856 5858 """redo merges or set/view the merge status of files
5857 5859
5858 5860 Merges with unresolved conflicts are often the result of
5859 5861 non-interactive merging using the ``internal:merge`` configuration
5860 5862 setting, or a command-line merge tool like ``diff3``. The resolve
5861 5863 command is used to manage the files involved in a merge, after
5862 5864 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5863 5865 working directory must have two parents). See :hg:`help
5864 5866 merge-tools` for information on configuring merge tools.
5865 5867
5866 5868 The resolve command can be used in the following ways:
5867 5869
5868 5870 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5869 5871 files, discarding any previous merge attempts. Re-merging is not
5870 5872 performed for files already marked as resolved. Use ``--all/-a``
5871 5873 to select all unresolved files. ``--tool`` can be used to specify
5872 5874 the merge tool used for the given files. It overrides the HGMERGE
5873 5875 environment variable and your configuration files. Previous file
5874 5876 contents are saved with a ``.orig`` suffix.
5875 5877
5876 5878 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5877 5879 (e.g. after having manually fixed-up the files). The default is
5878 5880 to mark all unresolved files.
5879 5881
5880 5882 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5881 5883 default is to mark all resolved files.
5882 5884
5883 5885 - :hg:`resolve -l`: list files which had or still have conflicts.
5884 5886 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5885 5887
5886 5888 .. note::
5887 5889
5888 5890 Mercurial will not let you commit files with unresolved merge
5889 5891 conflicts. You must use :hg:`resolve -m ...` before you can
5890 5892 commit after a conflicting merge.
5891 5893
5892 5894 Returns 0 on success, 1 if any files fail a resolve attempt.
5893 5895 """
5894 5896
5895 5897 all, mark, unmark, show, nostatus = \
5896 5898 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
5897 5899
5898 5900 if (show and (mark or unmark)) or (mark and unmark):
5899 5901 raise error.Abort(_("too many options specified"))
5900 5902 if pats and all:
5901 5903 raise error.Abort(_("can't specify --all and patterns"))
5902 5904 if not (all or pats or show or mark or unmark):
5903 5905 raise error.Abort(_('no files or directories specified'),
5904 5906 hint=('use --all to re-merge all unresolved files'))
5905 5907
5906 5908 if show:
5907 5909 fm = ui.formatter('resolve', opts)
5908 5910 ms = mergemod.mergestate.read(repo)
5909 5911 m = scmutil.match(repo[None], pats, opts)
5910 5912 for f in ms:
5911 5913 if not m(f):
5912 5914 continue
5913 5915 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
5914 5916 'd': 'driverresolved'}[ms[f]]
5915 5917 fm.startitem()
5916 5918 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
5917 5919 fm.write('path', '%s\n', f, label=l)
5918 5920 fm.end()
5919 5921 return 0
5920 5922
5921 5923 with repo.wlock():
5922 5924 ms = mergemod.mergestate.read(repo)
5923 5925
5924 5926 if not (ms.active() or repo.dirstate.p2() != nullid):
5925 5927 raise error.Abort(
5926 5928 _('resolve command not applicable when not merging'))
5927 5929
5928 5930 wctx = repo[None]
5929 5931
5930 5932 if ms.mergedriver and ms.mdstate() == 'u':
5931 5933 proceed = mergemod.driverpreprocess(repo, ms, wctx)
5932 5934 ms.commit()
5933 5935 # allow mark and unmark to go through
5934 5936 if not mark and not unmark and not proceed:
5935 5937 return 1
5936 5938
5937 5939 m = scmutil.match(wctx, pats, opts)
5938 5940 ret = 0
5939 5941 didwork = False
5940 5942 runconclude = False
5941 5943
5942 5944 tocomplete = []
5943 5945 for f in ms:
5944 5946 if not m(f):
5945 5947 continue
5946 5948
5947 5949 didwork = True
5948 5950
5949 5951 # don't let driver-resolved files be marked, and run the conclude
5950 5952 # step if asked to resolve
5951 5953 if ms[f] == "d":
5952 5954 exact = m.exact(f)
5953 5955 if mark:
5954 5956 if exact:
5955 5957 ui.warn(_('not marking %s as it is driver-resolved\n')
5956 5958 % f)
5957 5959 elif unmark:
5958 5960 if exact:
5959 5961 ui.warn(_('not unmarking %s as it is driver-resolved\n')
5960 5962 % f)
5961 5963 else:
5962 5964 runconclude = True
5963 5965 continue
5964 5966
5965 5967 if mark:
5966 5968 ms.mark(f, "r")
5967 5969 elif unmark:
5968 5970 ms.mark(f, "u")
5969 5971 else:
5970 5972 # backup pre-resolve (merge uses .orig for its own purposes)
5971 5973 a = repo.wjoin(f)
5972 5974 try:
5973 5975 util.copyfile(a, a + ".resolve")
5974 5976 except (IOError, OSError) as inst:
5975 5977 if inst.errno != errno.ENOENT:
5976 5978 raise
5977 5979
5978 5980 try:
5979 5981 # preresolve file
5980 5982 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5981 5983 'resolve')
5982 5984 complete, r = ms.preresolve(f, wctx)
5983 5985 if not complete:
5984 5986 tocomplete.append(f)
5985 5987 elif r:
5986 5988 ret = 1
5987 5989 finally:
5988 5990 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5989 5991 ms.commit()
5990 5992
5991 5993 # replace filemerge's .orig file with our resolve file, but only
5992 5994 # for merges that are complete
5993 5995 if complete:
5994 5996 try:
5995 5997 util.rename(a + ".resolve",
5996 5998 scmutil.origpath(ui, repo, a))
5997 5999 except OSError as inst:
5998 6000 if inst.errno != errno.ENOENT:
5999 6001 raise
6000 6002
6001 6003 for f in tocomplete:
6002 6004 try:
6003 6005 # resolve file
6004 6006 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
6005 6007 'resolve')
6006 6008 r = ms.resolve(f, wctx)
6007 6009 if r:
6008 6010 ret = 1
6009 6011 finally:
6010 6012 ui.setconfig('ui', 'forcemerge', '', 'resolve')
6011 6013 ms.commit()
6012 6014
6013 6015 # replace filemerge's .orig file with our resolve file
6014 6016 a = repo.wjoin(f)
6015 6017 try:
6016 6018 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
6017 6019 except OSError as inst:
6018 6020 if inst.errno != errno.ENOENT:
6019 6021 raise
6020 6022
6021 6023 ms.commit()
6022 6024 ms.recordactions()
6023 6025
6024 6026 if not didwork and pats:
6025 6027 ui.warn(_("arguments do not match paths that need resolving\n"))
6026 6028 elif ms.mergedriver and ms.mdstate() != 's':
6027 6029 # run conclude step when either a driver-resolved file is requested
6028 6030 # or there are no driver-resolved files
6029 6031 # we can't use 'ret' to determine whether any files are unresolved
6030 6032 # because we might not have tried to resolve some
6031 6033 if ((runconclude or not list(ms.driverresolved()))
6032 6034 and not list(ms.unresolved())):
6033 6035 proceed = mergemod.driverconclude(repo, ms, wctx)
6034 6036 ms.commit()
6035 6037 if not proceed:
6036 6038 return 1
6037 6039
6038 6040 # Nudge users into finishing an unfinished operation
6039 6041 unresolvedf = list(ms.unresolved())
6040 6042 driverresolvedf = list(ms.driverresolved())
6041 6043 if not unresolvedf and not driverresolvedf:
6042 6044 ui.status(_('(no more unresolved files)\n'))
6043 6045 cmdutil.checkafterresolved(repo)
6044 6046 elif not unresolvedf:
6045 6047 ui.status(_('(no more unresolved files -- '
6046 6048 'run "hg resolve --all" to conclude)\n'))
6047 6049
6048 6050 return ret
6049 6051
6050 6052 @command('revert',
6051 6053 [('a', 'all', None, _('revert all changes when no arguments given')),
6052 6054 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6053 6055 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
6054 6056 ('C', 'no-backup', None, _('do not save backup copies of files')),
6055 6057 ('i', 'interactive', None,
6056 6058 _('interactively select the changes (EXPERIMENTAL)')),
6057 6059 ] + walkopts + dryrunopts,
6058 6060 _('[OPTION]... [-r REV] [NAME]...'))
6059 6061 def revert(ui, repo, *pats, **opts):
6060 6062 """restore files to their checkout state
6061 6063
6062 6064 .. note::
6063 6065
6064 6066 To check out earlier revisions, you should use :hg:`update REV`.
6065 6067 To cancel an uncommitted merge (and lose your changes),
6066 6068 use :hg:`update --clean .`.
6067 6069
6068 6070 With no revision specified, revert the specified files or directories
6069 6071 to the contents they had in the parent of the working directory.
6070 6072 This restores the contents of files to an unmodified
6071 6073 state and unschedules adds, removes, copies, and renames. If the
6072 6074 working directory has two parents, you must explicitly specify a
6073 6075 revision.
6074 6076
6075 6077 Using the -r/--rev or -d/--date options, revert the given files or
6076 6078 directories to their states as of a specific revision. Because
6077 6079 revert does not change the working directory parents, this will
6078 6080 cause these files to appear modified. This can be helpful to "back
6079 6081 out" some or all of an earlier change. See :hg:`backout` for a
6080 6082 related method.
6081 6083
6082 6084 Modified files are saved with a .orig suffix before reverting.
6083 6085 To disable these backups, use --no-backup.
6084 6086
6085 6087 See :hg:`help dates` for a list of formats valid for -d/--date.
6086 6088
6087 6089 See :hg:`help backout` for a way to reverse the effect of an
6088 6090 earlier changeset.
6089 6091
6090 6092 Returns 0 on success.
6091 6093 """
6092 6094
6093 6095 if opts.get("date"):
6094 6096 if opts.get("rev"):
6095 6097 raise error.Abort(_("you can't specify a revision and a date"))
6096 6098 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
6097 6099
6098 6100 parent, p2 = repo.dirstate.parents()
6099 6101 if not opts.get('rev') and p2 != nullid:
6100 6102 # revert after merge is a trap for new users (issue2915)
6101 6103 raise error.Abort(_('uncommitted merge with no revision specified'),
6102 6104 hint=_('use "hg update" or see "hg help revert"'))
6103 6105
6104 6106 ctx = scmutil.revsingle(repo, opts.get('rev'))
6105 6107
6106 6108 if (not (pats or opts.get('include') or opts.get('exclude') or
6107 6109 opts.get('all') or opts.get('interactive'))):
6108 6110 msg = _("no files or directories specified")
6109 6111 if p2 != nullid:
6110 6112 hint = _("uncommitted merge, use --all to discard all changes,"
6111 6113 " or 'hg update -C .' to abort the merge")
6112 6114 raise error.Abort(msg, hint=hint)
6113 6115 dirty = any(repo.status())
6114 6116 node = ctx.node()
6115 6117 if node != parent:
6116 6118 if dirty:
6117 6119 hint = _("uncommitted changes, use --all to discard all"
6118 6120 " changes, or 'hg update %s' to update") % ctx.rev()
6119 6121 else:
6120 6122 hint = _("use --all to revert all files,"
6121 6123 " or 'hg update %s' to update") % ctx.rev()
6122 6124 elif dirty:
6123 6125 hint = _("uncommitted changes, use --all to discard all changes")
6124 6126 else:
6125 6127 hint = _("use --all to revert all files")
6126 6128 raise error.Abort(msg, hint=hint)
6127 6129
6128 6130 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
6129 6131
6130 6132 @command('rollback', dryrunopts +
6131 6133 [('f', 'force', False, _('ignore safety measures'))])
6132 6134 def rollback(ui, repo, **opts):
6133 6135 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6134 6136
6135 6137 Please use :hg:`commit --amend` instead of rollback to correct
6136 6138 mistakes in the last commit.
6137 6139
6138 6140 This command should be used with care. There is only one level of
6139 6141 rollback, and there is no way to undo a rollback. It will also
6140 6142 restore the dirstate at the time of the last transaction, losing
6141 6143 any dirstate changes since that time. This command does not alter
6142 6144 the working directory.
6143 6145
6144 6146 Transactions are used to encapsulate the effects of all commands
6145 6147 that create new changesets or propagate existing changesets into a
6146 6148 repository.
6147 6149
6148 6150 .. container:: verbose
6149 6151
6150 6152 For example, the following commands are transactional, and their
6151 6153 effects can be rolled back:
6152 6154
6153 6155 - commit
6154 6156 - import
6155 6157 - pull
6156 6158 - push (with this repository as the destination)
6157 6159 - unbundle
6158 6160
6159 6161 To avoid permanent data loss, rollback will refuse to rollback a
6160 6162 commit transaction if it isn't checked out. Use --force to
6161 6163 override this protection.
6162 6164
6163 6165 This command is not intended for use on public repositories. Once
6164 6166 changes are visible for pull by other users, rolling a transaction
6165 6167 back locally is ineffective (someone else may already have pulled
6166 6168 the changes). Furthermore, a race is possible with readers of the
6167 6169 repository; for example an in-progress pull from the repository
6168 6170 may fail if a rollback is performed.
6169 6171
6170 6172 Returns 0 on success, 1 if no rollback data is available.
6171 6173 """
6172 6174 return repo.rollback(dryrun=opts.get('dry_run'),
6173 6175 force=opts.get('force'))
6174 6176
6175 6177 @command('root', [])
6176 6178 def root(ui, repo):
6177 6179 """print the root (top) of the current working directory
6178 6180
6179 6181 Print the root directory of the current repository.
6180 6182
6181 6183 Returns 0 on success.
6182 6184 """
6183 6185 ui.write(repo.root + "\n")
6184 6186
6185 6187 @command('^serve',
6186 6188 [('A', 'accesslog', '', _('name of access log file to write to'),
6187 6189 _('FILE')),
6188 6190 ('d', 'daemon', None, _('run server in background')),
6189 6191 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('FILE')),
6190 6192 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
6191 6193 # use string type, then we can check if something was passed
6192 6194 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
6193 6195 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
6194 6196 _('ADDR')),
6195 6197 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
6196 6198 _('PREFIX')),
6197 6199 ('n', 'name', '',
6198 6200 _('name to show in web pages (default: working directory)'), _('NAME')),
6199 6201 ('', 'web-conf', '',
6200 6202 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
6201 6203 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
6202 6204 _('FILE')),
6203 6205 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
6204 6206 ('', 'stdio', None, _('for remote clients')),
6205 6207 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
6206 6208 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
6207 6209 ('', 'style', '', _('template style to use'), _('STYLE')),
6208 6210 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
6209 6211 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
6210 6212 _('[OPTION]...'),
6211 6213 optionalrepo=True)
6212 6214 def serve(ui, repo, **opts):
6213 6215 """start stand-alone webserver
6214 6216
6215 6217 Start a local HTTP repository browser and pull server. You can use
6216 6218 this for ad-hoc sharing and browsing of repositories. It is
6217 6219 recommended to use a real web server to serve a repository for
6218 6220 longer periods of time.
6219 6221
6220 6222 Please note that the server does not implement access control.
6221 6223 This means that, by default, anybody can read from the server and
6222 6224 nobody can write to it by default. Set the ``web.allow_push``
6223 6225 option to ``*`` to allow everybody to push to the server. You
6224 6226 should use a real web server if you need to authenticate users.
6225 6227
6226 6228 By default, the server logs accesses to stdout and errors to
6227 6229 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6228 6230 files.
6229 6231
6230 6232 To have the server choose a free port number to listen on, specify
6231 6233 a port number of 0; in this case, the server will print the port
6232 6234 number it uses.
6233 6235
6234 6236 Returns 0 on success.
6235 6237 """
6236 6238
6237 6239 if opts["stdio"] and opts["cmdserver"]:
6238 6240 raise error.Abort(_("cannot use --stdio with --cmdserver"))
6239 6241
6240 6242 if opts["stdio"]:
6241 6243 if repo is None:
6242 6244 raise error.RepoError(_("there is no Mercurial repository here"
6243 6245 " (.hg not found)"))
6244 6246 s = sshserver.sshserver(ui, repo)
6245 6247 s.serve_forever()
6246 6248
6247 6249 if opts["cmdserver"]:
6248 6250 service = commandserver.createservice(ui, repo, opts)
6249 6251 else:
6250 6252 service = hgweb.createservice(ui, repo, opts)
6251 6253 return cmdutil.service(opts, initfn=service.init, runfn=service.run)
6252 6254
6253 6255 @command('^status|st',
6254 6256 [('A', 'all', None, _('show status of all files')),
6255 6257 ('m', 'modified', None, _('show only modified files')),
6256 6258 ('a', 'added', None, _('show only added files')),
6257 6259 ('r', 'removed', None, _('show only removed files')),
6258 6260 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
6259 6261 ('c', 'clean', None, _('show only files without changes')),
6260 6262 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
6261 6263 ('i', 'ignored', None, _('show only ignored files')),
6262 6264 ('n', 'no-status', None, _('hide status prefix')),
6263 6265 ('C', 'copies', None, _('show source of copied files')),
6264 6266 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
6265 6267 ('', 'rev', [], _('show difference from revision'), _('REV')),
6266 6268 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
6267 6269 ] + walkopts + subrepoopts + formatteropts,
6268 6270 _('[OPTION]... [FILE]...'),
6269 6271 inferrepo=True)
6270 6272 def status(ui, repo, *pats, **opts):
6271 6273 """show changed files in the working directory
6272 6274
6273 6275 Show status of files in the repository. If names are given, only
6274 6276 files that match are shown. Files that are clean or ignored or
6275 6277 the source of a copy/move operation, are not listed unless
6276 6278 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6277 6279 Unless options described with "show only ..." are given, the
6278 6280 options -mardu are used.
6279 6281
6280 6282 Option -q/--quiet hides untracked (unknown and ignored) files
6281 6283 unless explicitly requested with -u/--unknown or -i/--ignored.
6282 6284
6283 6285 .. note::
6284 6286
6285 6287 :hg:`status` may appear to disagree with diff if permissions have
6286 6288 changed or a merge has occurred. The standard diff format does
6287 6289 not report permission changes and diff only reports changes
6288 6290 relative to one merge parent.
6289 6291
6290 6292 If one revision is given, it is used as the base revision.
6291 6293 If two revisions are given, the differences between them are
6292 6294 shown. The --change option can also be used as a shortcut to list
6293 6295 the changed files of a revision from its first parent.
6294 6296
6295 6297 The codes used to show the status of files are::
6296 6298
6297 6299 M = modified
6298 6300 A = added
6299 6301 R = removed
6300 6302 C = clean
6301 6303 ! = missing (deleted by non-hg command, but still tracked)
6302 6304 ? = not tracked
6303 6305 I = ignored
6304 6306 = origin of the previous file (with --copies)
6305 6307
6306 6308 .. container:: verbose
6307 6309
6308 6310 Examples:
6309 6311
6310 6312 - show changes in the working directory relative to a
6311 6313 changeset::
6312 6314
6313 6315 hg status --rev 9353
6314 6316
6315 6317 - show changes in the working directory relative to the
6316 6318 current directory (see :hg:`help patterns` for more information)::
6317 6319
6318 6320 hg status re:
6319 6321
6320 6322 - show all changes including copies in an existing changeset::
6321 6323
6322 6324 hg status --copies --change 9353
6323 6325
6324 6326 - get a NUL separated list of added files, suitable for xargs::
6325 6327
6326 6328 hg status -an0
6327 6329
6328 6330 Returns 0 on success.
6329 6331 """
6330 6332
6331 6333 revs = opts.get('rev')
6332 6334 change = opts.get('change')
6333 6335
6334 6336 if revs and change:
6335 6337 msg = _('cannot specify --rev and --change at the same time')
6336 6338 raise error.Abort(msg)
6337 6339 elif change:
6338 6340 node2 = scmutil.revsingle(repo, change, None).node()
6339 6341 node1 = repo[node2].p1().node()
6340 6342 else:
6341 6343 node1, node2 = scmutil.revpair(repo, revs)
6342 6344
6343 6345 if pats:
6344 6346 cwd = repo.getcwd()
6345 6347 else:
6346 6348 cwd = ''
6347 6349
6348 6350 if opts.get('print0'):
6349 6351 end = '\0'
6350 6352 else:
6351 6353 end = '\n'
6352 6354 copy = {}
6353 6355 states = 'modified added removed deleted unknown ignored clean'.split()
6354 6356 show = [k for k in states if opts.get(k)]
6355 6357 if opts.get('all'):
6356 6358 show += ui.quiet and (states[:4] + ['clean']) or states
6357 6359 if not show:
6358 6360 if ui.quiet:
6359 6361 show = states[:4]
6360 6362 else:
6361 6363 show = states[:5]
6362 6364
6363 6365 m = scmutil.match(repo[node2], pats, opts)
6364 6366 stat = repo.status(node1, node2, m,
6365 6367 'ignored' in show, 'clean' in show, 'unknown' in show,
6366 6368 opts.get('subrepos'))
6367 6369 changestates = zip(states, 'MAR!?IC', stat)
6368 6370
6369 6371 if (opts.get('all') or opts.get('copies')
6370 6372 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
6371 6373 copy = copies.pathcopies(repo[node1], repo[node2], m)
6372 6374
6373 6375 fm = ui.formatter('status', opts)
6374 6376 fmt = '%s' + end
6375 6377 showchar = not opts.get('no_status')
6376 6378
6377 6379 for state, char, files in changestates:
6378 6380 if state in show:
6379 6381 label = 'status.' + state
6380 6382 for f in files:
6381 6383 fm.startitem()
6382 6384 fm.condwrite(showchar, 'status', '%s ', char, label=label)
6383 6385 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
6384 6386 if f in copy:
6385 6387 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
6386 6388 label='status.copied')
6387 6389 fm.end()
6388 6390
6389 6391 @command('^summary|sum',
6390 6392 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
6391 6393 def summary(ui, repo, **opts):
6392 6394 """summarize working directory state
6393 6395
6394 6396 This generates a brief summary of the working directory state,
6395 6397 including parents, branch, commit status, phase and available updates.
6396 6398
6397 6399 With the --remote option, this will check the default paths for
6398 6400 incoming and outgoing changes. This can be time-consuming.
6399 6401
6400 6402 Returns 0 on success.
6401 6403 """
6402 6404
6403 6405 ctx = repo[None]
6404 6406 parents = ctx.parents()
6405 6407 pnode = parents[0].node()
6406 6408 marks = []
6407 6409
6408 6410 for p in parents:
6409 6411 # label with log.changeset (instead of log.parent) since this
6410 6412 # shows a working directory parent *changeset*:
6411 6413 # i18n: column positioning for "hg summary"
6412 6414 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
6413 6415 label='log.changeset changeset.%s' % p.phasestr())
6414 6416 ui.write(' '.join(p.tags()), label='log.tag')
6415 6417 if p.bookmarks():
6416 6418 marks.extend(p.bookmarks())
6417 6419 if p.rev() == -1:
6418 6420 if not len(repo):
6419 6421 ui.write(_(' (empty repository)'))
6420 6422 else:
6421 6423 ui.write(_(' (no revision checked out)'))
6422 6424 ui.write('\n')
6423 6425 if p.description():
6424 6426 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
6425 6427 label='log.summary')
6426 6428
6427 6429 branch = ctx.branch()
6428 6430 bheads = repo.branchheads(branch)
6429 6431 # i18n: column positioning for "hg summary"
6430 6432 m = _('branch: %s\n') % branch
6431 6433 if branch != 'default':
6432 6434 ui.write(m, label='log.branch')
6433 6435 else:
6434 6436 ui.status(m, label='log.branch')
6435 6437
6436 6438 if marks:
6437 6439 active = repo._activebookmark
6438 6440 # i18n: column positioning for "hg summary"
6439 6441 ui.write(_('bookmarks:'), label='log.bookmark')
6440 6442 if active is not None:
6441 6443 if active in marks:
6442 6444 ui.write(' *' + active, label=activebookmarklabel)
6443 6445 marks.remove(active)
6444 6446 else:
6445 6447 ui.write(' [%s]' % active, label=activebookmarklabel)
6446 6448 for m in marks:
6447 6449 ui.write(' ' + m, label='log.bookmark')
6448 6450 ui.write('\n', label='log.bookmark')
6449 6451
6450 6452 status = repo.status(unknown=True)
6451 6453
6452 6454 c = repo.dirstate.copies()
6453 6455 copied, renamed = [], []
6454 6456 for d, s in c.iteritems():
6455 6457 if s in status.removed:
6456 6458 status.removed.remove(s)
6457 6459 renamed.append(d)
6458 6460 else:
6459 6461 copied.append(d)
6460 6462 if d in status.added:
6461 6463 status.added.remove(d)
6462 6464
6463 6465 try:
6464 6466 ms = mergemod.mergestate.read(repo)
6465 6467 except error.UnsupportedMergeRecords as e:
6466 6468 s = ' '.join(e.recordtypes)
6467 6469 ui.warn(
6468 6470 _('warning: merge state has unsupported record types: %s\n') % s)
6469 6471 unresolved = 0
6470 6472 else:
6471 6473 unresolved = [f for f in ms if ms[f] == 'u']
6472 6474
6473 6475 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6474 6476
6475 6477 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
6476 6478 (ui.label(_('%d added'), 'status.added'), status.added),
6477 6479 (ui.label(_('%d removed'), 'status.removed'), status.removed),
6478 6480 (ui.label(_('%d renamed'), 'status.copied'), renamed),
6479 6481 (ui.label(_('%d copied'), 'status.copied'), copied),
6480 6482 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
6481 6483 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
6482 6484 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
6483 6485 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
6484 6486 t = []
6485 6487 for l, s in labels:
6486 6488 if s:
6487 6489 t.append(l % len(s))
6488 6490
6489 6491 t = ', '.join(t)
6490 6492 cleanworkdir = False
6491 6493
6492 6494 if repo.vfs.exists('graftstate'):
6493 6495 t += _(' (graft in progress)')
6494 6496 if repo.vfs.exists('updatestate'):
6495 6497 t += _(' (interrupted update)')
6496 6498 elif len(parents) > 1:
6497 6499 t += _(' (merge)')
6498 6500 elif branch != parents[0].branch():
6499 6501 t += _(' (new branch)')
6500 6502 elif (parents[0].closesbranch() and
6501 6503 pnode in repo.branchheads(branch, closed=True)):
6502 6504 t += _(' (head closed)')
6503 6505 elif not (status.modified or status.added or status.removed or renamed or
6504 6506 copied or subs):
6505 6507 t += _(' (clean)')
6506 6508 cleanworkdir = True
6507 6509 elif pnode not in bheads:
6508 6510 t += _(' (new branch head)')
6509 6511
6510 6512 if parents:
6511 6513 pendingphase = max(p.phase() for p in parents)
6512 6514 else:
6513 6515 pendingphase = phases.public
6514 6516
6515 6517 if pendingphase > phases.newcommitphase(ui):
6516 6518 t += ' (%s)' % phases.phasenames[pendingphase]
6517 6519
6518 6520 if cleanworkdir:
6519 6521 # i18n: column positioning for "hg summary"
6520 6522 ui.status(_('commit: %s\n') % t.strip())
6521 6523 else:
6522 6524 # i18n: column positioning for "hg summary"
6523 6525 ui.write(_('commit: %s\n') % t.strip())
6524 6526
6525 6527 # all ancestors of branch heads - all ancestors of parent = new csets
6526 6528 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
6527 6529 bheads))
6528 6530
6529 6531 if new == 0:
6530 6532 # i18n: column positioning for "hg summary"
6531 6533 ui.status(_('update: (current)\n'))
6532 6534 elif pnode not in bheads:
6533 6535 # i18n: column positioning for "hg summary"
6534 6536 ui.write(_('update: %d new changesets (update)\n') % new)
6535 6537 else:
6536 6538 # i18n: column positioning for "hg summary"
6537 6539 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
6538 6540 (new, len(bheads)))
6539 6541
6540 6542 t = []
6541 6543 draft = len(repo.revs('draft()'))
6542 6544 if draft:
6543 6545 t.append(_('%d draft') % draft)
6544 6546 secret = len(repo.revs('secret()'))
6545 6547 if secret:
6546 6548 t.append(_('%d secret') % secret)
6547 6549
6548 6550 if draft or secret:
6549 6551 ui.status(_('phases: %s\n') % ', '.join(t))
6550 6552
6551 6553 if obsolete.isenabled(repo, obsolete.createmarkersopt):
6552 6554 for trouble in ("unstable", "divergent", "bumped"):
6553 6555 numtrouble = len(repo.revs(trouble + "()"))
6554 6556 # We write all the possibilities to ease translation
6555 6557 troublemsg = {
6556 6558 "unstable": _("unstable: %d changesets"),
6557 6559 "divergent": _("divergent: %d changesets"),
6558 6560 "bumped": _("bumped: %d changesets"),
6559 6561 }
6560 6562 if numtrouble > 0:
6561 6563 ui.status(troublemsg[trouble] % numtrouble + "\n")
6562 6564
6563 6565 cmdutil.summaryhooks(ui, repo)
6564 6566
6565 6567 if opts.get('remote'):
6566 6568 needsincoming, needsoutgoing = True, True
6567 6569 else:
6568 6570 needsincoming, needsoutgoing = False, False
6569 6571 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
6570 6572 if i:
6571 6573 needsincoming = True
6572 6574 if o:
6573 6575 needsoutgoing = True
6574 6576 if not needsincoming and not needsoutgoing:
6575 6577 return
6576 6578
6577 6579 def getincoming():
6578 6580 source, branches = hg.parseurl(ui.expandpath('default'))
6579 6581 sbranch = branches[0]
6580 6582 try:
6581 6583 other = hg.peer(repo, {}, source)
6582 6584 except error.RepoError:
6583 6585 if opts.get('remote'):
6584 6586 raise
6585 6587 return source, sbranch, None, None, None
6586 6588 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6587 6589 if revs:
6588 6590 revs = [other.lookup(rev) for rev in revs]
6589 6591 ui.debug('comparing with %s\n' % util.hidepassword(source))
6590 6592 repo.ui.pushbuffer()
6591 6593 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6592 6594 repo.ui.popbuffer()
6593 6595 return source, sbranch, other, commoninc, commoninc[1]
6594 6596
6595 6597 if needsincoming:
6596 6598 source, sbranch, sother, commoninc, incoming = getincoming()
6597 6599 else:
6598 6600 source = sbranch = sother = commoninc = incoming = None
6599 6601
6600 6602 def getoutgoing():
6601 6603 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
6602 6604 dbranch = branches[0]
6603 6605 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
6604 6606 if source != dest:
6605 6607 try:
6606 6608 dother = hg.peer(repo, {}, dest)
6607 6609 except error.RepoError:
6608 6610 if opts.get('remote'):
6609 6611 raise
6610 6612 return dest, dbranch, None, None
6611 6613 ui.debug('comparing with %s\n' % util.hidepassword(dest))
6612 6614 elif sother is None:
6613 6615 # there is no explicit destination peer, but source one is invalid
6614 6616 return dest, dbranch, None, None
6615 6617 else:
6616 6618 dother = sother
6617 6619 if (source != dest or (sbranch is not None and sbranch != dbranch)):
6618 6620 common = None
6619 6621 else:
6620 6622 common = commoninc
6621 6623 if revs:
6622 6624 revs = [repo.lookup(rev) for rev in revs]
6623 6625 repo.ui.pushbuffer()
6624 6626 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
6625 6627 commoninc=common)
6626 6628 repo.ui.popbuffer()
6627 6629 return dest, dbranch, dother, outgoing
6628 6630
6629 6631 if needsoutgoing:
6630 6632 dest, dbranch, dother, outgoing = getoutgoing()
6631 6633 else:
6632 6634 dest = dbranch = dother = outgoing = None
6633 6635
6634 6636 if opts.get('remote'):
6635 6637 t = []
6636 6638 if incoming:
6637 6639 t.append(_('1 or more incoming'))
6638 6640 o = outgoing.missing
6639 6641 if o:
6640 6642 t.append(_('%d outgoing') % len(o))
6641 6643 other = dother or sother
6642 6644 if 'bookmarks' in other.listkeys('namespaces'):
6643 6645 counts = bookmarks.summary(repo, other)
6644 6646 if counts[0] > 0:
6645 6647 t.append(_('%d incoming bookmarks') % counts[0])
6646 6648 if counts[1] > 0:
6647 6649 t.append(_('%d outgoing bookmarks') % counts[1])
6648 6650
6649 6651 if t:
6650 6652 # i18n: column positioning for "hg summary"
6651 6653 ui.write(_('remote: %s\n') % (', '.join(t)))
6652 6654 else:
6653 6655 # i18n: column positioning for "hg summary"
6654 6656 ui.status(_('remote: (synced)\n'))
6655 6657
6656 6658 cmdutil.summaryremotehooks(ui, repo, opts,
6657 6659 ((source, sbranch, sother, commoninc),
6658 6660 (dest, dbranch, dother, outgoing)))
6659 6661
6660 6662 @command('tag',
6661 6663 [('f', 'force', None, _('force tag')),
6662 6664 ('l', 'local', None, _('make the tag local')),
6663 6665 ('r', 'rev', '', _('revision to tag'), _('REV')),
6664 6666 ('', 'remove', None, _('remove a tag')),
6665 6667 # -l/--local is already there, commitopts cannot be used
6666 6668 ('e', 'edit', None, _('invoke editor on commit messages')),
6667 6669 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
6668 6670 ] + commitopts2,
6669 6671 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
6670 6672 def tag(ui, repo, name1, *names, **opts):
6671 6673 """add one or more tags for the current or given revision
6672 6674
6673 6675 Name a particular revision using <name>.
6674 6676
6675 6677 Tags are used to name particular revisions of the repository and are
6676 6678 very useful to compare different revisions, to go back to significant
6677 6679 earlier versions or to mark branch points as releases, etc. Changing
6678 6680 an existing tag is normally disallowed; use -f/--force to override.
6679 6681
6680 6682 If no revision is given, the parent of the working directory is
6681 6683 used.
6682 6684
6683 6685 To facilitate version control, distribution, and merging of tags,
6684 6686 they are stored as a file named ".hgtags" which is managed similarly
6685 6687 to other project files and can be hand-edited if necessary. This
6686 6688 also means that tagging creates a new commit. The file
6687 6689 ".hg/localtags" is used for local tags (not shared among
6688 6690 repositories).
6689 6691
6690 6692 Tag commits are usually made at the head of a branch. If the parent
6691 6693 of the working directory is not a branch head, :hg:`tag` aborts; use
6692 6694 -f/--force to force the tag commit to be based on a non-head
6693 6695 changeset.
6694 6696
6695 6697 See :hg:`help dates` for a list of formats valid for -d/--date.
6696 6698
6697 6699 Since tag names have priority over branch names during revision
6698 6700 lookup, using an existing branch name as a tag name is discouraged.
6699 6701
6700 6702 Returns 0 on success.
6701 6703 """
6702 6704 wlock = lock = None
6703 6705 try:
6704 6706 wlock = repo.wlock()
6705 6707 lock = repo.lock()
6706 6708 rev_ = "."
6707 6709 names = [t.strip() for t in (name1,) + names]
6708 6710 if len(names) != len(set(names)):
6709 6711 raise error.Abort(_('tag names must be unique'))
6710 6712 for n in names:
6711 6713 scmutil.checknewlabel(repo, n, 'tag')
6712 6714 if not n:
6713 6715 raise error.Abort(_('tag names cannot consist entirely of '
6714 6716 'whitespace'))
6715 6717 if opts.get('rev') and opts.get('remove'):
6716 6718 raise error.Abort(_("--rev and --remove are incompatible"))
6717 6719 if opts.get('rev'):
6718 6720 rev_ = opts['rev']
6719 6721 message = opts.get('message')
6720 6722 if opts.get('remove'):
6721 6723 if opts.get('local'):
6722 6724 expectedtype = 'local'
6723 6725 else:
6724 6726 expectedtype = 'global'
6725 6727
6726 6728 for n in names:
6727 6729 if not repo.tagtype(n):
6728 6730 raise error.Abort(_("tag '%s' does not exist") % n)
6729 6731 if repo.tagtype(n) != expectedtype:
6730 6732 if expectedtype == 'global':
6731 6733 raise error.Abort(_("tag '%s' is not a global tag") % n)
6732 6734 else:
6733 6735 raise error.Abort(_("tag '%s' is not a local tag") % n)
6734 6736 rev_ = 'null'
6735 6737 if not message:
6736 6738 # we don't translate commit messages
6737 6739 message = 'Removed tag %s' % ', '.join(names)
6738 6740 elif not opts.get('force'):
6739 6741 for n in names:
6740 6742 if n in repo.tags():
6741 6743 raise error.Abort(_("tag '%s' already exists "
6742 6744 "(use -f to force)") % n)
6743 6745 if not opts.get('local'):
6744 6746 p1, p2 = repo.dirstate.parents()
6745 6747 if p2 != nullid:
6746 6748 raise error.Abort(_('uncommitted merge'))
6747 6749 bheads = repo.branchheads()
6748 6750 if not opts.get('force') and bheads and p1 not in bheads:
6749 6751 raise error.Abort(_('not at a branch head (use -f to force)'))
6750 6752 r = scmutil.revsingle(repo, rev_).node()
6751 6753
6752 6754 if not message:
6753 6755 # we don't translate commit messages
6754 6756 message = ('Added tag %s for changeset %s' %
6755 6757 (', '.join(names), short(r)))
6756 6758
6757 6759 date = opts.get('date')
6758 6760 if date:
6759 6761 date = util.parsedate(date)
6760 6762
6761 6763 if opts.get('remove'):
6762 6764 editform = 'tag.remove'
6763 6765 else:
6764 6766 editform = 'tag.add'
6765 6767 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6766 6768
6767 6769 # don't allow tagging the null rev
6768 6770 if (not opts.get('remove') and
6769 6771 scmutil.revsingle(repo, rev_).rev() == nullrev):
6770 6772 raise error.Abort(_("cannot tag null revision"))
6771 6773
6772 6774 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6773 6775 editor=editor)
6774 6776 finally:
6775 6777 release(lock, wlock)
6776 6778
6777 6779 @command('tags', formatteropts, '')
6778 6780 def tags(ui, repo, **opts):
6779 6781 """list repository tags
6780 6782
6781 6783 This lists both regular and local tags. When the -v/--verbose
6782 6784 switch is used, a third column "local" is printed for local tags.
6783 6785 When the -q/--quiet switch is used, only the tag name is printed.
6784 6786
6785 6787 Returns 0 on success.
6786 6788 """
6787 6789
6788 6790 fm = ui.formatter('tags', opts)
6789 6791 hexfunc = fm.hexfunc
6790 6792 tagtype = ""
6791 6793
6792 6794 for t, n in reversed(repo.tagslist()):
6793 6795 hn = hexfunc(n)
6794 6796 label = 'tags.normal'
6795 6797 tagtype = ''
6796 6798 if repo.tagtype(t) == 'local':
6797 6799 label = 'tags.local'
6798 6800 tagtype = 'local'
6799 6801
6800 6802 fm.startitem()
6801 6803 fm.write('tag', '%s', t, label=label)
6802 6804 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6803 6805 fm.condwrite(not ui.quiet, 'rev node', fmt,
6804 6806 repo.changelog.rev(n), hn, label=label)
6805 6807 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6806 6808 tagtype, label=label)
6807 6809 fm.plain('\n')
6808 6810 fm.end()
6809 6811
6810 6812 @command('tip',
6811 6813 [('p', 'patch', None, _('show patch')),
6812 6814 ('g', 'git', None, _('use git extended diff format')),
6813 6815 ] + templateopts,
6814 6816 _('[-p] [-g]'))
6815 6817 def tip(ui, repo, **opts):
6816 6818 """show the tip revision (DEPRECATED)
6817 6819
6818 6820 The tip revision (usually just called the tip) is the changeset
6819 6821 most recently added to the repository (and therefore the most
6820 6822 recently changed head).
6821 6823
6822 6824 If you have just made a commit, that commit will be the tip. If
6823 6825 you have just pulled changes from another repository, the tip of
6824 6826 that repository becomes the current tip. The "tip" tag is special
6825 6827 and cannot be renamed or assigned to a different changeset.
6826 6828
6827 6829 This command is deprecated, please use :hg:`heads` instead.
6828 6830
6829 6831 Returns 0 on success.
6830 6832 """
6831 6833 displayer = cmdutil.show_changeset(ui, repo, opts)
6832 6834 displayer.show(repo['tip'])
6833 6835 displayer.close()
6834 6836
6835 6837 @command('unbundle',
6836 6838 [('u', 'update', None,
6837 6839 _('update to new branch head if changesets were unbundled'))],
6838 6840 _('[-u] FILE...'))
6839 6841 def unbundle(ui, repo, fname1, *fnames, **opts):
6840 6842 """apply one or more changegroup files
6841 6843
6842 6844 Apply one or more compressed changegroup files generated by the
6843 6845 bundle command.
6844 6846
6845 6847 Returns 0 on success, 1 if an update has unresolved files.
6846 6848 """
6847 6849 fnames = (fname1,) + fnames
6848 6850
6849 6851 with repo.lock():
6850 6852 for fname in fnames:
6851 6853 f = hg.openpath(ui, fname)
6852 6854 gen = exchange.readbundle(ui, f, fname)
6853 6855 if isinstance(gen, bundle2.unbundle20):
6854 6856 tr = repo.transaction('unbundle')
6855 6857 try:
6856 6858 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
6857 6859 url='bundle:' + fname)
6858 6860 tr.close()
6859 6861 except error.BundleUnknownFeatureError as exc:
6860 6862 raise error.Abort(_('%s: unknown bundle feature, %s')
6861 6863 % (fname, exc),
6862 6864 hint=_("see https://mercurial-scm.org/"
6863 6865 "wiki/BundleFeature for more "
6864 6866 "information"))
6865 6867 finally:
6866 6868 if tr:
6867 6869 tr.release()
6868 6870 changes = [r.get('return', 0)
6869 6871 for r in op.records['changegroup']]
6870 6872 modheads = changegroup.combineresults(changes)
6871 6873 elif isinstance(gen, streamclone.streamcloneapplier):
6872 6874 raise error.Abort(
6873 6875 _('packed bundles cannot be applied with '
6874 6876 '"hg unbundle"'),
6875 6877 hint=_('use "hg debugapplystreamclonebundle"'))
6876 6878 else:
6877 6879 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
6878 6880
6879 6881 return postincoming(ui, repo, modheads, opts.get('update'), None)
6880 6882
6881 6883 @command('^update|up|checkout|co',
6882 6884 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6883 6885 ('c', 'check', None,
6884 6886 _('update across branches if no uncommitted changes')),
6885 6887 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6886 6888 ('r', 'rev', '', _('revision'), _('REV'))
6887 6889 ] + mergetoolopts,
6888 6890 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6889 6891 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6890 6892 tool=None):
6891 6893 """update working directory (or switch revisions)
6892 6894
6893 6895 Update the repository's working directory to the specified
6894 6896 changeset. If no changeset is specified, update to the tip of the
6895 6897 current named branch and move the active bookmark (see :hg:`help
6896 6898 bookmarks`).
6897 6899
6898 6900 Update sets the working directory's parent revision to the specified
6899 6901 changeset (see :hg:`help parents`).
6900 6902
6901 6903 If the changeset is not a descendant or ancestor of the working
6902 6904 directory's parent, the update is aborted. With the -c/--check
6903 6905 option, the working directory is checked for uncommitted changes; if
6904 6906 none are found, the working directory is updated to the specified
6905 6907 changeset.
6906 6908
6907 6909 .. container:: verbose
6908 6910
6909 6911 The following rules apply when the working directory contains
6910 6912 uncommitted changes:
6911 6913
6912 6914 1. If neither -c/--check nor -C/--clean is specified, and if
6913 6915 the requested changeset is an ancestor or descendant of
6914 6916 the working directory's parent, the uncommitted changes
6915 6917 are merged into the requested changeset and the merged
6916 6918 result is left uncommitted. If the requested changeset is
6917 6919 not an ancestor or descendant (that is, it is on another
6918 6920 branch), the update is aborted and the uncommitted changes
6919 6921 are preserved.
6920 6922
6921 6923 2. With the -c/--check option, the update is aborted and the
6922 6924 uncommitted changes are preserved.
6923 6925
6924 6926 3. With the -C/--clean option, uncommitted changes are discarded and
6925 6927 the working directory is updated to the requested changeset.
6926 6928
6927 6929 To cancel an uncommitted merge (and lose your changes), use
6928 6930 :hg:`update --clean .`.
6929 6931
6930 6932 Use null as the changeset to remove the working directory (like
6931 6933 :hg:`clone -U`).
6932 6934
6933 6935 If you want to revert just one file to an older revision, use
6934 6936 :hg:`revert [-r REV] NAME`.
6935 6937
6936 6938 See :hg:`help dates` for a list of formats valid for -d/--date.
6937 6939
6938 6940 Returns 0 on success, 1 if there are unresolved files.
6939 6941 """
6940 6942 movemarkfrom = None
6941 6943 if rev and node:
6942 6944 raise error.Abort(_("please specify just one revision"))
6943 6945
6944 6946 if rev is None or rev == '':
6945 6947 rev = node
6946 6948
6947 6949 with repo.wlock():
6948 6950 cmdutil.clearunfinished(repo)
6949 6951
6950 6952 if date:
6951 6953 if rev is not None:
6952 6954 raise error.Abort(_("you can't specify a revision and a date"))
6953 6955 rev = cmdutil.finddate(ui, repo, date)
6954 6956
6955 6957 # if we defined a bookmark, we have to remember the original name
6956 6958 brev = rev
6957 6959 rev = scmutil.revsingle(repo, rev, rev).rev()
6958 6960
6959 6961 if check and clean:
6960 6962 raise error.Abort(_("cannot specify both -c/--check and -C/--clean")
6961 6963 )
6962 6964
6963 6965 if check:
6964 6966 cmdutil.bailifchanged(repo, merge=False)
6965 6967 if rev is None:
6966 6968 updata = destutil.destupdate(repo, clean=clean, check=check)
6967 6969 rev, movemarkfrom, brev = updata
6968 6970
6969 6971 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6970 6972
6971 6973 if clean:
6972 6974 ret = hg.clean(repo, rev)
6973 6975 else:
6974 6976 ret = hg.update(repo, rev)
6975 6977
6976 6978 if not ret and movemarkfrom:
6977 6979 if movemarkfrom == repo['.'].node():
6978 6980 pass # no-op update
6979 6981 elif bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
6980 6982 ui.status(_("updating bookmark %s\n") % repo._activebookmark)
6981 6983 else:
6982 6984 # this can happen with a non-linear update
6983 6985 ui.status(_("(leaving bookmark %s)\n") %
6984 6986 repo._activebookmark)
6985 6987 bookmarks.deactivate(repo)
6986 6988 elif brev in repo._bookmarks:
6987 6989 bookmarks.activate(repo, brev)
6988 6990 ui.status(_("(activating bookmark %s)\n") % brev)
6989 6991 elif brev:
6990 6992 if repo._activebookmark:
6991 6993 ui.status(_("(leaving bookmark %s)\n") %
6992 6994 repo._activebookmark)
6993 6995 bookmarks.deactivate(repo)
6994 6996
6995 6997 return ret
6996 6998
6997 6999 @command('verify', [])
6998 7000 def verify(ui, repo):
6999 7001 """verify the integrity of the repository
7000 7002
7001 7003 Verify the integrity of the current repository.
7002 7004
7003 7005 This will perform an extensive check of the repository's
7004 7006 integrity, validating the hashes and checksums of each entry in
7005 7007 the changelog, manifest, and tracked files, as well as the
7006 7008 integrity of their crosslinks and indices.
7007 7009
7008 7010 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7009 7011 for more information about recovery from corruption of the
7010 7012 repository.
7011 7013
7012 7014 Returns 0 on success, 1 if errors are encountered.
7013 7015 """
7014 7016 return hg.verify(repo)
7015 7017
7016 7018 @command('version', [], norepo=True)
7017 7019 def version_(ui):
7018 7020 """output version and copyright information"""
7019 7021 ui.write(_("Mercurial Distributed SCM (version %s)\n")
7020 7022 % util.version())
7021 7023 ui.status(_(
7022 7024 "(see https://mercurial-scm.org for more information)\n"
7023 7025 "\nCopyright (C) 2005-2016 Matt Mackall and others\n"
7024 7026 "This is free software; see the source for copying conditions. "
7025 7027 "There is NO\nwarranty; "
7026 7028 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7027 7029 ))
7028 7030
7029 7031 ui.note(_("\nEnabled extensions:\n\n"))
7030 7032 if ui.verbose:
7031 7033 # format names and versions into columns
7032 7034 names = []
7033 7035 vers = []
7034 7036 for name, module in extensions.extensions():
7035 7037 names.append(name)
7036 7038 vers.append(extensions.moduleversion(module))
7037 7039 if names:
7038 7040 maxnamelen = max(len(n) for n in names)
7039 7041 for i, name in enumerate(names):
7040 7042 ui.write(" %-*s %s\n" % (maxnamelen, name, vers[i]))
@@ -1,755 +1,762 b''
1 1 $ hg init basic
2 2 $ cd basic
3 3
4 4 should complain
5 5
6 6 $ hg backout
7 7 abort: please specify a revision to backout
8 8 [255]
9 9 $ hg backout -r 0 0
10 10 abort: please specify just one revision
11 11 [255]
12 12
13 13 basic operation
14 14 (this also tests that editor is invoked if the commit message is not
15 15 specified explicitly)
16 16
17 17 $ echo a > a
18 18 $ hg commit -d '0 0' -A -m a
19 19 adding a
20 20 $ echo b >> a
21 21 $ hg commit -d '1 0' -m b
22 22
23 23 $ hg status --rev tip --rev "tip^1"
24 24 M a
25 25 $ HGEDITOR=cat hg backout -d '2 0' tip --tool=true
26 26 reverting a
27 27 Backed out changeset a820f4f40a57
28 28
29 29
30 30 HG: Enter commit message. Lines beginning with 'HG:' are removed.
31 31 HG: Leave message empty to abort commit.
32 32 HG: --
33 33 HG: user: test
34 34 HG: branch 'default'
35 35 HG: changed a
36 36 changeset 2:2929462c3dff backs out changeset 1:a820f4f40a57
37 37 $ cat a
38 38 a
39 39 $ hg summary
40 40 parent: 2:2929462c3dff tip
41 41 Backed out changeset a820f4f40a57
42 42 branch: default
43 43 commit: (clean)
44 44 update: (current)
45 45 phases: 3 draft
46 46
47 47 commit option
48 48
49 49 $ cd ..
50 50 $ hg init commit
51 51 $ cd commit
52 52
53 53 $ echo tomatoes > a
54 54 $ hg add a
55 55 $ hg commit -d '0 0' -m tomatoes
56 56
57 57 $ echo chair > b
58 58 $ hg add b
59 59 $ hg commit -d '1 0' -m chair
60 60
61 61 $ echo grapes >> a
62 62 $ hg commit -d '2 0' -m grapes
63 63
64 64 $ hg backout -d '4 0' 1 --tool=:fail
65 65 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
66 66 changeset 3:1c2161e97c0a backs out changeset 1:22cb4f70d813
67 67 $ hg summary
68 68 parent: 3:1c2161e97c0a tip
69 69 Backed out changeset 22cb4f70d813
70 70 branch: default
71 71 commit: (clean)
72 72 update: (current)
73 73 phases: 4 draft
74 74
75 75 $ echo ypples > a
76 76 $ hg commit -d '5 0' -m ypples
77 77
78 78 $ hg backout -d '6 0' 2 --tool=:fail
79 79 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
80 80 use 'hg resolve' to retry unresolved file merges
81 81 [1]
82 82 $ hg summary
83 83 parent: 4:ed99997b793d tip
84 84 ypples
85 85 branch: default
86 86 commit: 1 unresolved (clean)
87 87 update: (current)
88 88 phases: 5 draft
89 89
90 90 file that was removed is recreated
91 91 (this also tests that editor is not invoked if the commit message is
92 92 specified explicitly)
93 93
94 94 $ cd ..
95 95 $ hg init remove
96 96 $ cd remove
97 97
98 98 $ echo content > a
99 99 $ hg commit -d '0 0' -A -m a
100 100 adding a
101 101
102 102 $ hg rm a
103 103 $ hg commit -d '1 0' -m b
104 104
105 105 $ HGEDITOR=cat hg backout -d '2 0' tip --tool=true -m "Backed out changeset 76862dcce372"
106 106 adding a
107 107 changeset 2:de31bdc76c0d backs out changeset 1:76862dcce372
108 108 $ cat a
109 109 content
110 110 $ hg summary
111 111 parent: 2:de31bdc76c0d tip
112 112 Backed out changeset 76862dcce372
113 113 branch: default
114 114 commit: (clean)
115 115 update: (current)
116 116 phases: 3 draft
117 117
118 118 backout of backout is as if nothing happened
119 119
120 120 $ hg backout -d '3 0' --merge tip --tool=true
121 121 removing a
122 122 changeset 3:7f6d0f120113 backs out changeset 2:de31bdc76c0d
123 123 $ test -f a
124 124 [1]
125 125 $ hg summary
126 126 parent: 3:7f6d0f120113 tip
127 127 Backed out changeset de31bdc76c0d
128 128 branch: default
129 129 commit: (clean)
130 130 update: (current)
131 131 phases: 4 draft
132 132
133 133 Test that 'hg rollback' restores dirstate just before opening
134 134 transaction: in-memory dirstate changes should be written into
135 135 '.hg/journal.dirstate' as expected.
136 136
137 137 $ echo 'removed soon' > b
138 138 $ hg commit -A -d '4 0' -m 'prepare for subsequent removing'
139 139 adding b
140 140 $ echo 'newly added' > c
141 141 $ hg add c
142 142 $ hg remove b
143 143 $ hg commit -d '5 0' -m 'prepare for subsequent backout'
144 144 $ touch -t 200001010000 c
145 145 $ hg status -A
146 146 C c
147 147 $ hg debugstate --nodates
148 148 n 644 12 set c
149 149 $ hg backout -d '6 0' -m 'to be rollback-ed soon' -r .
150 150 adding b
151 151 removing c
152 152 changeset 6:4bfec048029d backs out changeset 5:fac0b729a654
153 153 $ hg rollback -q
154 154 $ hg status -A
155 155 A b
156 156 R c
157 157 $ hg debugstate --nodates
158 158 a 0 -1 unset b
159 159 r 0 0 set c
160 160
161 161 across branch
162 162
163 163 $ cd ..
164 164 $ hg init branch
165 165 $ cd branch
166 166 $ echo a > a
167 167 $ hg ci -Am0
168 168 adding a
169 169 $ echo b > b
170 170 $ hg ci -Am1
171 171 adding b
172 172 $ hg co -C 0
173 173 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
174 174 $ hg summary
175 175 parent: 0:f7b1eb17ad24
176 176 0
177 177 branch: default
178 178 commit: (clean)
179 179 update: 1 new changesets (update)
180 180 phases: 2 draft
181 181
182 182 should fail
183 183
184 184 $ hg backout 1
185 185 abort: cannot backout change that is not an ancestor
186 186 [255]
187 187 $ echo c > c
188 188 $ hg ci -Am2
189 189 adding c
190 190 created new head
191 191 $ hg summary
192 192 parent: 2:db815d6d32e6 tip
193 193 2
194 194 branch: default
195 195 commit: (clean)
196 196 update: 1 new changesets, 2 branch heads (merge)
197 197 phases: 3 draft
198 198
199 199 should fail
200 200
201 201 $ hg backout 1
202 202 abort: cannot backout change that is not an ancestor
203 203 [255]
204 204 $ hg summary
205 205 parent: 2:db815d6d32e6 tip
206 206 2
207 207 branch: default
208 208 commit: (clean)
209 209 update: 1 new changesets, 2 branch heads (merge)
210 210 phases: 3 draft
211 211
212 212 backout with merge
213 213
214 214 $ cd ..
215 215 $ hg init merge
216 216 $ cd merge
217 217
218 218 $ echo line 1 > a
219 219 $ echo line 2 >> a
220 220 $ hg commit -d '0 0' -A -m a
221 221 adding a
222 222 $ hg summary
223 223 parent: 0:59395513a13a tip
224 224 a
225 225 branch: default
226 226 commit: (clean)
227 227 update: (current)
228 228 phases: 1 draft
229 229
230 230 remove line 1
231 231
232 232 $ echo line 2 > a
233 233 $ hg commit -d '1 0' -m b
234 234
235 235 $ echo line 3 >> a
236 236 $ hg commit -d '2 0' -m c
237 237
238 238 $ hg backout --merge -d '3 0' 1 --tool=true
239 239 reverting a
240 240 created new head
241 241 changeset 3:26b8ccb9ad91 backs out changeset 1:5a50a024c182
242 242 merging with changeset 3:26b8ccb9ad91
243 243 merging a
244 244 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
245 245 (branch merge, don't forget to commit)
246 246 $ hg commit -d '4 0' -m d
247 247 $ hg summary
248 248 parent: 4:c7df5e0b9c09 tip
249 249 d
250 250 branch: default
251 251 commit: (clean)
252 252 update: (current)
253 253 phases: 5 draft
254 254
255 255 check line 1 is back
256 256
257 257 $ cat a
258 258 line 1
259 259 line 2
260 260 line 3
261 261
262 262 Test visibility of in-memory dirstate changes outside transaction to
263 263 external hook process
264 264
265 265 $ cat > $TESTTMP/checkvisibility.sh <<EOF
266 266 > echo "==== \$1:"
267 267 > hg parents --template "{rev}:{node|short}\n"
268 268 > echo "===="
269 269 > EOF
270 270
271 271 "hg backout --merge REV1" at REV2 below implies steps below:
272 272
273 273 (1) update to REV1 (REV2 => REV1)
274 274 (2) revert by REV1^1
275 275 (3) commit backnig out revision (REV3)
276 276 (4) update to REV2 (REV3 => REV2)
277 277 (5) merge with REV3 (REV2 => REV2, REV3)
278 278
279 279 == test visibility to external preupdate hook
280 280
281 281 $ hg update -q -C 2
282 282 $ hg --config extensions.strip= strip 3
283 283 saved backup bundle to * (glob)
284 284
285 285 $ cat >> .hg/hgrc <<EOF
286 286 > [hooks]
287 287 > preupdate.visibility = sh $TESTTMP/checkvisibility.sh preupdate
288 288 > EOF
289 289
290 290 ("-m" is needed to avoid writing dirstte changes out at other than
291 291 invocation of the hook to be examined)
292 292
293 293 $ hg backout --merge -d '3 0' 1 --tool=true -m 'fixed comment'
294 294 ==== preupdate:
295 295 2:6ea3f2a197a2
296 296 ====
297 297 reverting a
298 298 created new head
299 299 changeset 3:d92a3f57f067 backs out changeset 1:5a50a024c182
300 300 ==== preupdate:
301 301 3:d92a3f57f067
302 302 ====
303 303 merging with changeset 3:d92a3f57f067
304 304 ==== preupdate:
305 305 2:6ea3f2a197a2
306 306 ====
307 307 merging a
308 308 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
309 309 (branch merge, don't forget to commit)
310 310
311 311 $ cat >> .hg/hgrc <<EOF
312 312 > [hooks]
313 313 > preupdate.visibility =
314 314 > EOF
315 315
316 316 == test visibility to external update hook
317 317
318 318 $ hg update -q -C 2
319 319 $ hg --config extensions.strip= strip 3
320 320 saved backup bundle to * (glob)
321 321
322 322 $ cat >> .hg/hgrc <<EOF
323 323 > [hooks]
324 324 > update.visibility = sh $TESTTMP/checkvisibility.sh update
325 325 > EOF
326 326
327 327 $ hg backout --merge -d '3 0' 1 --tool=true -m 'fixed comment'
328 328 ==== update:
329 329 1:5a50a024c182
330 330 ====
331 331 reverting a
332 332 created new head
333 333 changeset 3:d92a3f57f067 backs out changeset 1:5a50a024c182
334 334 ==== update:
335 335 2:6ea3f2a197a2
336 336 ====
337 337 merging with changeset 3:d92a3f57f067
338 338 merging a
339 339 ==== update:
340 340 2:6ea3f2a197a2
341 341 3:d92a3f57f067
342 342 ====
343 343 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
344 344 (branch merge, don't forget to commit)
345 345
346 346 $ cat >> .hg/hgrc <<EOF
347 347 > [hooks]
348 348 > update.visibility =
349 349 > EOF
350 350
351 351 $ cd ..
352 352
353 353 backout should not back out subsequent changesets
354 354
355 355 $ hg init onecs
356 356 $ cd onecs
357 357 $ echo 1 > a
358 358 $ hg commit -d '0 0' -A -m a
359 359 adding a
360 360 $ echo 2 >> a
361 361 $ hg commit -d '1 0' -m b
362 362 $ echo 1 > b
363 363 $ hg commit -d '2 0' -A -m c
364 364 adding b
365 365 $ hg summary
366 366 parent: 2:882396649954 tip
367 367 c
368 368 branch: default
369 369 commit: (clean)
370 370 update: (current)
371 371 phases: 3 draft
372 372
373 373 without --merge
374 374 $ hg backout --no-commit -d '3 0' 1 --tool=true
375 375 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
376 376 changeset 22bca4c721e5 backed out, don't forget to commit.
377 377 $ hg locate b
378 378 b
379 379 $ hg update -C tip
380 380 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
381 381 $ hg locate b
382 382 b
383 383 $ hg summary
384 384 parent: 2:882396649954 tip
385 385 c
386 386 branch: default
387 387 commit: (clean)
388 388 update: (current)
389 389 phases: 3 draft
390 390
391 391 with --merge
392 392 $ hg backout --merge -d '3 0' 1 --tool=true
393 393 reverting a
394 394 created new head
395 395 changeset 3:3202beb76721 backs out changeset 1:22bca4c721e5
396 396 merging with changeset 3:3202beb76721
397 397 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
398 398 (branch merge, don't forget to commit)
399 399 $ hg locate b
400 400 b
401 401 $ hg update -C tip
402 402 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
403 403 $ hg locate b
404 404 [1]
405 405
406 406 $ cd ..
407 407 $ hg init m
408 408 $ cd m
409 409 $ echo a > a
410 410 $ hg commit -d '0 0' -A -m a
411 411 adding a
412 412 $ echo b > b
413 413 $ hg commit -d '1 0' -A -m b
414 414 adding b
415 415 $ echo c > c
416 416 $ hg commit -d '2 0' -A -m b
417 417 adding c
418 418 $ hg update 1
419 419 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
420 420 $ echo d > d
421 421 $ hg commit -d '3 0' -A -m c
422 422 adding d
423 423 created new head
424 424 $ hg merge 2
425 425 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
426 426 (branch merge, don't forget to commit)
427 427 $ hg commit -d '4 0' -A -m d
428 428 $ hg summary
429 429 parent: 4:b2f3bb92043e tip
430 430 d
431 431 branch: default
432 432 commit: (clean)
433 433 update: (current)
434 434 phases: 5 draft
435 435
436 436 backout of merge should fail
437 437
438 438 $ hg backout 4
439 439 abort: cannot backout a merge changeset
440 440 [255]
441 441
442 442 backout of merge with bad parent should fail
443 443
444 444 $ hg backout --parent 0 4
445 445 abort: cb9a9f314b8b is not a parent of b2f3bb92043e
446 446 [255]
447 447
448 448 backout of non-merge with parent should fail
449 449
450 450 $ hg backout --parent 0 3
451 451 abort: cannot use --parent on non-merge changeset
452 452 [255]
453 453
454 454 backout with valid parent should be ok
455 455
456 456 $ hg backout -d '5 0' --parent 2 4 --tool=true
457 457 removing d
458 458 changeset 5:10e5328c8435 backs out changeset 4:b2f3bb92043e
459 459 $ hg summary
460 460 parent: 5:10e5328c8435 tip
461 461 Backed out changeset b2f3bb92043e
462 462 branch: default
463 463 commit: (clean)
464 464 update: (current)
465 465 phases: 6 draft
466 466
467 467 $ hg rollback
468 468 repository tip rolled back to revision 4 (undo commit)
469 469 working directory now based on revision 4
470 470 $ hg update -C
471 471 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
472 472 $ hg summary
473 473 parent: 4:b2f3bb92043e tip
474 474 d
475 475 branch: default
476 476 commit: (clean)
477 477 update: (current)
478 478 phases: 5 draft
479 479
480 480 $ hg backout -d '6 0' --parent 3 4 --tool=true
481 481 removing c
482 482 changeset 5:033590168430 backs out changeset 4:b2f3bb92043e
483 483 $ hg summary
484 484 parent: 5:033590168430 tip
485 485 Backed out changeset b2f3bb92043e
486 486 branch: default
487 487 commit: (clean)
488 488 update: (current)
489 489 phases: 6 draft
490 490
491 491 $ cd ..
492 492
493 493 named branches
494 494
495 495 $ hg init named_branches
496 496 $ cd named_branches
497 497
498 498 $ echo default > default
499 499 $ hg ci -d '0 0' -Am default
500 500 adding default
501 501 $ hg branch branch1
502 502 marked working directory as branch branch1
503 503 (branches are permanent and global, did you want a bookmark?)
504 504 $ echo branch1 > file1
505 505 $ hg ci -d '1 0' -Am file1
506 506 adding file1
507 507 $ hg branch branch2
508 508 marked working directory as branch branch2
509 509 $ echo branch2 > file2
510 510 $ hg ci -d '2 0' -Am file2
511 511 adding file2
512 512
513 513 without --merge
514 514 $ hg backout --no-commit -r 1 --tool=true
515 515 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
516 516 changeset bf1602f437f3 backed out, don't forget to commit.
517 517 $ hg branch
518 518 branch2
519 519 $ hg status -A
520 520 R file1
521 521 C default
522 522 C file2
523 523 $ hg summary
524 524 parent: 2:45bbcd363bf0 tip
525 525 file2
526 526 branch: branch2
527 527 commit: 1 removed
528 528 update: (current)
529 529 phases: 3 draft
530 530
531 531 with --merge
532 532 (this also tests that editor is invoked if '--edit' is specified
533 533 explicitly regardless of '--message')
534 534
535 535 $ hg update -qC
536 536 $ HGEDITOR=cat hg backout --merge -d '3 0' -r 1 -m 'backout on branch1' --tool=true --edit
537 537 removing file1
538 538 backout on branch1
539 539
540 540
541 541 HG: Enter commit message. Lines beginning with 'HG:' are removed.
542 542 HG: Leave message empty to abort commit.
543 543 HG: --
544 544 HG: user: test
545 545 HG: branch 'branch2'
546 546 HG: removed file1
547 547 created new head
548 548 changeset 3:d4e8f6db59fb backs out changeset 1:bf1602f437f3
549 549 merging with changeset 3:d4e8f6db59fb
550 550 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
551 551 (branch merge, don't forget to commit)
552 552 $ hg summary
553 553 parent: 2:45bbcd363bf0
554 554 file2
555 555 parent: 3:d4e8f6db59fb tip
556 556 backout on branch1
557 557 branch: branch2
558 558 commit: 1 removed (merge)
559 559 update: (current)
560 560 phases: 4 draft
561 561 $ hg update -q -C 2
562 562
563 563 on branch2 with branch1 not merged, so file1 should still exist:
564 564
565 565 $ hg id
566 566 45bbcd363bf0 (branch2)
567 567 $ hg st -A
568 568 C default
569 569 C file1
570 570 C file2
571 571 $ hg summary
572 572 parent: 2:45bbcd363bf0
573 573 file2
574 574 branch: branch2
575 575 commit: (clean)
576 576 update: 1 new changesets, 2 branch heads (merge)
577 577 phases: 4 draft
578 578
579 579 on branch2 with branch1 merged, so file1 should be gone:
580 580
581 581 $ hg merge
582 582 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
583 583 (branch merge, don't forget to commit)
584 584 $ hg ci -d '4 0' -m 'merge backout of branch1'
585 585 $ hg id
586 586 22149cdde76d (branch2) tip
587 587 $ hg st -A
588 588 C default
589 589 C file2
590 590 $ hg summary
591 591 parent: 4:22149cdde76d tip
592 592 merge backout of branch1
593 593 branch: branch2
594 594 commit: (clean)
595 595 update: (current)
596 596 phases: 5 draft
597 597
598 598 on branch1, so no file1 and file2:
599 599
600 600 $ hg co -C branch1
601 601 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
602 602 $ hg id
603 603 bf1602f437f3 (branch1)
604 604 $ hg st -A
605 605 C default
606 606 C file1
607 607 $ hg summary
608 608 parent: 1:bf1602f437f3
609 609 file1
610 610 branch: branch1
611 611 commit: (clean)
612 612 update: (current)
613 613 phases: 5 draft
614 614
615 615 $ cd ..
616 616
617 617 backout of empty changeset (issue4190)
618 618
619 619 $ hg init emptycommit
620 620 $ cd emptycommit
621 621
622 622 $ touch file1
623 623 $ hg ci -Aqm file1
624 624 $ hg branch -q branch1
625 625 $ hg ci -qm branch1
626 626 $ hg backout -v 1
627 627 resolving manifests
628 628 nothing changed
629 629 [1]
630 630
631 631 $ cd ..
632 632
633 633
634 634 Test usage of `hg resolve` in case of conflict
635 635 (issue4163)
636 636
637 637 $ hg init issue4163
638 638 $ cd issue4163
639 639 $ touch foo
640 640 $ hg add foo
641 641 $ cat > foo << EOF
642 642 > one
643 643 > two
644 644 > three
645 645 > four
646 646 > five
647 647 > six
648 648 > seven
649 649 > height
650 650 > nine
651 651 > ten
652 652 > EOF
653 653 $ hg ci -m 'initial'
654 654 $ cat > foo << EOF
655 655 > one
656 656 > two
657 657 > THREE
658 658 > four
659 659 > five
660 660 > six
661 661 > seven
662 662 > height
663 663 > nine
664 664 > ten
665 665 > EOF
666 666 $ hg ci -m 'capital three'
667 667 $ cat > foo << EOF
668 668 > one
669 669 > two
670 670 > THREE
671 671 > four
672 672 > five
673 673 > six
674 674 > seven
675 675 > height
676 676 > nine
677 677 > TEN
678 678 > EOF
679 679 $ hg ci -m 'capital ten'
680 680 $ hg backout -r 'desc("capital three")' --tool internal:fail
681 681 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
682 682 use 'hg resolve' to retry unresolved file merges
683 683 [1]
684 684 $ hg status
685 685 $ hg debugmergestate
686 686 * version 2 records
687 687 local: b71750c4b0fdf719734971e3ef90dbeab5919a2d
688 688 other: a30dd8addae3ce71b8667868478542bc417439e6
689 689 file: foo (record type "F", state "u", hash 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33)
690 690 local path: foo (flags "")
691 691 ancestor path: foo (node f89532f44c247a0e993d63e3a734dd781ab04708)
692 692 other path: foo (node f50039b486d6fa1a90ae51778388cad161f425ee)
693 693 $ mv .hg/merge/state2 .hg/merge/state2-moved
694 694 $ hg debugmergestate
695 695 * version 1 records
696 696 local: b71750c4b0fdf719734971e3ef90dbeab5919a2d
697 697 file: foo (record type "F", state "u", hash 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33)
698 698 local path: foo (flags "")
699 699 ancestor path: foo (node f89532f44c247a0e993d63e3a734dd781ab04708)
700 700 other path: foo (node not stored in v1 format)
701 701 $ mv .hg/merge/state2-moved .hg/merge/state2
702 702 $ hg resolve -l # still unresolved
703 703 U foo
704 704 $ hg summary
705 705 parent: 2:b71750c4b0fd tip
706 706 capital ten
707 707 branch: default
708 708 commit: 1 unresolved (clean)
709 709 update: (current)
710 710 phases: 3 draft
711 711 $ hg resolve --all --debug
712 712 picked tool ':merge' for foo (binary False symlink False changedelete False)
713 713 merging foo
714 714 my foo@b71750c4b0fd+ other foo@a30dd8addae3 ancestor foo@913609522437
715 715 premerge successful
716 716 (no more unresolved files)
717 717 continue: hg commit
718 718 $ hg status
719 719 M foo
720 720 ? foo.orig
721 721 $ hg resolve -l
722 722 R foo
723 723 $ hg summary
724 724 parent: 2:b71750c4b0fd tip
725 725 capital ten
726 726 branch: default
727 727 commit: 1 modified, 1 unknown
728 728 update: (current)
729 729 phases: 3 draft
730 730 $ cat foo
731 731 one
732 732 two
733 733 three
734 734 four
735 735 five
736 736 six
737 737 seven
738 738 height
739 739 nine
740 740 TEN
741 741
742 742 --no-commit shouldn't commit
743 743
744 744 $ hg init a
745 745 $ cd a
746 746 $ for i in 1 2 3; do
747 747 > touch $i
748 748 > hg ci -Am $i
749 749 > done
750 750 adding 1
751 751 adding 2
752 752 adding 3
753 753 $ hg backout --no-commit .
754 754 removing 3
755 755 changeset cccc23d9d68f backed out, don't forget to commit.
756 $ hg revert -aq
757
758 --no-commit can't be used with --merge
759
760 $ hg backout --merge --no-commit 2
761 abort: cannot use --merge with --no-commit
762 [255]
General Comments 0
You need to be logged in to leave comments. Login now