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