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