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