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