##// END OF EJS Templates
move walkchangerevs to cmdutils
Matt Mackall -
r3650:731e739b default
parent child Browse files
Show More
@@ -1,594 +1,787
1 1 # cmdutil.py - help for command processing in mercurial
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from demandload import demandload
9 9 from node import *
10 10 from i18n import gettext as _
11 11 demandload(globals(), 'os sys')
12 12 demandload(globals(), 'mdiff util templater cStringIO patch')
13 13
14 14 revrangesep = ':'
15 15
16 16 def revpair(ui, repo, revs):
17 17 '''return pair of nodes, given list of revisions. second item can
18 18 be None, meaning use working dir.'''
19 19
20 20 def revfix(repo, val, defval):
21 21 if not val and val != 0:
22 22 val = defval
23 23 return repo.lookup(val)
24 24
25 25 if not revs:
26 26 return repo.dirstate.parents()[0], None
27 27 end = None
28 28 if len(revs) == 1:
29 29 if revrangesep in revs[0]:
30 30 start, end = revs[0].split(revrangesep, 1)
31 31 start = revfix(repo, start, 0)
32 32 end = revfix(repo, end, repo.changelog.count() - 1)
33 33 else:
34 34 start = revfix(repo, revs[0], None)
35 35 elif len(revs) == 2:
36 36 if revrangesep in revs[0] or revrangesep in revs[1]:
37 37 raise util.Abort(_('too many revisions specified'))
38 38 start = revfix(repo, revs[0], None)
39 39 end = revfix(repo, revs[1], None)
40 40 else:
41 41 raise util.Abort(_('too many revisions specified'))
42 42 return start, end
43 43
44 44 def revrange(ui, repo, revs):
45 45 """Yield revision as strings from a list of revision specifications."""
46 46
47 47 def revfix(repo, val, defval):
48 48 if not val and val != 0:
49 49 return defval
50 50 return repo.changelog.rev(repo.lookup(val))
51 51
52 52 seen, l = {}, []
53 53 for spec in revs:
54 54 if revrangesep in spec:
55 55 start, end = spec.split(revrangesep, 1)
56 56 start = revfix(repo, start, 0)
57 57 end = revfix(repo, end, repo.changelog.count() - 1)
58 58 step = start > end and -1 or 1
59 59 for rev in xrange(start, end+step, step):
60 60 if rev in seen:
61 61 continue
62 62 seen[rev] = 1
63 63 l.append(rev)
64 64 else:
65 65 rev = revfix(repo, spec, None)
66 66 if rev in seen:
67 67 continue
68 68 seen[rev] = 1
69 69 l.append(rev)
70 70
71 71 return l
72 72
73 73 def make_filename(repo, pat, node,
74 74 total=None, seqno=None, revwidth=None, pathname=None):
75 75 node_expander = {
76 76 'H': lambda: hex(node),
77 77 'R': lambda: str(repo.changelog.rev(node)),
78 78 'h': lambda: short(node),
79 79 }
80 80 expander = {
81 81 '%': lambda: '%',
82 82 'b': lambda: os.path.basename(repo.root),
83 83 }
84 84
85 85 try:
86 86 if node:
87 87 expander.update(node_expander)
88 88 if node and revwidth is not None:
89 89 expander['r'] = (lambda:
90 90 str(repo.changelog.rev(node)).zfill(revwidth))
91 91 if total is not None:
92 92 expander['N'] = lambda: str(total)
93 93 if seqno is not None:
94 94 expander['n'] = lambda: str(seqno)
95 95 if total is not None and seqno is not None:
96 96 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
97 97 if pathname is not None:
98 98 expander['s'] = lambda: os.path.basename(pathname)
99 99 expander['d'] = lambda: os.path.dirname(pathname) or '.'
100 100 expander['p'] = lambda: pathname
101 101
102 102 newname = []
103 103 patlen = len(pat)
104 104 i = 0
105 105 while i < patlen:
106 106 c = pat[i]
107 107 if c == '%':
108 108 i += 1
109 109 c = pat[i]
110 110 c = expander[c]()
111 111 newname.append(c)
112 112 i += 1
113 113 return ''.join(newname)
114 114 except KeyError, inst:
115 115 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
116 116 inst.args[0])
117 117
118 118 def make_file(repo, pat, node=None,
119 119 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
120 120 if not pat or pat == '-':
121 121 return 'w' in mode and sys.stdout or sys.stdin
122 122 if hasattr(pat, 'write') and 'w' in mode:
123 123 return pat
124 124 if hasattr(pat, 'read') and 'r' in mode:
125 125 return pat
126 126 return open(make_filename(repo, pat, node, total, seqno, revwidth,
127 127 pathname),
128 128 mode)
129 129
130 130 def matchpats(repo, pats=[], opts={}, head=''):
131 131 cwd = repo.getcwd()
132 132 if not pats and cwd:
133 133 opts['include'] = [os.path.join(cwd, i)
134 134 for i in opts.get('include', [])]
135 135 opts['exclude'] = [os.path.join(cwd, x)
136 136 for x in opts.get('exclude', [])]
137 137 cwd = ''
138 138 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
139 139 opts.get('exclude'), head)
140 140
141 141 def walk(repo, pats=[], opts={}, node=None, head='', badmatch=None):
142 142 files, matchfn, anypats = matchpats(repo, pats, opts, head)
143 143 exact = dict.fromkeys(files)
144 144 for src, fn in repo.walk(node=node, files=files, match=matchfn,
145 145 badmatch=badmatch):
146 146 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
147 147
148 148 def findrenames(repo, added=None, removed=None, threshold=0.5):
149 149 if added is None or removed is None:
150 150 added, removed = repo.status()[1:3]
151 151 changes = repo.changelog.read(repo.dirstate.parents()[0])
152 152 mf = repo.manifest.read(changes[0])
153 153 for a in added:
154 154 aa = repo.wread(a)
155 155 bestscore, bestname = None, None
156 156 for r in removed:
157 157 rr = repo.file(r).read(mf[r])
158 158 delta = mdiff.textdiff(aa, rr)
159 159 if len(delta) < len(aa):
160 160 myscore = 1.0 - (float(len(delta)) / len(aa))
161 161 if bestscore is None or myscore > bestscore:
162 162 bestscore, bestname = myscore, r
163 163 if bestname and bestscore >= threshold:
164 164 yield bestname, a, bestscore
165 165
166 166 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None,
167 167 similarity=None):
168 168 if dry_run is None:
169 169 dry_run = opts.get('dry_run')
170 170 if similarity is None:
171 171 similarity = float(opts.get('similarity') or 0)
172 172 add, remove = [], []
173 173 mapping = {}
174 174 for src, abs, rel, exact in walk(repo, pats, opts):
175 175 if src == 'f' and repo.dirstate.state(abs) == '?':
176 176 add.append(abs)
177 177 mapping[abs] = rel, exact
178 178 if repo.ui.verbose or not exact:
179 179 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
180 180 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
181 181 remove.append(abs)
182 182 mapping[abs] = rel, exact
183 183 if repo.ui.verbose or not exact:
184 184 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
185 185 if not dry_run:
186 186 repo.add(add, wlock=wlock)
187 187 repo.remove(remove, wlock=wlock)
188 188 if similarity > 0:
189 189 for old, new, score in findrenames(repo, add, remove, similarity):
190 190 oldrel, oldexact = mapping[old]
191 191 newrel, newexact = mapping[new]
192 192 if repo.ui.verbose or not oldexact or not newexact:
193 193 repo.ui.status(_('recording removal of %s as rename to %s '
194 194 '(%d%% similar)\n') %
195 195 (oldrel, newrel, score * 100))
196 196 if not dry_run:
197 197 repo.copy(old, new, wlock=wlock)
198 198
199 199 class uibuffer(object):
200 200 # Implement and delegate some ui protocol. Save hunks of
201 201 # output for later display in the desired order.
202 202 def __init__(self, ui):
203 203 self.ui = ui
204 204 self.hunk = {}
205 205 self.header = {}
206 206 self.quiet = ui.quiet
207 207 self.verbose = ui.verbose
208 208 self.debugflag = ui.debugflag
209 209 self.lastheader = None
210 210 def note(self, *args):
211 211 if self.verbose:
212 212 self.write(*args)
213 213 def status(self, *args):
214 214 if not self.quiet:
215 215 self.write(*args)
216 216 def debug(self, *args):
217 217 if self.debugflag:
218 218 self.write(*args)
219 219 def write(self, *args):
220 220 self.hunk.setdefault(self.rev, []).extend(args)
221 221 def write_header(self, *args):
222 222 self.header.setdefault(self.rev, []).extend(args)
223 223 def mark(self, rev):
224 224 self.rev = rev
225 225 def flush(self, rev):
226 226 if rev in self.header:
227 227 h = "".join(self.header[rev])
228 228 if h != self.lastheader:
229 229 self.lastheader = h
230 230 self.ui.write(h)
231 231 del self.header[rev]
232 232 if rev in self.hunk:
233 233 self.ui.write("".join(self.hunk[rev]))
234 234 del self.hunk[rev]
235 235 return 1
236 236 return 0
237 237
238 238 class changeset_printer(object):
239 239 '''show changeset information when templating not requested.'''
240 240
241 241 def __init__(self, ui, repo, patch, brinfo, buffered):
242 242 self.ui = ui
243 243 self.repo = repo
244 244 self.buffered = buffered
245 245 self.patch = patch
246 246 self.brinfo = brinfo
247 247 if buffered:
248 248 self.ui = uibuffer(ui)
249 249
250 250 def flush(self, rev):
251 251 return self.ui.flush(rev)
252 252
253 253 def show(self, rev=0, changenode=None, copies=None):
254 254 '''show a single changeset or file revision'''
255 255 if self.buffered:
256 256 self.ui.mark(rev)
257 257 log = self.repo.changelog
258 258 if changenode is None:
259 259 changenode = log.node(rev)
260 260 elif not rev:
261 261 rev = log.rev(changenode)
262 262
263 263 if self.ui.quiet:
264 264 self.ui.write("%d:%s\n" % (rev, short(changenode)))
265 265 return
266 266
267 267 changes = log.read(changenode)
268 268 date = util.datestr(changes[2])
269 269 extra = changes[5]
270 270 branch = extra.get("branch")
271 271
272 272 hexfunc = self.ui.debugflag and hex or short
273 273
274 274 parents = log.parentrevs(rev)
275 275 if not self.ui.debugflag:
276 276 if parents[1] == nullrev:
277 277 if parents[0] >= rev - 1:
278 278 parents = []
279 279 else:
280 280 parents = [parents[0]]
281 281 parents = [(p, hexfunc(log.node(p))) for p in parents]
282 282
283 283 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
284 284
285 285 if branch:
286 286 self.ui.write(_("branch: %s\n") % branch)
287 287 for tag in self.repo.nodetags(changenode):
288 288 self.ui.write(_("tag: %s\n") % tag)
289 289 for parent in parents:
290 290 self.ui.write(_("parent: %d:%s\n") % parent)
291 291
292 292 if self.brinfo:
293 293 br = self.repo.branchlookup([changenode])
294 294 if br:
295 295 self.ui.write(_("branch: %s\n") % " ".join(br[changenode]))
296 296
297 297 if self.ui.debugflag:
298 298 self.ui.write(_("manifest: %d:%s\n") %
299 299 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
300 300 self.ui.write(_("user: %s\n") % changes[1])
301 301 self.ui.write(_("date: %s\n") % date)
302 302
303 303 if self.ui.debugflag:
304 304 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
305 305 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
306 306 files):
307 307 if value:
308 308 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
309 309 elif changes[3] and self.ui.verbose:
310 310 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
311 311 if copies and self.ui.verbose:
312 312 copies = ['%s (%s)' % c for c in copies]
313 313 self.ui.write(_("copies: %s\n") % ' '.join(copies))
314 314
315 315 if extra and self.ui.debugflag:
316 316 extraitems = extra.items()
317 317 extraitems.sort()
318 318 for key, value in extraitems:
319 319 self.ui.write(_("extra: %s=%s\n")
320 320 % (key, value.encode('string_escape')))
321 321
322 322 description = changes[4].strip()
323 323 if description:
324 324 if self.ui.verbose:
325 325 self.ui.write(_("description:\n"))
326 326 self.ui.write(description)
327 327 self.ui.write("\n\n")
328 328 else:
329 329 self.ui.write(_("summary: %s\n") %
330 330 description.splitlines()[0])
331 331 self.ui.write("\n")
332 332
333 333 self.showpatch(changenode)
334 334
335 335 def showpatch(self, node):
336 336 if self.patch:
337 337 prev = self.repo.changelog.parents(node)[0]
338 338 patch.diff(self.repo, prev, node, fp=self.ui)
339 339 self.ui.write("\n")
340 340
341 341 class changeset_templater(changeset_printer):
342 342 '''format changeset information.'''
343 343
344 344 def __init__(self, ui, repo, patch, brinfo, mapfile, buffered):
345 345 changeset_printer.__init__(self, ui, repo, patch, brinfo, buffered)
346 346 self.t = templater.templater(mapfile, templater.common_filters,
347 347 cache={'parent': '{rev}:{node|short} ',
348 348 'manifest': '{rev}:{node|short}',
349 349 'filecopy': '{name} ({source})'})
350 350
351 351 def use_template(self, t):
352 352 '''set template string to use'''
353 353 self.t.cache['changeset'] = t
354 354
355 355 def show(self, rev=0, changenode=None, copies=[], **props):
356 356 '''show a single changeset or file revision'''
357 357 if self.buffered:
358 358 self.ui.mark(rev)
359 359 log = self.repo.changelog
360 360 if changenode is None:
361 361 changenode = log.node(rev)
362 362 elif not rev:
363 363 rev = log.rev(changenode)
364 364
365 365 changes = log.read(changenode)
366 366
367 367 def showlist(name, values, plural=None, **args):
368 368 '''expand set of values.
369 369 name is name of key in template map.
370 370 values is list of strings or dicts.
371 371 plural is plural of name, if not simply name + 's'.
372 372
373 373 expansion works like this, given name 'foo'.
374 374
375 375 if values is empty, expand 'no_foos'.
376 376
377 377 if 'foo' not in template map, return values as a string,
378 378 joined by space.
379 379
380 380 expand 'start_foos'.
381 381
382 382 for each value, expand 'foo'. if 'last_foo' in template
383 383 map, expand it instead of 'foo' for last key.
384 384
385 385 expand 'end_foos'.
386 386 '''
387 387 if plural: names = plural
388 388 else: names = name + 's'
389 389 if not values:
390 390 noname = 'no_' + names
391 391 if noname in self.t:
392 392 yield self.t(noname, **args)
393 393 return
394 394 if name not in self.t:
395 395 if isinstance(values[0], str):
396 396 yield ' '.join(values)
397 397 else:
398 398 for v in values:
399 399 yield dict(v, **args)
400 400 return
401 401 startname = 'start_' + names
402 402 if startname in self.t:
403 403 yield self.t(startname, **args)
404 404 vargs = args.copy()
405 405 def one(v, tag=name):
406 406 try:
407 407 vargs.update(v)
408 408 except (AttributeError, ValueError):
409 409 try:
410 410 for a, b in v:
411 411 vargs[a] = b
412 412 except ValueError:
413 413 vargs[name] = v
414 414 return self.t(tag, **vargs)
415 415 lastname = 'last_' + name
416 416 if lastname in self.t:
417 417 last = values.pop()
418 418 else:
419 419 last = None
420 420 for v in values:
421 421 yield one(v)
422 422 if last is not None:
423 423 yield one(last, tag=lastname)
424 424 endname = 'end_' + names
425 425 if endname in self.t:
426 426 yield self.t(endname, **args)
427 427
428 428 def showbranches(**args):
429 429 branch = changes[5].get("branch")
430 430 if branch:
431 431 return showlist('branch', [branch], plural='branches', **args)
432 432 # add old style branches if requested
433 433 if self.brinfo:
434 434 br = self.repo.branchlookup([changenode])
435 435 if changenode in br:
436 436 return showlist('branch', br[changenode],
437 437 plural='branches', **args)
438 438
439 439 def showparents(**args):
440 440 parents = [[('rev', log.rev(p)), ('node', hex(p))]
441 441 for p in log.parents(changenode)
442 442 if self.ui.debugflag or p != nullid]
443 443 if (not self.ui.debugflag and len(parents) == 1 and
444 444 parents[0][0][1] == rev - 1):
445 445 return
446 446 return showlist('parent', parents, **args)
447 447
448 448 def showtags(**args):
449 449 return showlist('tag', self.repo.nodetags(changenode), **args)
450 450
451 451 def showextras(**args):
452 452 extras = changes[5].items()
453 453 extras.sort()
454 454 for key, value in extras:
455 455 args = args.copy()
456 456 args.update(dict(key=key, value=value))
457 457 yield self.t('extra', **args)
458 458
459 459 def showcopies(**args):
460 460 c = [{'name': x[0], 'source': x[1]} for x in copies]
461 461 return showlist('file_copy', c, plural='file_copies', **args)
462 462
463 463 if self.ui.debugflag:
464 464 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
465 465 def showfiles(**args):
466 466 return showlist('file', files[0], **args)
467 467 def showadds(**args):
468 468 return showlist('file_add', files[1], **args)
469 469 def showdels(**args):
470 470 return showlist('file_del', files[2], **args)
471 471 def showmanifest(**args):
472 472 args = args.copy()
473 473 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
474 474 node=hex(changes[0])))
475 475 return self.t('manifest', **args)
476 476 else:
477 477 def showfiles(**args):
478 478 return showlist('file', changes[3], **args)
479 479 showadds = ''
480 480 showdels = ''
481 481 showmanifest = ''
482 482
483 483 defprops = {
484 484 'author': changes[1],
485 485 'branches': showbranches,
486 486 'date': changes[2],
487 487 'desc': changes[4],
488 488 'file_adds': showadds,
489 489 'file_dels': showdels,
490 490 'files': showfiles,
491 491 'file_copies': showcopies,
492 492 'manifest': showmanifest,
493 493 'node': hex(changenode),
494 494 'parents': showparents,
495 495 'rev': rev,
496 496 'tags': showtags,
497 497 'extras': showextras,
498 498 }
499 499 props = props.copy()
500 500 props.update(defprops)
501 501
502 502 try:
503 503 if self.ui.debugflag and 'header_debug' in self.t:
504 504 key = 'header_debug'
505 505 elif self.ui.quiet and 'header_quiet' in self.t:
506 506 key = 'header_quiet'
507 507 elif self.ui.verbose and 'header_verbose' in self.t:
508 508 key = 'header_verbose'
509 509 elif 'header' in self.t:
510 510 key = 'header'
511 511 else:
512 512 key = ''
513 513 if key:
514 514 h = templater.stringify(self.t(key, **props))
515 515 if self.buffered:
516 516 self.ui.write_header(h)
517 517 else:
518 518 self.ui.write(h)
519 519 if self.ui.debugflag and 'changeset_debug' in self.t:
520 520 key = 'changeset_debug'
521 521 elif self.ui.quiet and 'changeset_quiet' in self.t:
522 522 key = 'changeset_quiet'
523 523 elif self.ui.verbose and 'changeset_verbose' in self.t:
524 524 key = 'changeset_verbose'
525 525 else:
526 526 key = 'changeset'
527 527 self.ui.write(templater.stringify(self.t(key, **props)))
528 528 self.showpatch(changenode)
529 529 except KeyError, inst:
530 530 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
531 531 inst.args[0]))
532 532 except SyntaxError, inst:
533 533 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
534 534
535 535 class stringio(object):
536 536 '''wrap cStringIO for use by changeset_templater.'''
537 537 def __init__(self):
538 538 self.fp = cStringIO.StringIO()
539 539
540 540 def write(self, *args):
541 541 for a in args:
542 542 self.fp.write(a)
543 543
544 544 write_header = write
545 545
546 546 def __getattr__(self, key):
547 547 return getattr(self.fp, key)
548 548
549 549 def show_changeset(ui, repo, opts, buffered=False):
550 550 """show one changeset using template or regular display.
551 551
552 552 Display format will be the first non-empty hit of:
553 553 1. option 'template'
554 554 2. option 'style'
555 555 3. [ui] setting 'logtemplate'
556 556 4. [ui] setting 'style'
557 557 If all of these values are either the unset or the empty string,
558 558 regular display via changeset_printer() is done.
559 559 """
560 560 # options
561 561 patch = opts.get('patch')
562 562 br = None
563 563 if opts.get('branches'):
564 564 ui.warn(_("the --branches option is deprecated, "
565 565 "please use 'hg branches' instead\n"))
566 566 br = True
567 567 tmpl = opts.get('template')
568 568 mapfile = None
569 569 if tmpl:
570 570 tmpl = templater.parsestring(tmpl, quoted=False)
571 571 else:
572 572 mapfile = opts.get('style')
573 573 # ui settings
574 574 if not mapfile:
575 575 tmpl = ui.config('ui', 'logtemplate')
576 576 if tmpl:
577 577 tmpl = templater.parsestring(tmpl)
578 578 else:
579 579 mapfile = ui.config('ui', 'style')
580 580
581 581 if tmpl or mapfile:
582 582 if mapfile:
583 583 if not os.path.split(mapfile)[0]:
584 584 mapname = (templater.templatepath('map-cmdline.' + mapfile)
585 585 or templater.templatepath(mapfile))
586 586 if mapname: mapfile = mapname
587 587 try:
588 588 t = changeset_templater(ui, repo, patch, br, mapfile, buffered)
589 589 except SyntaxError, inst:
590 590 raise util.Abort(inst.args[0])
591 591 if tmpl: t.use_template(tmpl)
592 592 return t
593 593 return changeset_printer(ui, repo, patch, br, buffered)
594 594
595 def walkchangerevs(ui, repo, pats, change, opts):
596 '''Iterate over files and the revs they changed in.
597
598 Callers most commonly need to iterate backwards over the history
599 it is interested in. Doing so has awful (quadratic-looking)
600 performance, so we use iterators in a "windowed" way.
601
602 We walk a window of revisions in the desired order. Within the
603 window, we first walk forwards to gather data, then in the desired
604 order (usually backwards) to display it.
605
606 This function returns an (iterator, matchfn) tuple. The iterator
607 yields 3-tuples. They will be of one of the following forms:
608
609 "window", incrementing, lastrev: stepping through a window,
610 positive if walking forwards through revs, last rev in the
611 sequence iterated over - use to reset state for the current window
612
613 "add", rev, fns: out-of-order traversal of the given file names
614 fns, which changed during revision rev - use to gather data for
615 possible display
616
617 "iter", rev, None: in-order traversal of the revs earlier iterated
618 over with "add" - use to display data'''
619
620 def increasing_windows(start, end, windowsize=8, sizelimit=512):
621 if start < end:
622 while start < end:
623 yield start, min(windowsize, end-start)
624 start += windowsize
625 if windowsize < sizelimit:
626 windowsize *= 2
627 else:
628 while start > end:
629 yield start, min(windowsize, start-end-1)
630 start -= windowsize
631 if windowsize < sizelimit:
632 windowsize *= 2
633
634 files, matchfn, anypats = matchpats(repo, pats, opts)
635 follow = opts.get('follow') or opts.get('follow_first')
636
637 if repo.changelog.count() == 0:
638 return [], matchfn
639
640 if follow:
641 defrange = '%s:0' % repo.changectx().rev()
642 else:
643 defrange = 'tip:0'
644 revs = revrange(ui, repo, opts['rev'] or [defrange])
645 wanted = {}
646 slowpath = anypats
647 fncache = {}
648
649 if not slowpath and not files:
650 # No files, no patterns. Display all revs.
651 wanted = dict.fromkeys(revs)
652 copies = []
653 if not slowpath:
654 # Only files, no patterns. Check the history of each file.
655 def filerevgen(filelog, node):
656 cl_count = repo.changelog.count()
657 if node is None:
658 last = filelog.count() - 1
659 else:
660 last = filelog.rev(node)
661 for i, window in increasing_windows(last, nullrev):
662 revs = []
663 for j in xrange(i - window, i + 1):
664 n = filelog.node(j)
665 revs.append((filelog.linkrev(n),
666 follow and filelog.renamed(n)))
667 revs.reverse()
668 for rev in revs:
669 # only yield rev for which we have the changelog, it can
670 # happen while doing "hg log" during a pull or commit
671 if rev[0] < cl_count:
672 yield rev
673 def iterfiles():
674 for filename in files:
675 yield filename, None
676 for filename_node in copies:
677 yield filename_node
678 minrev, maxrev = min(revs), max(revs)
679 for file_, node in iterfiles():
680 filelog = repo.file(file_)
681 # A zero count may be a directory or deleted file, so
682 # try to find matching entries on the slow path.
683 if filelog.count() == 0:
684 slowpath = True
685 break
686 for rev, copied in filerevgen(filelog, node):
687 if rev <= maxrev:
688 if rev < minrev:
689 break
690 fncache.setdefault(rev, [])
691 fncache[rev].append(file_)
692 wanted[rev] = 1
693 if follow and copied:
694 copies.append(copied)
695 if slowpath:
696 if follow:
697 raise util.Abort(_('can only follow copies/renames for explicit '
698 'file names'))
699
700 # The slow path checks files modified in every changeset.
701 def changerevgen():
702 for i, window in increasing_windows(repo.changelog.count()-1,
703 nullrev):
704 for j in xrange(i - window, i + 1):
705 yield j, change(j)[3]
706
707 for rev, changefiles in changerevgen():
708 matches = filter(matchfn, changefiles)
709 if matches:
710 fncache[rev] = matches
711 wanted[rev] = 1
712
713 class followfilter:
714 def __init__(self, onlyfirst=False):
715 self.startrev = nullrev
716 self.roots = []
717 self.onlyfirst = onlyfirst
718
719 def match(self, rev):
720 def realparents(rev):
721 if self.onlyfirst:
722 return repo.changelog.parentrevs(rev)[0:1]
723 else:
724 return filter(lambda x: x != nullrev,
725 repo.changelog.parentrevs(rev))
726
727 if self.startrev == nullrev:
728 self.startrev = rev
729 return True
730
731 if rev > self.startrev:
732 # forward: all descendants
733 if not self.roots:
734 self.roots.append(self.startrev)
735 for parent in realparents(rev):
736 if parent in self.roots:
737 self.roots.append(rev)
738 return True
739 else:
740 # backwards: all parents
741 if not self.roots:
742 self.roots.extend(realparents(self.startrev))
743 if rev in self.roots:
744 self.roots.remove(rev)
745 self.roots.extend(realparents(rev))
746 return True
747
748 return False
749
750 # it might be worthwhile to do this in the iterator if the rev range
751 # is descending and the prune args are all within that range
752 for rev in opts.get('prune', ()):
753 rev = repo.changelog.rev(repo.lookup(rev))
754 ff = followfilter()
755 stop = min(revs[0], revs[-1])
756 for x in xrange(rev, stop-1, -1):
757 if ff.match(x) and x in wanted:
758 del wanted[x]
759
760 def iterate():
761 if follow and not files:
762 ff = followfilter(onlyfirst=opts.get('follow_first'))
763 def want(rev):
764 if ff.match(rev) and rev in wanted:
765 return True
766 return False
767 else:
768 def want(rev):
769 return rev in wanted
770
771 for i, window in increasing_windows(0, len(revs)):
772 yield 'window', revs[0] < revs[-1], revs[-1]
773 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
774 srevs = list(nrevs)
775 srevs.sort()
776 for rev in srevs:
777 fns = fncache.get(rev)
778 if not fns:
779 def fns_generator():
780 for f in change(rev)[3]:
781 if matchfn(f):
782 yield f
783 fns = fns_generator()
784 yield 'add', rev, fns
785 for rev in nrevs:
786 yield 'iter', rev, None
787 return iterate(), matchfn
@@ -1,3334 +1,3140
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from demandload import demandload
9 9 from node import *
10 10 from i18n import gettext as _
11 11 demandload(globals(), "os re sys signal imp urllib pdb shlex")
12 12 demandload(globals(), "fancyopts ui hg util lock revlog bundlerepo")
13 13 demandload(globals(), "difflib patch tempfile time")
14 14 demandload(globals(), "traceback errno version atexit bz2")
15 15 demandload(globals(), "archival changegroup cmdutil hgweb.server sshserver")
16 16
17 17 class UnknownCommand(Exception):
18 18 """Exception raised if command is not in the command table."""
19 19 class AmbiguousCommand(Exception):
20 20 """Exception raised if command shortcut matches more than one command."""
21 21
22 22 def bail_if_changed(repo):
23 23 modified, added, removed, deleted = repo.status()[:4]
24 24 if modified or added or removed or deleted:
25 25 raise util.Abort(_("outstanding uncommitted changes"))
26 26
27 27 def relpath(repo, args):
28 28 cwd = repo.getcwd()
29 29 if cwd:
30 30 return [util.normpath(os.path.join(cwd, x)) for x in args]
31 31 return args
32 32
33 33 def logmessage(opts):
34 34 """ get the log message according to -m and -l option """
35 35 message = opts['message']
36 36 logfile = opts['logfile']
37 37
38 38 if message and logfile:
39 39 raise util.Abort(_('options --message and --logfile are mutually '
40 40 'exclusive'))
41 41 if not message and logfile:
42 42 try:
43 43 if logfile == '-':
44 44 message = sys.stdin.read()
45 45 else:
46 46 message = open(logfile).read()
47 47 except IOError, inst:
48 48 raise util.Abort(_("can't read commit message '%s': %s") %
49 49 (logfile, inst.strerror))
50 50 return message
51 51
52 def walkchangerevs(ui, repo, pats, change, opts):
53 '''Iterate over files and the revs they changed in.
54
55 Callers most commonly need to iterate backwards over the history
56 it is interested in. Doing so has awful (quadratic-looking)
57 performance, so we use iterators in a "windowed" way.
58
59 We walk a window of revisions in the desired order. Within the
60 window, we first walk forwards to gather data, then in the desired
61 order (usually backwards) to display it.
62
63 This function returns an (iterator, matchfn) tuple. The iterator
64 yields 3-tuples. They will be of one of the following forms:
65
66 "window", incrementing, lastrev: stepping through a window,
67 positive if walking forwards through revs, last rev in the
68 sequence iterated over - use to reset state for the current window
69
70 "add", rev, fns: out-of-order traversal of the given file names
71 fns, which changed during revision rev - use to gather data for
72 possible display
73
74 "iter", rev, None: in-order traversal of the revs earlier iterated
75 over with "add" - use to display data'''
76
77 def increasing_windows(start, end, windowsize=8, sizelimit=512):
78 if start < end:
79 while start < end:
80 yield start, min(windowsize, end-start)
81 start += windowsize
82 if windowsize < sizelimit:
83 windowsize *= 2
84 else:
85 while start > end:
86 yield start, min(windowsize, start-end-1)
87 start -= windowsize
88 if windowsize < sizelimit:
89 windowsize *= 2
90
91 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
92 follow = opts.get('follow') or opts.get('follow_first')
93
94 if repo.changelog.count() == 0:
95 return [], matchfn
96
97 if follow:
98 defrange = '%s:0' % repo.changectx().rev()
99 else:
100 defrange = 'tip:0'
101 revs = cmdutil.revrange(ui, repo, opts['rev'] or [defrange])
102 wanted = {}
103 slowpath = anypats
104 fncache = {}
105
106 if not slowpath and not files:
107 # No files, no patterns. Display all revs.
108 wanted = dict.fromkeys(revs)
109 copies = []
110 if not slowpath:
111 # Only files, no patterns. Check the history of each file.
112 def filerevgen(filelog, node):
113 cl_count = repo.changelog.count()
114 if node is None:
115 last = filelog.count() - 1
116 else:
117 last = filelog.rev(node)
118 for i, window in increasing_windows(last, nullrev):
119 revs = []
120 for j in xrange(i - window, i + 1):
121 n = filelog.node(j)
122 revs.append((filelog.linkrev(n),
123 follow and filelog.renamed(n)))
124 revs.reverse()
125 for rev in revs:
126 # only yield rev for which we have the changelog, it can
127 # happen while doing "hg log" during a pull or commit
128 if rev[0] < cl_count:
129 yield rev
130 def iterfiles():
131 for filename in files:
132 yield filename, None
133 for filename_node in copies:
134 yield filename_node
135 minrev, maxrev = min(revs), max(revs)
136 for file_, node in iterfiles():
137 filelog = repo.file(file_)
138 # A zero count may be a directory or deleted file, so
139 # try to find matching entries on the slow path.
140 if filelog.count() == 0:
141 slowpath = True
142 break
143 for rev, copied in filerevgen(filelog, node):
144 if rev <= maxrev:
145 if rev < minrev:
146 break
147 fncache.setdefault(rev, [])
148 fncache[rev].append(file_)
149 wanted[rev] = 1
150 if follow and copied:
151 copies.append(copied)
152 if slowpath:
153 if follow:
154 raise util.Abort(_('can only follow copies/renames for explicit '
155 'file names'))
156
157 # The slow path checks files modified in every changeset.
158 def changerevgen():
159 for i, window in increasing_windows(repo.changelog.count()-1,
160 nullrev):
161 for j in xrange(i - window, i + 1):
162 yield j, change(j)[3]
163
164 for rev, changefiles in changerevgen():
165 matches = filter(matchfn, changefiles)
166 if matches:
167 fncache[rev] = matches
168 wanted[rev] = 1
169
170 class followfilter:
171 def __init__(self, onlyfirst=False):
172 self.startrev = nullrev
173 self.roots = []
174 self.onlyfirst = onlyfirst
175
176 def match(self, rev):
177 def realparents(rev):
178 if self.onlyfirst:
179 return repo.changelog.parentrevs(rev)[0:1]
180 else:
181 return filter(lambda x: x != nullrev,
182 repo.changelog.parentrevs(rev))
183
184 if self.startrev == nullrev:
185 self.startrev = rev
186 return True
187
188 if rev > self.startrev:
189 # forward: all descendants
190 if not self.roots:
191 self.roots.append(self.startrev)
192 for parent in realparents(rev):
193 if parent in self.roots:
194 self.roots.append(rev)
195 return True
196 else:
197 # backwards: all parents
198 if not self.roots:
199 self.roots.extend(realparents(self.startrev))
200 if rev in self.roots:
201 self.roots.remove(rev)
202 self.roots.extend(realparents(rev))
203 return True
204
205 return False
206
207 # it might be worthwhile to do this in the iterator if the rev range
208 # is descending and the prune args are all within that range
209 for rev in opts.get('prune', ()):
210 rev = repo.changelog.rev(repo.lookup(rev))
211 ff = followfilter()
212 stop = min(revs[0], revs[-1])
213 for x in xrange(rev, stop-1, -1):
214 if ff.match(x) and x in wanted:
215 del wanted[x]
216
217 def iterate():
218 if follow and not files:
219 ff = followfilter(onlyfirst=opts.get('follow_first'))
220 def want(rev):
221 if ff.match(rev) and rev in wanted:
222 return True
223 return False
224 else:
225 def want(rev):
226 return rev in wanted
227
228 for i, window in increasing_windows(0, len(revs)):
229 yield 'window', revs[0] < revs[-1], revs[-1]
230 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
231 srevs = list(nrevs)
232 srevs.sort()
233 for rev in srevs:
234 fns = fncache.get(rev)
235 if not fns:
236 def fns_generator():
237 for f in change(rev)[3]:
238 if matchfn(f):
239 yield f
240 fns = fns_generator()
241 yield 'add', rev, fns
242 for rev in nrevs:
243 yield 'iter', rev, None
244 return iterate(), matchfn
245
246 52 def write_bundle(cg, filename=None, compress=True):
247 53 """Write a bundle file and return its filename.
248 54
249 55 Existing files will not be overwritten.
250 56 If no filename is specified, a temporary file is created.
251 57 bz2 compression can be turned off.
252 58 The bundle file will be deleted in case of errors.
253 59 """
254 60 class nocompress(object):
255 61 def compress(self, x):
256 62 return x
257 63 def flush(self):
258 64 return ""
259 65
260 66 fh = None
261 67 cleanup = None
262 68 try:
263 69 if filename:
264 70 if os.path.exists(filename):
265 71 raise util.Abort(_("file '%s' already exists") % filename)
266 72 fh = open(filename, "wb")
267 73 else:
268 74 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
269 75 fh = os.fdopen(fd, "wb")
270 76 cleanup = filename
271 77
272 78 if compress:
273 79 fh.write("HG10")
274 80 z = bz2.BZ2Compressor(9)
275 81 else:
276 82 fh.write("HG10UN")
277 83 z = nocompress()
278 84 # parse the changegroup data, otherwise we will block
279 85 # in case of sshrepo because we don't know the end of the stream
280 86
281 87 # an empty chunkiter is the end of the changegroup
282 88 empty = False
283 89 while not empty:
284 90 empty = True
285 91 for chunk in changegroup.chunkiter(cg):
286 92 empty = False
287 93 fh.write(z.compress(changegroup.genchunk(chunk)))
288 94 fh.write(z.compress(changegroup.closechunk()))
289 95 fh.write(z.flush())
290 96 cleanup = None
291 97 return filename
292 98 finally:
293 99 if fh is not None:
294 100 fh.close()
295 101 if cleanup is not None:
296 102 os.unlink(cleanup)
297 103
298 104 def setremoteconfig(ui, opts):
299 105 "copy remote options to ui tree"
300 106 if opts.get('ssh'):
301 107 ui.setconfig("ui", "ssh", opts['ssh'])
302 108 if opts.get('remotecmd'):
303 109 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
304 110
305 111 def show_version(ui):
306 112 """output version and copyright information"""
307 113 ui.write(_("Mercurial Distributed SCM (version %s)\n")
308 114 % version.get_version())
309 115 ui.status(_(
310 116 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
311 117 "This is free software; see the source for copying conditions. "
312 118 "There is NO\nwarranty; "
313 119 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
314 120 ))
315 121
316 122 def help_(ui, name=None, with_version=False):
317 123 """show help for a command, extension, or list of commands
318 124
319 125 With no arguments, print a list of commands and short help.
320 126
321 127 Given a command name, print help for that command.
322 128
323 129 Given an extension name, print help for that extension, and the
324 130 commands it provides."""
325 131 option_lists = []
326 132
327 133 def helpcmd(name):
328 134 if with_version:
329 135 show_version(ui)
330 136 ui.write('\n')
331 137 aliases, i = findcmd(ui, name)
332 138 # synopsis
333 139 ui.write("%s\n\n" % i[2])
334 140
335 141 # description
336 142 doc = i[0].__doc__
337 143 if not doc:
338 144 doc = _("(No help text available)")
339 145 if ui.quiet:
340 146 doc = doc.splitlines(0)[0]
341 147 ui.write("%s\n" % doc.rstrip())
342 148
343 149 if not ui.quiet:
344 150 # aliases
345 151 if len(aliases) > 1:
346 152 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
347 153
348 154 # options
349 155 if i[1]:
350 156 option_lists.append(("options", i[1]))
351 157
352 158 def helplist(select=None):
353 159 h = {}
354 160 cmds = {}
355 161 for c, e in table.items():
356 162 f = c.split("|", 1)[0]
357 163 if select and not select(f):
358 164 continue
359 165 if name == "shortlist" and not f.startswith("^"):
360 166 continue
361 167 f = f.lstrip("^")
362 168 if not ui.debugflag and f.startswith("debug"):
363 169 continue
364 170 doc = e[0].__doc__
365 171 if not doc:
366 172 doc = _("(No help text available)")
367 173 h[f] = doc.splitlines(0)[0].rstrip()
368 174 cmds[f] = c.lstrip("^")
369 175
370 176 fns = h.keys()
371 177 fns.sort()
372 178 m = max(map(len, fns))
373 179 for f in fns:
374 180 if ui.verbose:
375 181 commands = cmds[f].replace("|",", ")
376 182 ui.write(" %s:\n %s\n"%(commands, h[f]))
377 183 else:
378 184 ui.write(' %-*s %s\n' % (m, f, h[f]))
379 185
380 186 def helpext(name):
381 187 try:
382 188 mod = findext(name)
383 189 except KeyError:
384 190 raise UnknownCommand(name)
385 191
386 192 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
387 193 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
388 194 for d in doc[1:]:
389 195 ui.write(d, '\n')
390 196
391 197 ui.status('\n')
392 198 if ui.verbose:
393 199 ui.status(_('list of commands:\n\n'))
394 200 else:
395 201 ui.status(_('list of commands (use "hg help -v %s" '
396 202 'to show aliases and global options):\n\n') % name)
397 203
398 204 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
399 205 helplist(modcmds.has_key)
400 206
401 207 if name and name != 'shortlist':
402 208 try:
403 209 helpcmd(name)
404 210 except UnknownCommand:
405 211 helpext(name)
406 212
407 213 else:
408 214 # program name
409 215 if ui.verbose or with_version:
410 216 show_version(ui)
411 217 else:
412 218 ui.status(_("Mercurial Distributed SCM\n"))
413 219 ui.status('\n')
414 220
415 221 # list of commands
416 222 if name == "shortlist":
417 223 ui.status(_('basic commands (use "hg help" '
418 224 'for the full list or option "-v" for details):\n\n'))
419 225 elif ui.verbose:
420 226 ui.status(_('list of commands:\n\n'))
421 227 else:
422 228 ui.status(_('list of commands (use "hg help -v" '
423 229 'to show aliases and global options):\n\n'))
424 230
425 231 helplist()
426 232
427 233 # global options
428 234 if ui.verbose:
429 235 option_lists.append(("global options", globalopts))
430 236
431 237 # list all option lists
432 238 opt_output = []
433 239 for title, options in option_lists:
434 240 opt_output.append(("\n%s:\n" % title, None))
435 241 for shortopt, longopt, default, desc in options:
436 242 if "DEPRECATED" in desc and not ui.verbose: continue
437 243 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
438 244 longopt and " --%s" % longopt),
439 245 "%s%s" % (desc,
440 246 default
441 247 and _(" (default: %s)") % default
442 248 or "")))
443 249
444 250 if opt_output:
445 251 opts_len = max([len(line[0]) for line in opt_output if line[1]])
446 252 for first, second in opt_output:
447 253 if second:
448 254 ui.write(" %-*s %s\n" % (opts_len, first, second))
449 255 else:
450 256 ui.write("%s\n" % first)
451 257
452 258 # Commands start here, listed alphabetically
453 259
454 260 def add(ui, repo, *pats, **opts):
455 261 """add the specified files on the next commit
456 262
457 263 Schedule files to be version controlled and added to the repository.
458 264
459 265 The files will be added to the repository at the next commit.
460 266
461 267 If no names are given, add all files in the repository.
462 268 """
463 269
464 270 names = []
465 271 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
466 272 if exact:
467 273 if ui.verbose:
468 274 ui.status(_('adding %s\n') % rel)
469 275 names.append(abs)
470 276 elif repo.dirstate.state(abs) == '?':
471 277 ui.status(_('adding %s\n') % rel)
472 278 names.append(abs)
473 279 if not opts.get('dry_run'):
474 280 repo.add(names)
475 281
476 282 def addremove(ui, repo, *pats, **opts):
477 283 """add all new files, delete all missing files
478 284
479 285 Add all new files and remove all missing files from the repository.
480 286
481 287 New files are ignored if they match any of the patterns in .hgignore. As
482 288 with add, these changes take effect at the next commit.
483 289
484 290 Use the -s option to detect renamed files. With a parameter > 0,
485 291 this compares every removed file with every added file and records
486 292 those similar enough as renames. This option takes a percentage
487 293 between 0 (disabled) and 100 (files must be identical) as its
488 294 parameter. Detecting renamed files this way can be expensive.
489 295 """
490 296 sim = float(opts.get('similarity') or 0)
491 297 if sim < 0 or sim > 100:
492 298 raise util.Abort(_('similarity must be between 0 and 100'))
493 299 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
494 300
495 301 def annotate(ui, repo, *pats, **opts):
496 302 """show changeset information per file line
497 303
498 304 List changes in files, showing the revision id responsible for each line
499 305
500 306 This command is useful to discover who did a change or when a change took
501 307 place.
502 308
503 309 Without the -a option, annotate will avoid processing files it
504 310 detects as binary. With -a, annotate will generate an annotation
505 311 anyway, probably with undesirable results.
506 312 """
507 313 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
508 314
509 315 if not pats:
510 316 raise util.Abort(_('at least one file name or pattern required'))
511 317
512 318 opmap = [['user', lambda x: ui.shortuser(x.user())],
513 319 ['number', lambda x: str(x.rev())],
514 320 ['changeset', lambda x: short(x.node())],
515 321 ['date', getdate], ['follow', lambda x: x.path()]]
516 322 if (not opts['user'] and not opts['changeset'] and not opts['date']
517 323 and not opts['follow']):
518 324 opts['number'] = 1
519 325
520 326 ctx = repo.changectx(opts['rev'])
521 327
522 328 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
523 329 node=ctx.node()):
524 330 fctx = ctx.filectx(abs)
525 331 if not opts['text'] and util.binary(fctx.data()):
526 332 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
527 333 continue
528 334
529 335 lines = fctx.annotate(follow=opts.get('follow'))
530 336 pieces = []
531 337
532 338 for o, f in opmap:
533 339 if opts[o]:
534 340 l = [f(n) for n, dummy in lines]
535 341 if l:
536 342 m = max(map(len, l))
537 343 pieces.append(["%*s" % (m, x) for x in l])
538 344
539 345 if pieces:
540 346 for p, l in zip(zip(*pieces), lines):
541 347 ui.write("%s: %s" % (" ".join(p), l[1]))
542 348
543 349 def archive(ui, repo, dest, **opts):
544 350 '''create unversioned archive of a repository revision
545 351
546 352 By default, the revision used is the parent of the working
547 353 directory; use "-r" to specify a different revision.
548 354
549 355 To specify the type of archive to create, use "-t". Valid
550 356 types are:
551 357
552 358 "files" (default): a directory full of files
553 359 "tar": tar archive, uncompressed
554 360 "tbz2": tar archive, compressed using bzip2
555 361 "tgz": tar archive, compressed using gzip
556 362 "uzip": zip archive, uncompressed
557 363 "zip": zip archive, compressed using deflate
558 364
559 365 The exact name of the destination archive or directory is given
560 366 using a format string; see "hg help export" for details.
561 367
562 368 Each member added to an archive file has a directory prefix
563 369 prepended. Use "-p" to specify a format string for the prefix.
564 370 The default is the basename of the archive, with suffixes removed.
565 371 '''
566 372
567 373 node = repo.changectx(opts['rev']).node()
568 374 dest = cmdutil.make_filename(repo, dest, node)
569 375 if os.path.realpath(dest) == repo.root:
570 376 raise util.Abort(_('repository root cannot be destination'))
571 377 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
572 378 kind = opts.get('type') or 'files'
573 379 prefix = opts['prefix']
574 380 if dest == '-':
575 381 if kind == 'files':
576 382 raise util.Abort(_('cannot archive plain files to stdout'))
577 383 dest = sys.stdout
578 384 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
579 385 prefix = cmdutil.make_filename(repo, prefix, node)
580 386 archival.archive(repo, dest, node, kind, not opts['no_decode'],
581 387 matchfn, prefix)
582 388
583 389 def backout(ui, repo, rev, **opts):
584 390 '''reverse effect of earlier changeset
585 391
586 392 Commit the backed out changes as a new changeset. The new
587 393 changeset is a child of the backed out changeset.
588 394
589 395 If you back out a changeset other than the tip, a new head is
590 396 created. This head is the parent of the working directory. If
591 397 you back out an old changeset, your working directory will appear
592 398 old after the backout. You should merge the backout changeset
593 399 with another head.
594 400
595 401 The --merge option remembers the parent of the working directory
596 402 before starting the backout, then merges the new head with that
597 403 changeset afterwards. This saves you from doing the merge by
598 404 hand. The result of this merge is not committed, as for a normal
599 405 merge.'''
600 406
601 407 bail_if_changed(repo)
602 408 op1, op2 = repo.dirstate.parents()
603 409 if op2 != nullid:
604 410 raise util.Abort(_('outstanding uncommitted merge'))
605 411 node = repo.lookup(rev)
606 412 p1, p2 = repo.changelog.parents(node)
607 413 if p1 == nullid:
608 414 raise util.Abort(_('cannot back out a change with no parents'))
609 415 if p2 != nullid:
610 416 if not opts['parent']:
611 417 raise util.Abort(_('cannot back out a merge changeset without '
612 418 '--parent'))
613 419 p = repo.lookup(opts['parent'])
614 420 if p not in (p1, p2):
615 421 raise util.Abort(_('%s is not a parent of %s' %
616 422 (short(p), short(node))))
617 423 parent = p
618 424 else:
619 425 if opts['parent']:
620 426 raise util.Abort(_('cannot use --parent on non-merge changeset'))
621 427 parent = p1
622 428 hg.clean(repo, node, show_stats=False)
623 429 revert_opts = opts.copy()
624 430 revert_opts['all'] = True
625 431 revert_opts['rev'] = hex(parent)
626 432 revert(ui, repo, **revert_opts)
627 433 commit_opts = opts.copy()
628 434 commit_opts['addremove'] = False
629 435 if not commit_opts['message'] and not commit_opts['logfile']:
630 436 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
631 437 commit_opts['force_editor'] = True
632 438 commit(ui, repo, **commit_opts)
633 439 def nice(node):
634 440 return '%d:%s' % (repo.changelog.rev(node), short(node))
635 441 ui.status(_('changeset %s backs out changeset %s\n') %
636 442 (nice(repo.changelog.tip()), nice(node)))
637 443 if op1 != node:
638 444 if opts['merge']:
639 445 ui.status(_('merging with changeset %s\n') % nice(op1))
640 446 n = _lookup(repo, hex(op1))
641 447 hg.merge(repo, n)
642 448 else:
643 449 ui.status(_('the backout changeset is a new head - '
644 450 'do not forget to merge\n'))
645 451 ui.status(_('(use "backout --merge" '
646 452 'if you want to auto-merge)\n'))
647 453
648 454 def branch(ui, repo, label=None):
649 455 """set or show the current branch name
650 456
651 457 With <name>, set the current branch name. Otherwise, show the
652 458 current branch name.
653 459 """
654 460
655 461 if label is not None:
656 462 repo.opener("branch", "w").write(label)
657 463 else:
658 464 b = repo.workingctx().branch()
659 465 if b:
660 466 ui.write("%s\n" % b)
661 467
662 468 def branches(ui, repo):
663 469 """list repository named branches
664 470
665 471 List the repository's named branches.
666 472 """
667 473 b = repo.branchtags()
668 474 l = [(-repo.changelog.rev(n), n, t) for t,n in b.items()]
669 475 l.sort()
670 476 for r, n, t in l:
671 477 hexfunc = ui.debugflag and hex or short
672 478 if ui.quiet:
673 479 ui.write("%s\n" % t)
674 480 else:
675 481 ui.write("%-30s %s:%s\n" % (t, -r, hexfunc(n)))
676 482
677 483 def bundle(ui, repo, fname, dest=None, **opts):
678 484 """create a changegroup file
679 485
680 486 Generate a compressed changegroup file collecting changesets not
681 487 found in the other repository.
682 488
683 489 If no destination repository is specified the destination is assumed
684 490 to have all the nodes specified by one or more --base parameters.
685 491
686 492 The bundle file can then be transferred using conventional means and
687 493 applied to another repository with the unbundle or pull command.
688 494 This is useful when direct push and pull are not available or when
689 495 exporting an entire repository is undesirable.
690 496
691 497 Applying bundles preserves all changeset contents including
692 498 permissions, copy/rename information, and revision history.
693 499 """
694 500 revs = opts.get('rev') or None
695 501 if revs:
696 502 revs = [repo.lookup(rev) for rev in revs]
697 503 base = opts.get('base')
698 504 if base:
699 505 if dest:
700 506 raise util.Abort(_("--base is incompatible with specifiying "
701 507 "a destination"))
702 508 base = [repo.lookup(rev) for rev in base]
703 509 # create the right base
704 510 # XXX: nodesbetween / changegroup* should be "fixed" instead
705 511 o = []
706 512 has = {nullid: None}
707 513 for n in base:
708 514 has.update(repo.changelog.reachable(n))
709 515 if revs:
710 516 visit = list(revs)
711 517 else:
712 518 visit = repo.changelog.heads()
713 519 seen = {}
714 520 while visit:
715 521 n = visit.pop(0)
716 522 parents = [p for p in repo.changelog.parents(n) if p not in has]
717 523 if len(parents) == 0:
718 524 o.insert(0, n)
719 525 else:
720 526 for p in parents:
721 527 if p not in seen:
722 528 seen[p] = 1
723 529 visit.append(p)
724 530 else:
725 531 setremoteconfig(ui, opts)
726 532 dest = ui.expandpath(dest or 'default-push', dest or 'default')
727 533 other = hg.repository(ui, dest)
728 534 o = repo.findoutgoing(other, force=opts['force'])
729 535
730 536 if revs:
731 537 cg = repo.changegroupsubset(o, revs, 'bundle')
732 538 else:
733 539 cg = repo.changegroup(o, 'bundle')
734 540 write_bundle(cg, fname)
735 541
736 542 def cat(ui, repo, file1, *pats, **opts):
737 543 """output the latest or given revisions of files
738 544
739 545 Print the specified files as they were at the given revision.
740 546 If no revision is given then working dir parent is used, or tip
741 547 if no revision is checked out.
742 548
743 549 Output may be to a file, in which case the name of the file is
744 550 given using a format string. The formatting rules are the same as
745 551 for the export command, with the following additions:
746 552
747 553 %s basename of file being printed
748 554 %d dirname of file being printed, or '.' if in repo root
749 555 %p root-relative path name of file being printed
750 556 """
751 557 ctx = repo.changectx(opts['rev'])
752 558 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
753 559 ctx.node()):
754 560 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
755 561 fp.write(ctx.filectx(abs).data())
756 562
757 563 def clone(ui, source, dest=None, **opts):
758 564 """make a copy of an existing repository
759 565
760 566 Create a copy of an existing repository in a new directory.
761 567
762 568 If no destination directory name is specified, it defaults to the
763 569 basename of the source.
764 570
765 571 The location of the source is added to the new repository's
766 572 .hg/hgrc file, as the default to be used for future pulls.
767 573
768 574 For efficiency, hardlinks are used for cloning whenever the source
769 575 and destination are on the same filesystem (note this applies only
770 576 to the repository data, not to the checked out files). Some
771 577 filesystems, such as AFS, implement hardlinking incorrectly, but
772 578 do not report errors. In these cases, use the --pull option to
773 579 avoid hardlinking.
774 580
775 581 You can safely clone repositories and checked out files using full
776 582 hardlinks with
777 583
778 584 $ cp -al REPO REPOCLONE
779 585
780 586 which is the fastest way to clone. However, the operation is not
781 587 atomic (making sure REPO is not modified during the operation is
782 588 up to you) and you have to make sure your editor breaks hardlinks
783 589 (Emacs and most Linux Kernel tools do so).
784 590
785 591 If you use the -r option to clone up to a specific revision, no
786 592 subsequent revisions will be present in the cloned repository.
787 593 This option implies --pull, even on local repositories.
788 594
789 595 See pull for valid source format details.
790 596
791 597 It is possible to specify an ssh:// URL as the destination, but no
792 598 .hg/hgrc and working directory will be created on the remote side.
793 599 Look at the help text for the pull command for important details
794 600 about ssh:// URLs.
795 601 """
796 602 setremoteconfig(ui, opts)
797 603 hg.clone(ui, ui.expandpath(source), dest,
798 604 pull=opts['pull'],
799 605 stream=opts['uncompressed'],
800 606 rev=opts['rev'],
801 607 update=not opts['noupdate'])
802 608
803 609 def commit(ui, repo, *pats, **opts):
804 610 """commit the specified files or all outstanding changes
805 611
806 612 Commit changes to the given files into the repository.
807 613
808 614 If a list of files is omitted, all changes reported by "hg status"
809 615 will be committed.
810 616
811 617 If no commit message is specified, the editor configured in your hgrc
812 618 or in the EDITOR environment variable is started to enter a message.
813 619 """
814 620 message = logmessage(opts)
815 621
816 622 if opts['addremove']:
817 623 cmdutil.addremove(repo, pats, opts)
818 624 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
819 625 if pats:
820 626 modified, added, removed = repo.status(files=fns, match=match)[:3]
821 627 files = modified + added + removed
822 628 else:
823 629 files = []
824 630 try:
825 631 repo.commit(files, message, opts['user'], opts['date'], match,
826 632 force_editor=opts.get('force_editor'))
827 633 except ValueError, inst:
828 634 raise util.Abort(str(inst))
829 635
830 636 def docopy(ui, repo, pats, opts, wlock):
831 637 # called with the repo lock held
832 638 cwd = repo.getcwd()
833 639 errors = 0
834 640 copied = []
835 641 targets = {}
836 642
837 643 def okaytocopy(abs, rel, exact):
838 644 reasons = {'?': _('is not managed'),
839 645 'a': _('has been marked for add'),
840 646 'r': _('has been marked for remove')}
841 647 state = repo.dirstate.state(abs)
842 648 reason = reasons.get(state)
843 649 if reason:
844 650 if state == 'a':
845 651 origsrc = repo.dirstate.copied(abs)
846 652 if origsrc is not None:
847 653 return origsrc
848 654 if exact:
849 655 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
850 656 else:
851 657 return abs
852 658
853 659 def copy(origsrc, abssrc, relsrc, target, exact):
854 660 abstarget = util.canonpath(repo.root, cwd, target)
855 661 reltarget = util.pathto(cwd, abstarget)
856 662 prevsrc = targets.get(abstarget)
857 663 if prevsrc is not None:
858 664 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
859 665 (reltarget, abssrc, prevsrc))
860 666 return
861 667 if (not opts['after'] and os.path.exists(reltarget) or
862 668 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
863 669 if not opts['force']:
864 670 ui.warn(_('%s: not overwriting - file exists\n') %
865 671 reltarget)
866 672 return
867 673 if not opts['after'] and not opts.get('dry_run'):
868 674 os.unlink(reltarget)
869 675 if opts['after']:
870 676 if not os.path.exists(reltarget):
871 677 return
872 678 else:
873 679 targetdir = os.path.dirname(reltarget) or '.'
874 680 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
875 681 os.makedirs(targetdir)
876 682 try:
877 683 restore = repo.dirstate.state(abstarget) == 'r'
878 684 if restore and not opts.get('dry_run'):
879 685 repo.undelete([abstarget], wlock)
880 686 try:
881 687 if not opts.get('dry_run'):
882 688 util.copyfile(relsrc, reltarget)
883 689 restore = False
884 690 finally:
885 691 if restore:
886 692 repo.remove([abstarget], wlock)
887 693 except IOError, inst:
888 694 if inst.errno == errno.ENOENT:
889 695 ui.warn(_('%s: deleted in working copy\n') % relsrc)
890 696 else:
891 697 ui.warn(_('%s: cannot copy - %s\n') %
892 698 (relsrc, inst.strerror))
893 699 errors += 1
894 700 return
895 701 if ui.verbose or not exact:
896 702 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
897 703 targets[abstarget] = abssrc
898 704 if abstarget != origsrc and not opts.get('dry_run'):
899 705 repo.copy(origsrc, abstarget, wlock)
900 706 copied.append((abssrc, relsrc, exact))
901 707
902 708 def targetpathfn(pat, dest, srcs):
903 709 if os.path.isdir(pat):
904 710 abspfx = util.canonpath(repo.root, cwd, pat)
905 711 if destdirexists:
906 712 striplen = len(os.path.split(abspfx)[0])
907 713 else:
908 714 striplen = len(abspfx)
909 715 if striplen:
910 716 striplen += len(os.sep)
911 717 res = lambda p: os.path.join(dest, p[striplen:])
912 718 elif destdirexists:
913 719 res = lambda p: os.path.join(dest, os.path.basename(p))
914 720 else:
915 721 res = lambda p: dest
916 722 return res
917 723
918 724 def targetpathafterfn(pat, dest, srcs):
919 725 if util.patkind(pat, None)[0]:
920 726 # a mercurial pattern
921 727 res = lambda p: os.path.join(dest, os.path.basename(p))
922 728 else:
923 729 abspfx = util.canonpath(repo.root, cwd, pat)
924 730 if len(abspfx) < len(srcs[0][0]):
925 731 # A directory. Either the target path contains the last
926 732 # component of the source path or it does not.
927 733 def evalpath(striplen):
928 734 score = 0
929 735 for s in srcs:
930 736 t = os.path.join(dest, s[0][striplen:])
931 737 if os.path.exists(t):
932 738 score += 1
933 739 return score
934 740
935 741 striplen = len(abspfx)
936 742 if striplen:
937 743 striplen += len(os.sep)
938 744 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
939 745 score = evalpath(striplen)
940 746 striplen1 = len(os.path.split(abspfx)[0])
941 747 if striplen1:
942 748 striplen1 += len(os.sep)
943 749 if evalpath(striplen1) > score:
944 750 striplen = striplen1
945 751 res = lambda p: os.path.join(dest, p[striplen:])
946 752 else:
947 753 # a file
948 754 if destdirexists:
949 755 res = lambda p: os.path.join(dest, os.path.basename(p))
950 756 else:
951 757 res = lambda p: dest
952 758 return res
953 759
954 760
955 761 pats = list(pats)
956 762 if not pats:
957 763 raise util.Abort(_('no source or destination specified'))
958 764 if len(pats) == 1:
959 765 raise util.Abort(_('no destination specified'))
960 766 dest = pats.pop()
961 767 destdirexists = os.path.isdir(dest)
962 768 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
963 769 raise util.Abort(_('with multiple sources, destination must be an '
964 770 'existing directory'))
965 771 if opts['after']:
966 772 tfn = targetpathafterfn
967 773 else:
968 774 tfn = targetpathfn
969 775 copylist = []
970 776 for pat in pats:
971 777 srcs = []
972 778 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
973 779 origsrc = okaytocopy(abssrc, relsrc, exact)
974 780 if origsrc:
975 781 srcs.append((origsrc, abssrc, relsrc, exact))
976 782 if not srcs:
977 783 continue
978 784 copylist.append((tfn(pat, dest, srcs), srcs))
979 785 if not copylist:
980 786 raise util.Abort(_('no files to copy'))
981 787
982 788 for targetpath, srcs in copylist:
983 789 for origsrc, abssrc, relsrc, exact in srcs:
984 790 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
985 791
986 792 if errors:
987 793 ui.warn(_('(consider using --after)\n'))
988 794 return errors, copied
989 795
990 796 def copy(ui, repo, *pats, **opts):
991 797 """mark files as copied for the next commit
992 798
993 799 Mark dest as having copies of source files. If dest is a
994 800 directory, copies are put in that directory. If dest is a file,
995 801 there can only be one source.
996 802
997 803 By default, this command copies the contents of files as they
998 804 stand in the working directory. If invoked with --after, the
999 805 operation is recorded, but no copying is performed.
1000 806
1001 807 This command takes effect in the next commit.
1002 808
1003 809 NOTE: This command should be treated as experimental. While it
1004 810 should properly record copied files, this information is not yet
1005 811 fully used by merge, nor fully reported by log.
1006 812 """
1007 813 wlock = repo.wlock(0)
1008 814 errs, copied = docopy(ui, repo, pats, opts, wlock)
1009 815 return errs
1010 816
1011 817 def debugancestor(ui, index, rev1, rev2):
1012 818 """find the ancestor revision of two revisions in a given index"""
1013 819 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1014 820 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1015 821 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1016 822
1017 823 def debugcomplete(ui, cmd='', **opts):
1018 824 """returns the completion list associated with the given command"""
1019 825
1020 826 if opts['options']:
1021 827 options = []
1022 828 otables = [globalopts]
1023 829 if cmd:
1024 830 aliases, entry = findcmd(ui, cmd)
1025 831 otables.append(entry[1])
1026 832 for t in otables:
1027 833 for o in t:
1028 834 if o[0]:
1029 835 options.append('-%s' % o[0])
1030 836 options.append('--%s' % o[1])
1031 837 ui.write("%s\n" % "\n".join(options))
1032 838 return
1033 839
1034 840 clist = findpossible(ui, cmd).keys()
1035 841 clist.sort()
1036 842 ui.write("%s\n" % "\n".join(clist))
1037 843
1038 844 def debugrebuildstate(ui, repo, rev=None):
1039 845 """rebuild the dirstate as it would look like for the given revision"""
1040 846 if not rev:
1041 847 rev = repo.changelog.tip()
1042 848 else:
1043 849 rev = repo.lookup(rev)
1044 850 change = repo.changelog.read(rev)
1045 851 n = change[0]
1046 852 files = repo.manifest.read(n)
1047 853 wlock = repo.wlock()
1048 854 repo.dirstate.rebuild(rev, files)
1049 855
1050 856 def debugcheckstate(ui, repo):
1051 857 """validate the correctness of the current dirstate"""
1052 858 parent1, parent2 = repo.dirstate.parents()
1053 859 repo.dirstate.read()
1054 860 dc = repo.dirstate.map
1055 861 keys = dc.keys()
1056 862 keys.sort()
1057 863 m1n = repo.changelog.read(parent1)[0]
1058 864 m2n = repo.changelog.read(parent2)[0]
1059 865 m1 = repo.manifest.read(m1n)
1060 866 m2 = repo.manifest.read(m2n)
1061 867 errors = 0
1062 868 for f in dc:
1063 869 state = repo.dirstate.state(f)
1064 870 if state in "nr" and f not in m1:
1065 871 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1066 872 errors += 1
1067 873 if state in "a" and f in m1:
1068 874 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1069 875 errors += 1
1070 876 if state in "m" and f not in m1 and f not in m2:
1071 877 ui.warn(_("%s in state %s, but not in either manifest\n") %
1072 878 (f, state))
1073 879 errors += 1
1074 880 for f in m1:
1075 881 state = repo.dirstate.state(f)
1076 882 if state not in "nrm":
1077 883 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1078 884 errors += 1
1079 885 if errors:
1080 886 error = _(".hg/dirstate inconsistent with current parent's manifest")
1081 887 raise util.Abort(error)
1082 888
1083 889 def showconfig(ui, repo, *values, **opts):
1084 890 """show combined config settings from all hgrc files
1085 891
1086 892 With no args, print names and values of all config items.
1087 893
1088 894 With one arg of the form section.name, print just the value of
1089 895 that config item.
1090 896
1091 897 With multiple args, print names and values of all config items
1092 898 with matching section names."""
1093 899
1094 900 untrusted = bool(opts.get('untrusted'))
1095 901 if values:
1096 902 if len([v for v in values if '.' in v]) > 1:
1097 903 raise util.Abort(_('only one config item permitted'))
1098 904 for section, name, value in ui.walkconfig(untrusted=untrusted):
1099 905 sectname = section + '.' + name
1100 906 if values:
1101 907 for v in values:
1102 908 if v == section:
1103 909 ui.write('%s=%s\n' % (sectname, value))
1104 910 elif v == sectname:
1105 911 ui.write(value, '\n')
1106 912 else:
1107 913 ui.write('%s=%s\n' % (sectname, value))
1108 914
1109 915 def debugsetparents(ui, repo, rev1, rev2=None):
1110 916 """manually set the parents of the current working directory
1111 917
1112 918 This is useful for writing repository conversion tools, but should
1113 919 be used with care.
1114 920 """
1115 921
1116 922 if not rev2:
1117 923 rev2 = hex(nullid)
1118 924
1119 925 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1120 926
1121 927 def debugstate(ui, repo):
1122 928 """show the contents of the current dirstate"""
1123 929 repo.dirstate.read()
1124 930 dc = repo.dirstate.map
1125 931 keys = dc.keys()
1126 932 keys.sort()
1127 933 for file_ in keys:
1128 934 ui.write("%c %3o %10d %s %s\n"
1129 935 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1130 936 time.strftime("%x %X",
1131 937 time.localtime(dc[file_][3])), file_))
1132 938 for f in repo.dirstate.copies():
1133 939 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1134 940
1135 941 def debugdata(ui, file_, rev):
1136 942 """dump the contents of an data file revision"""
1137 943 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1138 944 file_[:-2] + ".i", file_, 0)
1139 945 try:
1140 946 ui.write(r.revision(r.lookup(rev)))
1141 947 except KeyError:
1142 948 raise util.Abort(_('invalid revision identifier %s') % rev)
1143 949
1144 950 def debugindex(ui, file_):
1145 951 """dump the contents of an index file"""
1146 952 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1147 953 ui.write(" rev offset length base linkrev" +
1148 954 " nodeid p1 p2\n")
1149 955 for i in xrange(r.count()):
1150 956 node = r.node(i)
1151 957 pp = r.parents(node)
1152 958 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1153 959 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1154 960 short(node), short(pp[0]), short(pp[1])))
1155 961
1156 962 def debugindexdot(ui, file_):
1157 963 """dump an index DAG as a .dot file"""
1158 964 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1159 965 ui.write("digraph G {\n")
1160 966 for i in xrange(r.count()):
1161 967 node = r.node(i)
1162 968 pp = r.parents(node)
1163 969 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1164 970 if pp[1] != nullid:
1165 971 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1166 972 ui.write("}\n")
1167 973
1168 974 def debugrename(ui, repo, file, rev=None):
1169 975 """dump rename information"""
1170 976 r = repo.file(relpath(repo, [file])[0])
1171 977 if rev:
1172 978 try:
1173 979 # assume all revision numbers are for changesets
1174 980 n = repo.lookup(rev)
1175 981 change = repo.changelog.read(n)
1176 982 m = repo.manifest.read(change[0])
1177 983 n = m[relpath(repo, [file])[0]]
1178 984 except (hg.RepoError, KeyError):
1179 985 n = r.lookup(rev)
1180 986 else:
1181 987 n = r.tip()
1182 988 m = r.renamed(n)
1183 989 if m:
1184 990 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1185 991 else:
1186 992 ui.write(_("not renamed\n"))
1187 993
1188 994 def debugwalk(ui, repo, *pats, **opts):
1189 995 """show how files match on given patterns"""
1190 996 items = list(cmdutil.walk(repo, pats, opts))
1191 997 if not items:
1192 998 return
1193 999 fmt = '%%s %%-%ds %%-%ds %%s' % (
1194 1000 max([len(abs) for (src, abs, rel, exact) in items]),
1195 1001 max([len(rel) for (src, abs, rel, exact) in items]))
1196 1002 for src, abs, rel, exact in items:
1197 1003 line = fmt % (src, abs, rel, exact and 'exact' or '')
1198 1004 ui.write("%s\n" % line.rstrip())
1199 1005
1200 1006 def diff(ui, repo, *pats, **opts):
1201 1007 """diff repository (or selected files)
1202 1008
1203 1009 Show differences between revisions for the specified files.
1204 1010
1205 1011 Differences between files are shown using the unified diff format.
1206 1012
1207 1013 When two revision arguments are given, then changes are shown
1208 1014 between those revisions. If only one revision is specified then
1209 1015 that revision is compared to the working directory, and, when no
1210 1016 revisions are specified, the working directory files are compared
1211 1017 to its parent.
1212 1018
1213 1019 Without the -a option, diff will avoid generating diffs of files
1214 1020 it detects as binary. With -a, diff will generate a diff anyway,
1215 1021 probably with undesirable results.
1216 1022 """
1217 1023 node1, node2 = cmdutil.revpair(ui, repo, opts['rev'])
1218 1024
1219 1025 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1220 1026
1221 1027 patch.diff(repo, node1, node2, fns, match=matchfn,
1222 1028 opts=patch.diffopts(ui, opts))
1223 1029
1224 1030 def export(ui, repo, *changesets, **opts):
1225 1031 """dump the header and diffs for one or more changesets
1226 1032
1227 1033 Print the changeset header and diffs for one or more revisions.
1228 1034
1229 1035 The information shown in the changeset header is: author,
1230 1036 changeset hash, parent and commit comment.
1231 1037
1232 1038 Output may be to a file, in which case the name of the file is
1233 1039 given using a format string. The formatting rules are as follows:
1234 1040
1235 1041 %% literal "%" character
1236 1042 %H changeset hash (40 bytes of hexadecimal)
1237 1043 %N number of patches being generated
1238 1044 %R changeset revision number
1239 1045 %b basename of the exporting repository
1240 1046 %h short-form changeset hash (12 bytes of hexadecimal)
1241 1047 %n zero-padded sequence number, starting at 1
1242 1048 %r zero-padded changeset revision number
1243 1049
1244 1050 Without the -a option, export will avoid generating diffs of files
1245 1051 it detects as binary. With -a, export will generate a diff anyway,
1246 1052 probably with undesirable results.
1247 1053
1248 1054 With the --switch-parent option, the diff will be against the second
1249 1055 parent. It can be useful to review a merge.
1250 1056 """
1251 1057 if not changesets:
1252 1058 raise util.Abort(_("export requires at least one changeset"))
1253 1059 revs = cmdutil.revrange(ui, repo, changesets)
1254 1060 if len(revs) > 1:
1255 1061 ui.note(_('exporting patches:\n'))
1256 1062 else:
1257 1063 ui.note(_('exporting patch:\n'))
1258 1064 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1259 1065 switch_parent=opts['switch_parent'],
1260 1066 opts=patch.diffopts(ui, opts))
1261 1067
1262 1068 def grep(ui, repo, pattern, *pats, **opts):
1263 1069 """search for a pattern in specified files and revisions
1264 1070
1265 1071 Search revisions of files for a regular expression.
1266 1072
1267 1073 This command behaves differently than Unix grep. It only accepts
1268 1074 Python/Perl regexps. It searches repository history, not the
1269 1075 working directory. It always prints the revision number in which
1270 1076 a match appears.
1271 1077
1272 1078 By default, grep only prints output for the first revision of a
1273 1079 file in which it finds a match. To get it to print every revision
1274 1080 that contains a change in match status ("-" for a match that
1275 1081 becomes a non-match, or "+" for a non-match that becomes a match),
1276 1082 use the --all flag.
1277 1083 """
1278 1084 reflags = 0
1279 1085 if opts['ignore_case']:
1280 1086 reflags |= re.I
1281 1087 regexp = re.compile(pattern, reflags)
1282 1088 sep, eol = ':', '\n'
1283 1089 if opts['print0']:
1284 1090 sep = eol = '\0'
1285 1091
1286 1092 fcache = {}
1287 1093 def getfile(fn):
1288 1094 if fn not in fcache:
1289 1095 fcache[fn] = repo.file(fn)
1290 1096 return fcache[fn]
1291 1097
1292 1098 def matchlines(body):
1293 1099 begin = 0
1294 1100 linenum = 0
1295 1101 while True:
1296 1102 match = regexp.search(body, begin)
1297 1103 if not match:
1298 1104 break
1299 1105 mstart, mend = match.span()
1300 1106 linenum += body.count('\n', begin, mstart) + 1
1301 1107 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1302 1108 lend = body.find('\n', mend)
1303 1109 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1304 1110 begin = lend + 1
1305 1111
1306 1112 class linestate(object):
1307 1113 def __init__(self, line, linenum, colstart, colend):
1308 1114 self.line = line
1309 1115 self.linenum = linenum
1310 1116 self.colstart = colstart
1311 1117 self.colend = colend
1312 1118
1313 1119 def __eq__(self, other):
1314 1120 return self.line == other.line
1315 1121
1316 1122 matches = {}
1317 1123 copies = {}
1318 1124 def grepbody(fn, rev, body):
1319 1125 matches[rev].setdefault(fn, [])
1320 1126 m = matches[rev][fn]
1321 1127 for lnum, cstart, cend, line in matchlines(body):
1322 1128 s = linestate(line, lnum, cstart, cend)
1323 1129 m.append(s)
1324 1130
1325 1131 def difflinestates(a, b):
1326 1132 sm = difflib.SequenceMatcher(None, a, b)
1327 1133 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1328 1134 if tag == 'insert':
1329 1135 for i in xrange(blo, bhi):
1330 1136 yield ('+', b[i])
1331 1137 elif tag == 'delete':
1332 1138 for i in xrange(alo, ahi):
1333 1139 yield ('-', a[i])
1334 1140 elif tag == 'replace':
1335 1141 for i in xrange(alo, ahi):
1336 1142 yield ('-', a[i])
1337 1143 for i in xrange(blo, bhi):
1338 1144 yield ('+', b[i])
1339 1145
1340 1146 prev = {}
1341 1147 def display(fn, rev, states, prevstates):
1342 1148 counts = {'-': 0, '+': 0}
1343 1149 filerevmatches = {}
1344 1150 if incrementing or not opts['all']:
1345 1151 a, b, r = prevstates, states, rev
1346 1152 else:
1347 1153 a, b, r = states, prevstates, prev.get(fn, -1)
1348 1154 for change, l in difflinestates(a, b):
1349 1155 cols = [fn, str(r)]
1350 1156 if opts['line_number']:
1351 1157 cols.append(str(l.linenum))
1352 1158 if opts['all']:
1353 1159 cols.append(change)
1354 1160 if opts['user']:
1355 cols.append(ui.shortuser(getchange(r)[1]))
1161 cols.append(ui.shortuser(get(r)[1]))
1356 1162 if opts['files_with_matches']:
1357 1163 c = (fn, r)
1358 1164 if c in filerevmatches:
1359 1165 continue
1360 1166 filerevmatches[c] = 1
1361 1167 else:
1362 1168 cols.append(l.line)
1363 1169 ui.write(sep.join(cols), eol)
1364 1170 counts[change] += 1
1365 1171 return counts['+'], counts['-']
1366 1172
1367 1173 fstate = {}
1368 1174 skip = {}
1369 getchange = util.cachefunc(lambda r:repo.changectx(r).changeset())
1370 changeiter, matchfn = walkchangerevs(ui, repo, pats, getchange, opts)
1175 get = util.cachefunc(lambda r:repo.changectx(r).changeset())
1176 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1371 1177 count = 0
1372 1178 incrementing = False
1373 1179 follow = opts.get('follow')
1374 1180 for st, rev, fns in changeiter:
1375 1181 if st == 'window':
1376 1182 incrementing = rev
1377 1183 matches.clear()
1378 1184 elif st == 'add':
1379 1185 mf = repo.changectx(rev).manifest()
1380 1186 matches[rev] = {}
1381 1187 for fn in fns:
1382 1188 if fn in skip:
1383 1189 continue
1384 1190 fstate.setdefault(fn, {})
1385 1191 try:
1386 1192 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1387 1193 if follow:
1388 1194 copied = getfile(fn).renamed(mf[fn])
1389 1195 if copied:
1390 1196 copies.setdefault(rev, {})[fn] = copied[0]
1391 1197 except KeyError:
1392 1198 pass
1393 1199 elif st == 'iter':
1394 1200 states = matches[rev].items()
1395 1201 states.sort()
1396 1202 for fn, m in states:
1397 1203 copy = copies.get(rev, {}).get(fn)
1398 1204 if fn in skip:
1399 1205 if copy:
1400 1206 skip[copy] = True
1401 1207 continue
1402 1208 if incrementing or not opts['all'] or fstate[fn]:
1403 1209 pos, neg = display(fn, rev, m, fstate[fn])
1404 1210 count += pos + neg
1405 1211 if pos and not opts['all']:
1406 1212 skip[fn] = True
1407 1213 if copy:
1408 1214 skip[copy] = True
1409 1215 fstate[fn] = m
1410 1216 if copy:
1411 1217 fstate[copy] = m
1412 1218 prev[fn] = rev
1413 1219
1414 1220 if not incrementing:
1415 1221 fstate = fstate.items()
1416 1222 fstate.sort()
1417 1223 for fn, state in fstate:
1418 1224 if fn in skip:
1419 1225 continue
1420 1226 if fn not in copies.get(prev[fn], {}):
1421 1227 display(fn, rev, {}, state)
1422 1228 return (count == 0 and 1) or 0
1423 1229
1424 1230 def heads(ui, repo, **opts):
1425 1231 """show current repository heads
1426 1232
1427 1233 Show all repository head changesets.
1428 1234
1429 1235 Repository "heads" are changesets that don't have children
1430 1236 changesets. They are where development generally takes place and
1431 1237 are the usual targets for update and merge operations.
1432 1238 """
1433 1239 if opts['rev']:
1434 1240 heads = repo.heads(repo.lookup(opts['rev']))
1435 1241 else:
1436 1242 heads = repo.heads()
1437 1243 displayer = cmdutil.show_changeset(ui, repo, opts)
1438 1244 for n in heads:
1439 1245 displayer.show(changenode=n)
1440 1246
1441 1247 def identify(ui, repo):
1442 1248 """print information about the working copy
1443 1249
1444 1250 Print a short summary of the current state of the repo.
1445 1251
1446 1252 This summary identifies the repository state using one or two parent
1447 1253 hash identifiers, followed by a "+" if there are uncommitted changes
1448 1254 in the working directory, followed by a list of tags for this revision.
1449 1255 """
1450 1256 parents = [p for p in repo.dirstate.parents() if p != nullid]
1451 1257 if not parents:
1452 1258 ui.write(_("unknown\n"))
1453 1259 return
1454 1260
1455 1261 hexfunc = ui.debugflag and hex or short
1456 1262 modified, added, removed, deleted = repo.status()[:4]
1457 1263 output = ["%s%s" %
1458 1264 ('+'.join([hexfunc(parent) for parent in parents]),
1459 1265 (modified or added or removed or deleted) and "+" or "")]
1460 1266
1461 1267 if not ui.quiet:
1462 1268
1463 1269 branch = repo.workingctx().branch()
1464 1270 if branch:
1465 1271 output.append("(%s)" % branch)
1466 1272
1467 1273 # multiple tags for a single parent separated by '/'
1468 1274 parenttags = ['/'.join(tags)
1469 1275 for tags in map(repo.nodetags, parents) if tags]
1470 1276 # tags for multiple parents separated by ' + '
1471 1277 if parenttags:
1472 1278 output.append(' + '.join(parenttags))
1473 1279
1474 1280 ui.write("%s\n" % ' '.join(output))
1475 1281
1476 1282 def import_(ui, repo, patch1, *patches, **opts):
1477 1283 """import an ordered set of patches
1478 1284
1479 1285 Import a list of patches and commit them individually.
1480 1286
1481 1287 If there are outstanding changes in the working directory, import
1482 1288 will abort unless given the -f flag.
1483 1289
1484 1290 You can import a patch straight from a mail message. Even patches
1485 1291 as attachments work (body part must be type text/plain or
1486 1292 text/x-patch to be used). From and Subject headers of email
1487 1293 message are used as default committer and commit message. All
1488 1294 text/plain body parts before first diff are added to commit
1489 1295 message.
1490 1296
1491 1297 If imported patch was generated by hg export, user and description
1492 1298 from patch override values from message headers and body. Values
1493 1299 given on command line with -m and -u override these.
1494 1300
1495 1301 To read a patch from standard input, use patch name "-".
1496 1302 """
1497 1303 patches = (patch1,) + patches
1498 1304
1499 1305 if not opts['force']:
1500 1306 bail_if_changed(repo)
1501 1307
1502 1308 d = opts["base"]
1503 1309 strip = opts["strip"]
1504 1310
1505 1311 wlock = repo.wlock()
1506 1312 lock = repo.lock()
1507 1313
1508 1314 for p in patches:
1509 1315 pf = os.path.join(d, p)
1510 1316
1511 1317 if pf == '-':
1512 1318 ui.status(_("applying patch from stdin\n"))
1513 1319 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1514 1320 else:
1515 1321 ui.status(_("applying %s\n") % p)
1516 1322 tmpname, message, user, date = patch.extract(ui, file(pf))
1517 1323
1518 1324 if tmpname is None:
1519 1325 raise util.Abort(_('no diffs found'))
1520 1326
1521 1327 try:
1522 1328 if opts['message']:
1523 1329 # pickup the cmdline msg
1524 1330 message = opts['message']
1525 1331 elif message:
1526 1332 # pickup the patch msg
1527 1333 message = message.strip()
1528 1334 else:
1529 1335 # launch the editor
1530 1336 message = None
1531 1337 ui.debug(_('message:\n%s\n') % message)
1532 1338
1533 1339 files = {}
1534 1340 try:
1535 1341 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1536 1342 files=files)
1537 1343 finally:
1538 1344 files = patch.updatedir(ui, repo, files, wlock=wlock)
1539 1345 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1540 1346 finally:
1541 1347 os.unlink(tmpname)
1542 1348
1543 1349 def incoming(ui, repo, source="default", **opts):
1544 1350 """show new changesets found in source
1545 1351
1546 1352 Show new changesets found in the specified path/URL or the default
1547 1353 pull location. These are the changesets that would be pulled if a pull
1548 1354 was requested.
1549 1355
1550 1356 For remote repository, using --bundle avoids downloading the changesets
1551 1357 twice if the incoming is followed by a pull.
1552 1358
1553 1359 See pull for valid source format details.
1554 1360 """
1555 1361 source = ui.expandpath(source)
1556 1362 setremoteconfig(ui, opts)
1557 1363
1558 1364 other = hg.repository(ui, source)
1559 1365 incoming = repo.findincoming(other, force=opts["force"])
1560 1366 if not incoming:
1561 1367 ui.status(_("no changes found\n"))
1562 1368 return
1563 1369
1564 1370 cleanup = None
1565 1371 try:
1566 1372 fname = opts["bundle"]
1567 1373 if fname or not other.local():
1568 1374 # create a bundle (uncompressed if other repo is not local)
1569 1375 cg = other.changegroup(incoming, "incoming")
1570 1376 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1571 1377 # keep written bundle?
1572 1378 if opts["bundle"]:
1573 1379 cleanup = None
1574 1380 if not other.local():
1575 1381 # use the created uncompressed bundlerepo
1576 1382 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1577 1383
1578 1384 revs = None
1579 1385 if opts['rev']:
1580 1386 revs = [other.lookup(rev) for rev in opts['rev']]
1581 1387 o = other.changelog.nodesbetween(incoming, revs)[0]
1582 1388 if opts['newest_first']:
1583 1389 o.reverse()
1584 1390 displayer = cmdutil.show_changeset(ui, other, opts)
1585 1391 for n in o:
1586 1392 parents = [p for p in other.changelog.parents(n) if p != nullid]
1587 1393 if opts['no_merges'] and len(parents) == 2:
1588 1394 continue
1589 1395 displayer.show(changenode=n)
1590 1396 finally:
1591 1397 if hasattr(other, 'close'):
1592 1398 other.close()
1593 1399 if cleanup:
1594 1400 os.unlink(cleanup)
1595 1401
1596 1402 def init(ui, dest=".", **opts):
1597 1403 """create a new repository in the given directory
1598 1404
1599 1405 Initialize a new repository in the given directory. If the given
1600 1406 directory does not exist, it is created.
1601 1407
1602 1408 If no directory is given, the current directory is used.
1603 1409
1604 1410 It is possible to specify an ssh:// URL as the destination.
1605 1411 Look at the help text for the pull command for important details
1606 1412 about ssh:// URLs.
1607 1413 """
1608 1414 setremoteconfig(ui, opts)
1609 1415 hg.repository(ui, dest, create=1)
1610 1416
1611 1417 def locate(ui, repo, *pats, **opts):
1612 1418 """locate files matching specific patterns
1613 1419
1614 1420 Print all files under Mercurial control whose names match the
1615 1421 given patterns.
1616 1422
1617 1423 This command searches the current directory and its
1618 1424 subdirectories. To search an entire repository, move to the root
1619 1425 of the repository.
1620 1426
1621 1427 If no patterns are given to match, this command prints all file
1622 1428 names.
1623 1429
1624 1430 If you want to feed the output of this command into the "xargs"
1625 1431 command, use the "-0" option to both this command and "xargs".
1626 1432 This will avoid the problem of "xargs" treating single filenames
1627 1433 that contain white space as multiple filenames.
1628 1434 """
1629 1435 end = opts['print0'] and '\0' or '\n'
1630 1436 rev = opts['rev']
1631 1437 if rev:
1632 1438 node = repo.lookup(rev)
1633 1439 else:
1634 1440 node = None
1635 1441
1636 1442 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1637 1443 head='(?:.*/|)'):
1638 1444 if not node and repo.dirstate.state(abs) == '?':
1639 1445 continue
1640 1446 if opts['fullpath']:
1641 1447 ui.write(os.path.join(repo.root, abs), end)
1642 1448 else:
1643 1449 ui.write(((pats and rel) or abs), end)
1644 1450
1645 1451 def log(ui, repo, *pats, **opts):
1646 1452 """show revision history of entire repository or files
1647 1453
1648 1454 Print the revision history of the specified files or the entire
1649 1455 project.
1650 1456
1651 1457 File history is shown without following rename or copy history of
1652 1458 files. Use -f/--follow with a file name to follow history across
1653 1459 renames and copies. --follow without a file name will only show
1654 1460 ancestors or descendants of the starting revision. --follow-first
1655 1461 only follows the first parent of merge revisions.
1656 1462
1657 1463 If no revision range is specified, the default is tip:0 unless
1658 1464 --follow is set, in which case the working directory parent is
1659 1465 used as the starting revision.
1660 1466
1661 1467 By default this command outputs: changeset id and hash, tags,
1662 1468 non-trivial parents, user, date and time, and a summary for each
1663 1469 commit. When the -v/--verbose switch is used, the list of changed
1664 1470 files and full commit message is shown.
1665 1471 """
1666 1472
1667 getchange = util.cachefunc(lambda r:repo.changectx(r).changeset())
1668 changeiter, matchfn = walkchangerevs(ui, repo, pats, getchange, opts)
1473 get = util.cachefunc(lambda r:repo.changectx(r).changeset())
1474 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1669 1475
1670 1476 if opts['limit']:
1671 1477 try:
1672 1478 limit = int(opts['limit'])
1673 1479 except ValueError:
1674 1480 raise util.Abort(_('limit must be a positive integer'))
1675 1481 if limit <= 0: raise util.Abort(_('limit must be positive'))
1676 1482 else:
1677 1483 limit = sys.maxint
1678 1484 count = 0
1679 1485
1680 1486 if opts['copies'] and opts['rev']:
1681 1487 endrev = max(cmdutil.revrange(ui, repo, opts['rev'])) + 1
1682 1488 else:
1683 1489 endrev = repo.changelog.count()
1684 1490 rcache = {}
1685 1491 ncache = {}
1686 1492 dcache = []
1687 1493 def getrenamed(fn, rev, man):
1688 1494 '''looks up all renames for a file (up to endrev) the first
1689 1495 time the file is given. It indexes on the changerev and only
1690 1496 parses the manifest if linkrev != changerev.
1691 1497 Returns rename info for fn at changerev rev.'''
1692 1498 if fn not in rcache:
1693 1499 rcache[fn] = {}
1694 1500 ncache[fn] = {}
1695 1501 fl = repo.file(fn)
1696 1502 for i in xrange(fl.count()):
1697 1503 node = fl.node(i)
1698 1504 lr = fl.linkrev(node)
1699 1505 renamed = fl.renamed(node)
1700 1506 rcache[fn][lr] = renamed
1701 1507 if renamed:
1702 1508 ncache[fn][node] = renamed
1703 1509 if lr >= endrev:
1704 1510 break
1705 1511 if rev in rcache[fn]:
1706 1512 return rcache[fn][rev]
1707 1513 mr = repo.manifest.rev(man)
1708 1514 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1709 1515 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1710 1516 if not dcache or dcache[0] != man:
1711 1517 dcache[:] = [man, repo.manifest.readdelta(man)]
1712 1518 if fn in dcache[1]:
1713 1519 return ncache[fn].get(dcache[1][fn])
1714 1520 return None
1715 1521
1716 1522 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
1717 1523 for st, rev, fns in changeiter:
1718 1524 if st == 'add':
1719 1525 changenode = repo.changelog.node(rev)
1720 1526 parents = [p for p in repo.changelog.parentrevs(rev)
1721 1527 if p != nullrev]
1722 1528 if opts['no_merges'] and len(parents) == 2:
1723 1529 continue
1724 1530 if opts['only_merges'] and len(parents) != 2:
1725 1531 continue
1726 1532
1727 1533 if opts['keyword']:
1728 changes = getchange(rev)
1534 changes = get(rev)
1729 1535 miss = 0
1730 1536 for k in [kw.lower() for kw in opts['keyword']]:
1731 1537 if not (k in changes[1].lower() or
1732 1538 k in changes[4].lower() or
1733 1539 k in " ".join(changes[3][:20]).lower()):
1734 1540 miss = 1
1735 1541 break
1736 1542 if miss:
1737 1543 continue
1738 1544
1739 1545 copies = []
1740 1546 if opts.get('copies') and rev:
1741 mf = getchange(rev)[0]
1742 for fn in getchange(rev)[3]:
1547 mf = get(rev)[0]
1548 for fn in get(rev)[3]:
1743 1549 rename = getrenamed(fn, rev, mf)
1744 1550 if rename:
1745 1551 copies.append((fn, rename[0]))
1746 1552 displayer.show(rev, changenode, copies=copies)
1747 1553 elif st == 'iter':
1748 1554 if count == limit: break
1749 1555 if displayer.flush(rev):
1750 1556 count += 1
1751 1557
1752 1558 def manifest(ui, repo, rev=None):
1753 1559 """output the latest or given revision of the project manifest
1754 1560
1755 1561 Print a list of version controlled files for the given revision.
1756 1562
1757 1563 The manifest is the list of files being version controlled. If no revision
1758 1564 is given then the tip is used.
1759 1565 """
1760 1566 if rev:
1761 1567 try:
1762 1568 # assume all revision numbers are for changesets
1763 1569 n = repo.lookup(rev)
1764 1570 change = repo.changelog.read(n)
1765 1571 n = change[0]
1766 1572 except hg.RepoError:
1767 1573 n = repo.manifest.lookup(rev)
1768 1574 else:
1769 1575 n = repo.manifest.tip()
1770 1576 m = repo.manifest.read(n)
1771 1577 files = m.keys()
1772 1578 files.sort()
1773 1579
1774 1580 for f in files:
1775 1581 ui.write("%40s %3s %s\n" % (hex(m[f]),
1776 1582 m.execf(f) and "755" or "644", f))
1777 1583
1778 1584 def merge(ui, repo, node=None, force=None, branch=None):
1779 1585 """Merge working directory with another revision
1780 1586
1781 1587 Merge the contents of the current working directory and the
1782 1588 requested revision. Files that changed between either parent are
1783 1589 marked as changed for the next commit and a commit must be
1784 1590 performed before any further updates are allowed.
1785 1591
1786 1592 If no revision is specified, the working directory's parent is a
1787 1593 head revision, and the repository contains exactly one other head,
1788 1594 the other head is merged with by default. Otherwise, an explicit
1789 1595 revision to merge with must be provided.
1790 1596 """
1791 1597
1792 1598 if node or branch:
1793 1599 node = _lookup(repo, node, branch)
1794 1600 else:
1795 1601 heads = repo.heads()
1796 1602 if len(heads) > 2:
1797 1603 raise util.Abort(_('repo has %d heads - '
1798 1604 'please merge with an explicit rev') %
1799 1605 len(heads))
1800 1606 if len(heads) == 1:
1801 1607 raise util.Abort(_('there is nothing to merge - '
1802 1608 'use "hg update" instead'))
1803 1609 parent = repo.dirstate.parents()[0]
1804 1610 if parent not in heads:
1805 1611 raise util.Abort(_('working dir not at a head rev - '
1806 1612 'use "hg update" or merge with an explicit rev'))
1807 1613 node = parent == heads[0] and heads[-1] or heads[0]
1808 1614 return hg.merge(repo, node, force=force)
1809 1615
1810 1616 def outgoing(ui, repo, dest=None, **opts):
1811 1617 """show changesets not found in destination
1812 1618
1813 1619 Show changesets not found in the specified destination repository or
1814 1620 the default push location. These are the changesets that would be pushed
1815 1621 if a push was requested.
1816 1622
1817 1623 See pull for valid destination format details.
1818 1624 """
1819 1625 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1820 1626 setremoteconfig(ui, opts)
1821 1627 revs = None
1822 1628 if opts['rev']:
1823 1629 revs = [repo.lookup(rev) for rev in opts['rev']]
1824 1630
1825 1631 other = hg.repository(ui, dest)
1826 1632 o = repo.findoutgoing(other, force=opts['force'])
1827 1633 if not o:
1828 1634 ui.status(_("no changes found\n"))
1829 1635 return
1830 1636 o = repo.changelog.nodesbetween(o, revs)[0]
1831 1637 if opts['newest_first']:
1832 1638 o.reverse()
1833 1639 displayer = cmdutil.show_changeset(ui, repo, opts)
1834 1640 for n in o:
1835 1641 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1836 1642 if opts['no_merges'] and len(parents) == 2:
1837 1643 continue
1838 1644 displayer.show(changenode=n)
1839 1645
1840 1646 def parents(ui, repo, file_=None, rev=None, **opts):
1841 1647 """show the parents of the working dir or revision
1842 1648
1843 1649 Print the working directory's parent revisions.
1844 1650 """
1845 1651 # legacy
1846 1652 if file_ and not rev:
1847 1653 try:
1848 1654 rev = repo.lookup(file_)
1849 1655 file_ = None
1850 1656 except hg.RepoError:
1851 1657 pass
1852 1658 else:
1853 1659 ui.warn(_("'hg parent REV' is deprecated, "
1854 1660 "please use 'hg parents -r REV instead\n"))
1855 1661
1856 1662 if rev:
1857 1663 if file_:
1858 1664 ctx = repo.filectx(file_, changeid=rev)
1859 1665 else:
1860 1666 ctx = repo.changectx(rev)
1861 1667 p = [cp.node() for cp in ctx.parents()]
1862 1668 else:
1863 1669 p = repo.dirstate.parents()
1864 1670
1865 1671 displayer = cmdutil.show_changeset(ui, repo, opts)
1866 1672 for n in p:
1867 1673 if n != nullid:
1868 1674 displayer.show(changenode=n)
1869 1675
1870 1676 def paths(ui, repo, search=None):
1871 1677 """show definition of symbolic path names
1872 1678
1873 1679 Show definition of symbolic path name NAME. If no name is given, show
1874 1680 definition of available names.
1875 1681
1876 1682 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1877 1683 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1878 1684 """
1879 1685 if search:
1880 1686 for name, path in ui.configitems("paths"):
1881 1687 if name == search:
1882 1688 ui.write("%s\n" % path)
1883 1689 return
1884 1690 ui.warn(_("not found!\n"))
1885 1691 return 1
1886 1692 else:
1887 1693 for name, path in ui.configitems("paths"):
1888 1694 ui.write("%s = %s\n" % (name, path))
1889 1695
1890 1696 def postincoming(ui, repo, modheads, optupdate):
1891 1697 if modheads == 0:
1892 1698 return
1893 1699 if optupdate:
1894 1700 if modheads == 1:
1895 1701 return hg.update(repo, repo.changelog.tip()) # update
1896 1702 else:
1897 1703 ui.status(_("not updating, since new heads added\n"))
1898 1704 if modheads > 1:
1899 1705 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1900 1706 else:
1901 1707 ui.status(_("(run 'hg update' to get a working copy)\n"))
1902 1708
1903 1709 def pull(ui, repo, source="default", **opts):
1904 1710 """pull changes from the specified source
1905 1711
1906 1712 Pull changes from a remote repository to a local one.
1907 1713
1908 1714 This finds all changes from the repository at the specified path
1909 1715 or URL and adds them to the local repository. By default, this
1910 1716 does not update the copy of the project in the working directory.
1911 1717
1912 1718 Valid URLs are of the form:
1913 1719
1914 1720 local/filesystem/path (or file://local/filesystem/path)
1915 1721 http://[user@]host[:port]/[path]
1916 1722 https://[user@]host[:port]/[path]
1917 1723 ssh://[user@]host[:port]/[path]
1918 1724 static-http://host[:port]/[path]
1919 1725
1920 1726 Paths in the local filesystem can either point to Mercurial
1921 1727 repositories or to bundle files (as created by 'hg bundle' or
1922 1728 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1923 1729 allows access to a Mercurial repository where you simply use a web
1924 1730 server to publish the .hg directory as static content.
1925 1731
1926 1732 Some notes about using SSH with Mercurial:
1927 1733 - SSH requires an accessible shell account on the destination machine
1928 1734 and a copy of hg in the remote path or specified with as remotecmd.
1929 1735 - path is relative to the remote user's home directory by default.
1930 1736 Use an extra slash at the start of a path to specify an absolute path:
1931 1737 ssh://example.com//tmp/repository
1932 1738 - Mercurial doesn't use its own compression via SSH; the right thing
1933 1739 to do is to configure it in your ~/.ssh/config, e.g.:
1934 1740 Host *.mylocalnetwork.example.com
1935 1741 Compression no
1936 1742 Host *
1937 1743 Compression yes
1938 1744 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1939 1745 with the --ssh command line option.
1940 1746 """
1941 1747 source = ui.expandpath(source)
1942 1748 setremoteconfig(ui, opts)
1943 1749
1944 1750 other = hg.repository(ui, source)
1945 1751 ui.status(_('pulling from %s\n') % (source))
1946 1752 revs = None
1947 1753 if opts['rev']:
1948 1754 if 'lookup' in other.capabilities:
1949 1755 revs = [other.lookup(rev) for rev in opts['rev']]
1950 1756 else:
1951 1757 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1952 1758 raise util.Abort(error)
1953 1759 modheads = repo.pull(other, heads=revs, force=opts['force'])
1954 1760 return postincoming(ui, repo, modheads, opts['update'])
1955 1761
1956 1762 def push(ui, repo, dest=None, **opts):
1957 1763 """push changes to the specified destination
1958 1764
1959 1765 Push changes from the local repository to the given destination.
1960 1766
1961 1767 This is the symmetrical operation for pull. It helps to move
1962 1768 changes from the current repository to a different one. If the
1963 1769 destination is local this is identical to a pull in that directory
1964 1770 from the current one.
1965 1771
1966 1772 By default, push will refuse to run if it detects the result would
1967 1773 increase the number of remote heads. This generally indicates the
1968 1774 the client has forgotten to sync and merge before pushing.
1969 1775
1970 1776 Valid URLs are of the form:
1971 1777
1972 1778 local/filesystem/path (or file://local/filesystem/path)
1973 1779 ssh://[user@]host[:port]/[path]
1974 1780 http://[user@]host[:port]/[path]
1975 1781 https://[user@]host[:port]/[path]
1976 1782
1977 1783 Look at the help text for the pull command for important details
1978 1784 about ssh:// URLs.
1979 1785
1980 1786 Pushing to http:// and https:// URLs is only possible, if this
1981 1787 feature is explicitly enabled on the remote Mercurial server.
1982 1788 """
1983 1789 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1984 1790 setremoteconfig(ui, opts)
1985 1791
1986 1792 other = hg.repository(ui, dest)
1987 1793 ui.status('pushing to %s\n' % (dest))
1988 1794 revs = None
1989 1795 if opts['rev']:
1990 1796 revs = [repo.lookup(rev) for rev in opts['rev']]
1991 1797 r = repo.push(other, opts['force'], revs=revs)
1992 1798 return r == 0
1993 1799
1994 1800 def rawcommit(ui, repo, *flist, **rc):
1995 1801 """raw commit interface (DEPRECATED)
1996 1802
1997 1803 (DEPRECATED)
1998 1804 Lowlevel commit, for use in helper scripts.
1999 1805
2000 1806 This command is not intended to be used by normal users, as it is
2001 1807 primarily useful for importing from other SCMs.
2002 1808
2003 1809 This command is now deprecated and will be removed in a future
2004 1810 release, please use debugsetparents and commit instead.
2005 1811 """
2006 1812
2007 1813 ui.warn(_("(the rawcommit command is deprecated)\n"))
2008 1814
2009 1815 message = rc['message']
2010 1816 if not message and rc['logfile']:
2011 1817 try:
2012 1818 message = open(rc['logfile']).read()
2013 1819 except IOError:
2014 1820 pass
2015 1821 if not message and not rc['logfile']:
2016 1822 raise util.Abort(_("missing commit message"))
2017 1823
2018 1824 files = relpath(repo, list(flist))
2019 1825 if rc['files']:
2020 1826 files += open(rc['files']).read().splitlines()
2021 1827
2022 1828 rc['parent'] = map(repo.lookup, rc['parent'])
2023 1829
2024 1830 try:
2025 1831 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2026 1832 except ValueError, inst:
2027 1833 raise util.Abort(str(inst))
2028 1834
2029 1835 def recover(ui, repo):
2030 1836 """roll back an interrupted transaction
2031 1837
2032 1838 Recover from an interrupted commit or pull.
2033 1839
2034 1840 This command tries to fix the repository status after an interrupted
2035 1841 operation. It should only be necessary when Mercurial suggests it.
2036 1842 """
2037 1843 if repo.recover():
2038 1844 return hg.verify(repo)
2039 1845 return 1
2040 1846
2041 1847 def remove(ui, repo, *pats, **opts):
2042 1848 """remove the specified files on the next commit
2043 1849
2044 1850 Schedule the indicated files for removal from the repository.
2045 1851
2046 1852 This command schedules the files to be removed at the next commit.
2047 1853 This only removes files from the current branch, not from the
2048 1854 entire project history. If the files still exist in the working
2049 1855 directory, they will be deleted from it. If invoked with --after,
2050 1856 files that have been manually deleted are marked as removed.
2051 1857
2052 1858 Modified files and added files are not removed by default. To
2053 1859 remove them, use the -f/--force option.
2054 1860 """
2055 1861 names = []
2056 1862 if not opts['after'] and not pats:
2057 1863 raise util.Abort(_('no files specified'))
2058 1864 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2059 1865 exact = dict.fromkeys(files)
2060 1866 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2061 1867 modified, added, removed, deleted, unknown = mardu
2062 1868 remove, forget = [], []
2063 1869 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2064 1870 reason = None
2065 1871 if abs not in deleted and opts['after']:
2066 1872 reason = _('is still present')
2067 1873 elif abs in modified and not opts['force']:
2068 1874 reason = _('is modified (use -f to force removal)')
2069 1875 elif abs in added:
2070 1876 if opts['force']:
2071 1877 forget.append(abs)
2072 1878 continue
2073 1879 reason = _('has been marked for add (use -f to force removal)')
2074 1880 elif abs in unknown:
2075 1881 reason = _('is not managed')
2076 1882 elif abs in removed:
2077 1883 continue
2078 1884 if reason:
2079 1885 if exact:
2080 1886 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2081 1887 else:
2082 1888 if ui.verbose or not exact:
2083 1889 ui.status(_('removing %s\n') % rel)
2084 1890 remove.append(abs)
2085 1891 repo.forget(forget)
2086 1892 repo.remove(remove, unlink=not opts['after'])
2087 1893
2088 1894 def rename(ui, repo, *pats, **opts):
2089 1895 """rename files; equivalent of copy + remove
2090 1896
2091 1897 Mark dest as copies of sources; mark sources for deletion. If
2092 1898 dest is a directory, copies are put in that directory. If dest is
2093 1899 a file, there can only be one source.
2094 1900
2095 1901 By default, this command copies the contents of files as they
2096 1902 stand in the working directory. If invoked with --after, the
2097 1903 operation is recorded, but no copying is performed.
2098 1904
2099 1905 This command takes effect in the next commit.
2100 1906
2101 1907 NOTE: This command should be treated as experimental. While it
2102 1908 should properly record rename files, this information is not yet
2103 1909 fully used by merge, nor fully reported by log.
2104 1910 """
2105 1911 wlock = repo.wlock(0)
2106 1912 errs, copied = docopy(ui, repo, pats, opts, wlock)
2107 1913 names = []
2108 1914 for abs, rel, exact in copied:
2109 1915 if ui.verbose or not exact:
2110 1916 ui.status(_('removing %s\n') % rel)
2111 1917 names.append(abs)
2112 1918 if not opts.get('dry_run'):
2113 1919 repo.remove(names, True, wlock)
2114 1920 return errs
2115 1921
2116 1922 def revert(ui, repo, *pats, **opts):
2117 1923 """revert files or dirs to their states as of some revision
2118 1924
2119 1925 With no revision specified, revert the named files or directories
2120 1926 to the contents they had in the parent of the working directory.
2121 1927 This restores the contents of the affected files to an unmodified
2122 1928 state. If the working directory has two parents, you must
2123 1929 explicitly specify the revision to revert to.
2124 1930
2125 1931 Modified files are saved with a .orig suffix before reverting.
2126 1932 To disable these backups, use --no-backup.
2127 1933
2128 1934 Using the -r option, revert the given files or directories to their
2129 1935 contents as of a specific revision. This can be helpful to "roll
2130 1936 back" some or all of a change that should not have been committed.
2131 1937
2132 1938 Revert modifies the working directory. It does not commit any
2133 1939 changes, or change the parent of the working directory. If you
2134 1940 revert to a revision other than the parent of the working
2135 1941 directory, the reverted files will thus appear modified
2136 1942 afterwards.
2137 1943
2138 1944 If a file has been deleted, it is recreated. If the executable
2139 1945 mode of a file was changed, it is reset.
2140 1946
2141 1947 If names are given, all files matching the names are reverted.
2142 1948
2143 1949 If no arguments are given, no files are reverted.
2144 1950 """
2145 1951
2146 1952 if not pats and not opts['all']:
2147 1953 raise util.Abort(_('no files or directories specified; '
2148 1954 'use --all to revert the whole repo'))
2149 1955
2150 1956 parent, p2 = repo.dirstate.parents()
2151 1957 if not opts['rev'] and p2 != nullid:
2152 1958 raise util.Abort(_('uncommitted merge - please provide a '
2153 1959 'specific revision'))
2154 1960 node = repo.changectx(opts['rev']).node()
2155 1961 mf = repo.manifest.read(repo.changelog.read(node)[0])
2156 1962 if node == parent:
2157 1963 pmf = mf
2158 1964 else:
2159 1965 pmf = None
2160 1966
2161 1967 wlock = repo.wlock()
2162 1968
2163 1969 # need all matching names in dirstate and manifest of target rev,
2164 1970 # so have to walk both. do not print errors if files exist in one
2165 1971 # but not other.
2166 1972
2167 1973 names = {}
2168 1974 target_only = {}
2169 1975
2170 1976 # walk dirstate.
2171 1977
2172 1978 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2173 1979 badmatch=mf.has_key):
2174 1980 names[abs] = (rel, exact)
2175 1981 if src == 'b':
2176 1982 target_only[abs] = True
2177 1983
2178 1984 # walk target manifest.
2179 1985
2180 1986 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2181 1987 badmatch=names.has_key):
2182 1988 if abs in names: continue
2183 1989 names[abs] = (rel, exact)
2184 1990 target_only[abs] = True
2185 1991
2186 1992 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2187 1993 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2188 1994
2189 1995 revert = ([], _('reverting %s\n'))
2190 1996 add = ([], _('adding %s\n'))
2191 1997 remove = ([], _('removing %s\n'))
2192 1998 forget = ([], _('forgetting %s\n'))
2193 1999 undelete = ([], _('undeleting %s\n'))
2194 2000 update = {}
2195 2001
2196 2002 disptable = (
2197 2003 # dispatch table:
2198 2004 # file state
2199 2005 # action if in target manifest
2200 2006 # action if not in target manifest
2201 2007 # make backup if in target manifest
2202 2008 # make backup if not in target manifest
2203 2009 (modified, revert, remove, True, True),
2204 2010 (added, revert, forget, True, False),
2205 2011 (removed, undelete, None, False, False),
2206 2012 (deleted, revert, remove, False, False),
2207 2013 (unknown, add, None, True, False),
2208 2014 (target_only, add, None, False, False),
2209 2015 )
2210 2016
2211 2017 entries = names.items()
2212 2018 entries.sort()
2213 2019
2214 2020 for abs, (rel, exact) in entries:
2215 2021 mfentry = mf.get(abs)
2216 2022 def handle(xlist, dobackup):
2217 2023 xlist[0].append(abs)
2218 2024 update[abs] = 1
2219 2025 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2220 2026 bakname = "%s.orig" % rel
2221 2027 ui.note(_('saving current version of %s as %s\n') %
2222 2028 (rel, bakname))
2223 2029 if not opts.get('dry_run'):
2224 2030 util.copyfile(rel, bakname)
2225 2031 if ui.verbose or not exact:
2226 2032 ui.status(xlist[1] % rel)
2227 2033 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2228 2034 if abs not in table: continue
2229 2035 # file has changed in dirstate
2230 2036 if mfentry:
2231 2037 handle(hitlist, backuphit)
2232 2038 elif misslist is not None:
2233 2039 handle(misslist, backupmiss)
2234 2040 else:
2235 2041 if exact: ui.warn(_('file not managed: %s\n' % rel))
2236 2042 break
2237 2043 else:
2238 2044 # file has not changed in dirstate
2239 2045 if node == parent:
2240 2046 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2241 2047 continue
2242 2048 if pmf is None:
2243 2049 # only need parent manifest in this unlikely case,
2244 2050 # so do not read by default
2245 2051 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2246 2052 if abs in pmf:
2247 2053 if mfentry:
2248 2054 # if version of file is same in parent and target
2249 2055 # manifests, do nothing
2250 2056 if pmf[abs] != mfentry:
2251 2057 handle(revert, False)
2252 2058 else:
2253 2059 handle(remove, False)
2254 2060
2255 2061 if not opts.get('dry_run'):
2256 2062 repo.dirstate.forget(forget[0])
2257 2063 r = hg.revert(repo, node, update.has_key, wlock)
2258 2064 repo.dirstate.update(add[0], 'a')
2259 2065 repo.dirstate.update(undelete[0], 'n')
2260 2066 repo.dirstate.update(remove[0], 'r')
2261 2067 return r
2262 2068
2263 2069 def rollback(ui, repo):
2264 2070 """roll back the last transaction in this repository
2265 2071
2266 2072 Roll back the last transaction in this repository, restoring the
2267 2073 project to its state prior to the transaction.
2268 2074
2269 2075 Transactions are used to encapsulate the effects of all commands
2270 2076 that create new changesets or propagate existing changesets into a
2271 2077 repository. For example, the following commands are transactional,
2272 2078 and their effects can be rolled back:
2273 2079
2274 2080 commit
2275 2081 import
2276 2082 pull
2277 2083 push (with this repository as destination)
2278 2084 unbundle
2279 2085
2280 2086 This command should be used with care. There is only one level of
2281 2087 rollback, and there is no way to undo a rollback.
2282 2088
2283 2089 This command is not intended for use on public repositories. Once
2284 2090 changes are visible for pull by other users, rolling a transaction
2285 2091 back locally is ineffective (someone else may already have pulled
2286 2092 the changes). Furthermore, a race is possible with readers of the
2287 2093 repository; for example an in-progress pull from the repository
2288 2094 may fail if a rollback is performed.
2289 2095 """
2290 2096 repo.rollback()
2291 2097
2292 2098 def root(ui, repo):
2293 2099 """print the root (top) of the current working dir
2294 2100
2295 2101 Print the root directory of the current repository.
2296 2102 """
2297 2103 ui.write(repo.root + "\n")
2298 2104
2299 2105 def serve(ui, repo, **opts):
2300 2106 """export the repository via HTTP
2301 2107
2302 2108 Start a local HTTP repository browser and pull server.
2303 2109
2304 2110 By default, the server logs accesses to stdout and errors to
2305 2111 stderr. Use the "-A" and "-E" options to log to files.
2306 2112 """
2307 2113
2308 2114 if opts["stdio"]:
2309 2115 if repo is None:
2310 2116 raise hg.RepoError(_("There is no Mercurial repository here"
2311 2117 " (.hg not found)"))
2312 2118 s = sshserver.sshserver(ui, repo)
2313 2119 s.serve_forever()
2314 2120
2315 2121 optlist = ("name templates style address port ipv6"
2316 2122 " accesslog errorlog webdir_conf")
2317 2123 for o in optlist.split():
2318 2124 if opts[o]:
2319 2125 ui.setconfig("web", o, str(opts[o]))
2320 2126
2321 2127 if repo is None and not ui.config("web", "webdir_conf"):
2322 2128 raise hg.RepoError(_("There is no Mercurial repository here"
2323 2129 " (.hg not found)"))
2324 2130
2325 2131 if opts['daemon'] and not opts['daemon_pipefds']:
2326 2132 rfd, wfd = os.pipe()
2327 2133 args = sys.argv[:]
2328 2134 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2329 2135 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2330 2136 args[0], args)
2331 2137 os.close(wfd)
2332 2138 os.read(rfd, 1)
2333 2139 os._exit(0)
2334 2140
2335 2141 httpd = hgweb.server.create_server(ui, repo)
2336 2142
2337 2143 if ui.verbose:
2338 2144 if httpd.port != 80:
2339 2145 ui.status(_('listening at http://%s:%d/\n') %
2340 2146 (httpd.addr, httpd.port))
2341 2147 else:
2342 2148 ui.status(_('listening at http://%s/\n') % httpd.addr)
2343 2149
2344 2150 if opts['pid_file']:
2345 2151 fp = open(opts['pid_file'], 'w')
2346 2152 fp.write(str(os.getpid()) + '\n')
2347 2153 fp.close()
2348 2154
2349 2155 if opts['daemon_pipefds']:
2350 2156 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2351 2157 os.close(rfd)
2352 2158 os.write(wfd, 'y')
2353 2159 os.close(wfd)
2354 2160 sys.stdout.flush()
2355 2161 sys.stderr.flush()
2356 2162 fd = os.open(util.nulldev, os.O_RDWR)
2357 2163 if fd != 0: os.dup2(fd, 0)
2358 2164 if fd != 1: os.dup2(fd, 1)
2359 2165 if fd != 2: os.dup2(fd, 2)
2360 2166 if fd not in (0, 1, 2): os.close(fd)
2361 2167
2362 2168 httpd.serve_forever()
2363 2169
2364 2170 def status(ui, repo, *pats, **opts):
2365 2171 """show changed files in the working directory
2366 2172
2367 2173 Show status of files in the repository. If names are given, only
2368 2174 files that match are shown. Files that are clean or ignored, are
2369 2175 not listed unless -c (clean), -i (ignored) or -A is given.
2370 2176
2371 2177 If one revision is given, it is used as the base revision.
2372 2178 If two revisions are given, the difference between them is shown.
2373 2179
2374 2180 The codes used to show the status of files are:
2375 2181 M = modified
2376 2182 A = added
2377 2183 R = removed
2378 2184 C = clean
2379 2185 ! = deleted, but still tracked
2380 2186 ? = not tracked
2381 2187 I = ignored (not shown by default)
2382 2188 = the previous added file was copied from here
2383 2189 """
2384 2190
2385 2191 all = opts['all']
2386 2192 node1, node2 = cmdutil.revpair(ui, repo, opts.get('rev'))
2387 2193
2388 2194 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2389 2195 cwd = (pats and repo.getcwd()) or ''
2390 2196 modified, added, removed, deleted, unknown, ignored, clean = [
2391 2197 [util.pathto(cwd, x) for x in n]
2392 2198 for n in repo.status(node1=node1, node2=node2, files=files,
2393 2199 match=matchfn,
2394 2200 list_ignored=all or opts['ignored'],
2395 2201 list_clean=all or opts['clean'])]
2396 2202
2397 2203 changetypes = (('modified', 'M', modified),
2398 2204 ('added', 'A', added),
2399 2205 ('removed', 'R', removed),
2400 2206 ('deleted', '!', deleted),
2401 2207 ('unknown', '?', unknown),
2402 2208 ('ignored', 'I', ignored))
2403 2209
2404 2210 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2405 2211
2406 2212 end = opts['print0'] and '\0' or '\n'
2407 2213
2408 2214 for opt, char, changes in ([ct for ct in explicit_changetypes
2409 2215 if all or opts[ct[0]]]
2410 2216 or changetypes):
2411 2217 if opts['no_status']:
2412 2218 format = "%%s%s" % end
2413 2219 else:
2414 2220 format = "%s %%s%s" % (char, end)
2415 2221
2416 2222 for f in changes:
2417 2223 ui.write(format % f)
2418 2224 if ((all or opts.get('copies')) and not opts.get('no_status')):
2419 2225 copied = repo.dirstate.copied(f)
2420 2226 if copied:
2421 2227 ui.write(' %s%s' % (copied, end))
2422 2228
2423 2229 def tag(ui, repo, name, rev_=None, **opts):
2424 2230 """add a tag for the current tip or a given revision
2425 2231
2426 2232 Name a particular revision using <name>.
2427 2233
2428 2234 Tags are used to name particular revisions of the repository and are
2429 2235 very useful to compare different revision, to go back to significant
2430 2236 earlier versions or to mark branch points as releases, etc.
2431 2237
2432 2238 If no revision is given, the parent of the working directory is used.
2433 2239
2434 2240 To facilitate version control, distribution, and merging of tags,
2435 2241 they are stored as a file named ".hgtags" which is managed
2436 2242 similarly to other project files and can be hand-edited if
2437 2243 necessary. The file '.hg/localtags' is used for local tags (not
2438 2244 shared among repositories).
2439 2245 """
2440 2246 if name in ['tip', '.']:
2441 2247 raise util.Abort(_("the name '%s' is reserved") % name)
2442 2248 if rev_ is not None:
2443 2249 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2444 2250 "please use 'hg tag [-r REV] NAME' instead\n"))
2445 2251 if opts['rev']:
2446 2252 raise util.Abort(_("use only one form to specify the revision"))
2447 2253 if opts['rev']:
2448 2254 rev_ = opts['rev']
2449 2255 if not rev_ and repo.dirstate.parents()[1] != nullid:
2450 2256 raise util.Abort(_('uncommitted merge - please provide a '
2451 2257 'specific revision'))
2452 2258 r = repo.changectx(rev_).node()
2453 2259
2454 2260 message = opts['message']
2455 2261 if not message:
2456 2262 message = _('Added tag %s for changeset %s') % (name, short(r))
2457 2263
2458 2264 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2459 2265
2460 2266 def tags(ui, repo):
2461 2267 """list repository tags
2462 2268
2463 2269 List the repository tags.
2464 2270
2465 2271 This lists both regular and local tags.
2466 2272 """
2467 2273
2468 2274 l = repo.tagslist()
2469 2275 l.reverse()
2470 2276 hexfunc = ui.debugflag and hex or short
2471 2277 for t, n in l:
2472 2278 try:
2473 2279 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2474 2280 except KeyError:
2475 2281 r = " ?:?"
2476 2282 if ui.quiet:
2477 2283 ui.write("%s\n" % t)
2478 2284 else:
2479 2285 ui.write("%-30s %s\n" % (t, r))
2480 2286
2481 2287 def tip(ui, repo, **opts):
2482 2288 """show the tip revision
2483 2289
2484 2290 Show the tip revision.
2485 2291 """
2486 2292 cmdutil.show_changeset(ui, repo, opts).show(repo.changelog.count()-1)
2487 2293
2488 2294 def unbundle(ui, repo, fname, **opts):
2489 2295 """apply a changegroup file
2490 2296
2491 2297 Apply a compressed changegroup file generated by the bundle
2492 2298 command.
2493 2299 """
2494 2300 f = urllib.urlopen(fname)
2495 2301
2496 2302 header = f.read(6)
2497 2303 if not header.startswith("HG"):
2498 2304 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2499 2305 elif not header.startswith("HG10"):
2500 2306 raise util.Abort(_("%s: unknown bundle version") % fname)
2501 2307 elif header == "HG10BZ":
2502 2308 def generator(f):
2503 2309 zd = bz2.BZ2Decompressor()
2504 2310 zd.decompress("BZ")
2505 2311 for chunk in f:
2506 2312 yield zd.decompress(chunk)
2507 2313 elif header == "HG10UN":
2508 2314 def generator(f):
2509 2315 for chunk in f:
2510 2316 yield chunk
2511 2317 else:
2512 2318 raise util.Abort(_("%s: unknown bundle compression type")
2513 2319 % fname)
2514 2320 gen = generator(util.filechunkiter(f, 4096))
2515 2321 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle',
2516 2322 'bundle:' + fname)
2517 2323 return postincoming(ui, repo, modheads, opts['update'])
2518 2324
2519 2325 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2520 2326 branch=None):
2521 2327 """update or merge working directory
2522 2328
2523 2329 Update the working directory to the specified revision.
2524 2330
2525 2331 If there are no outstanding changes in the working directory and
2526 2332 there is a linear relationship between the current version and the
2527 2333 requested version, the result is the requested version.
2528 2334
2529 2335 To merge the working directory with another revision, use the
2530 2336 merge command.
2531 2337
2532 2338 By default, update will refuse to run if doing so would require
2533 2339 merging or discarding local changes.
2534 2340 """
2535 2341 node = _lookup(repo, node, branch)
2536 2342 if clean:
2537 2343 return hg.clean(repo, node)
2538 2344 else:
2539 2345 return hg.update(repo, node)
2540 2346
2541 2347 def _lookup(repo, node, branch=None):
2542 2348 if branch:
2543 2349 repo.ui.warn(_("the --branch option is deprecated, "
2544 2350 "please use 'hg branch' instead\n"))
2545 2351 br = repo.branchlookup(branch=branch)
2546 2352 found = []
2547 2353 for x in br:
2548 2354 if branch in br[x]:
2549 2355 found.append(x)
2550 2356 if len(found) > 1:
2551 2357 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2552 2358 for x in found:
2553 2359 cmdutil.show_changeset(ui, repo, {}).show(changenode=x)
2554 2360 raise util.Abort("")
2555 2361 if len(found) == 1:
2556 2362 node = found[0]
2557 2363 repo.ui.warn(_("Using head %s for branch %s\n")
2558 2364 % (short(node), branch))
2559 2365 else:
2560 2366 raise util.Abort(_("branch %s not found") % branch)
2561 2367 else:
2562 2368 node = node and repo.lookup(node) or repo.changelog.tip()
2563 2369 return node
2564 2370
2565 2371 def verify(ui, repo):
2566 2372 """verify the integrity of the repository
2567 2373
2568 2374 Verify the integrity of the current repository.
2569 2375
2570 2376 This will perform an extensive check of the repository's
2571 2377 integrity, validating the hashes and checksums of each entry in
2572 2378 the changelog, manifest, and tracked files, as well as the
2573 2379 integrity of their crosslinks and indices.
2574 2380 """
2575 2381 return hg.verify(repo)
2576 2382
2577 2383 # Command options and aliases are listed here, alphabetically
2578 2384
2579 2385 globalopts = [
2580 2386 ('R', 'repository', '',
2581 2387 _('repository root directory or symbolic path name')),
2582 2388 ('', 'cwd', '', _('change working directory')),
2583 2389 ('y', 'noninteractive', None,
2584 2390 _('do not prompt, assume \'yes\' for any required answers')),
2585 2391 ('q', 'quiet', None, _('suppress output')),
2586 2392 ('v', 'verbose', None, _('enable additional output')),
2587 2393 ('', 'config', [], _('set/override config option')),
2588 2394 ('', 'debug', None, _('enable debugging output')),
2589 2395 ('', 'debugger', None, _('start debugger')),
2590 2396 ('', 'lsprof', None, _('print improved command execution profile')),
2591 2397 ('', 'traceback', None, _('print traceback on exception')),
2592 2398 ('', 'time', None, _('time how long the command takes')),
2593 2399 ('', 'profile', None, _('print command execution profile')),
2594 2400 ('', 'version', None, _('output version information and exit')),
2595 2401 ('h', 'help', None, _('display help and exit')),
2596 2402 ]
2597 2403
2598 2404 dryrunopts = [('n', 'dry-run', None,
2599 2405 _('do not perform actions, just print output'))]
2600 2406
2601 2407 remoteopts = [
2602 2408 ('e', 'ssh', '', _('specify ssh command to use')),
2603 2409 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2604 2410 ]
2605 2411
2606 2412 walkopts = [
2607 2413 ('I', 'include', [], _('include names matching the given patterns')),
2608 2414 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2609 2415 ]
2610 2416
2611 2417 table = {
2612 2418 "^add":
2613 2419 (add,
2614 2420 walkopts + dryrunopts,
2615 2421 _('hg add [OPTION]... [FILE]...')),
2616 2422 "addremove":
2617 2423 (addremove,
2618 2424 [('s', 'similarity', '',
2619 2425 _('guess renamed files by similarity (0<=s<=100)')),
2620 2426 ] + walkopts + dryrunopts,
2621 2427 _('hg addremove [OPTION]... [FILE]...')),
2622 2428 "^annotate":
2623 2429 (annotate,
2624 2430 [('r', 'rev', '', _('annotate the specified revision')),
2625 2431 ('f', 'follow', None, _('follow file copies and renames')),
2626 2432 ('a', 'text', None, _('treat all files as text')),
2627 2433 ('u', 'user', None, _('list the author')),
2628 2434 ('d', 'date', None, _('list the date')),
2629 2435 ('n', 'number', None, _('list the revision number (default)')),
2630 2436 ('c', 'changeset', None, _('list the changeset')),
2631 2437 ] + walkopts,
2632 2438 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2633 2439 "archive":
2634 2440 (archive,
2635 2441 [('', 'no-decode', None, _('do not pass files through decoders')),
2636 2442 ('p', 'prefix', '', _('directory prefix for files in archive')),
2637 2443 ('r', 'rev', '', _('revision to distribute')),
2638 2444 ('t', 'type', '', _('type of distribution to create')),
2639 2445 ] + walkopts,
2640 2446 _('hg archive [OPTION]... DEST')),
2641 2447 "backout":
2642 2448 (backout,
2643 2449 [('', 'merge', None,
2644 2450 _('merge with old dirstate parent after backout')),
2645 2451 ('m', 'message', '', _('use <text> as commit message')),
2646 2452 ('l', 'logfile', '', _('read commit message from <file>')),
2647 2453 ('d', 'date', '', _('record datecode as commit date')),
2648 2454 ('', 'parent', '', _('parent to choose when backing out merge')),
2649 2455 ('u', 'user', '', _('record user as committer')),
2650 2456 ] + walkopts,
2651 2457 _('hg backout [OPTION]... REV')),
2652 2458 "branch": (branch, [], _('hg branch [NAME]')),
2653 2459 "branches": (branches, [], _('hg branches')),
2654 2460 "bundle":
2655 2461 (bundle,
2656 2462 [('f', 'force', None,
2657 2463 _('run even when remote repository is unrelated')),
2658 2464 ('r', 'rev', [],
2659 2465 _('a changeset you would like to bundle')),
2660 2466 ('', 'base', [],
2661 2467 _('a base changeset to specify instead of a destination')),
2662 2468 ] + remoteopts,
2663 2469 _('hg bundle [--base REV]... [--rev REV]... FILE [DEST]')),
2664 2470 "cat":
2665 2471 (cat,
2666 2472 [('o', 'output', '', _('print output to file with formatted name')),
2667 2473 ('r', 'rev', '', _('print the given revision')),
2668 2474 ] + walkopts,
2669 2475 _('hg cat [OPTION]... FILE...')),
2670 2476 "^clone":
2671 2477 (clone,
2672 2478 [('U', 'noupdate', None, _('do not update the new working directory')),
2673 2479 ('r', 'rev', [],
2674 2480 _('a changeset you would like to have after cloning')),
2675 2481 ('', 'pull', None, _('use pull protocol to copy metadata')),
2676 2482 ('', 'uncompressed', None,
2677 2483 _('use uncompressed transfer (fast over LAN)')),
2678 2484 ] + remoteopts,
2679 2485 _('hg clone [OPTION]... SOURCE [DEST]')),
2680 2486 "^commit|ci":
2681 2487 (commit,
2682 2488 [('A', 'addremove', None,
2683 2489 _('mark new/missing files as added/removed before committing')),
2684 2490 ('m', 'message', '', _('use <text> as commit message')),
2685 2491 ('l', 'logfile', '', _('read the commit message from <file>')),
2686 2492 ('d', 'date', '', _('record datecode as commit date')),
2687 2493 ('u', 'user', '', _('record user as commiter')),
2688 2494 ] + walkopts,
2689 2495 _('hg commit [OPTION]... [FILE]...')),
2690 2496 "copy|cp":
2691 2497 (copy,
2692 2498 [('A', 'after', None, _('record a copy that has already occurred')),
2693 2499 ('f', 'force', None,
2694 2500 _('forcibly copy over an existing managed file')),
2695 2501 ] + walkopts + dryrunopts,
2696 2502 _('hg copy [OPTION]... [SOURCE]... DEST')),
2697 2503 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2698 2504 "debugcomplete":
2699 2505 (debugcomplete,
2700 2506 [('o', 'options', None, _('show the command options'))],
2701 2507 _('debugcomplete [-o] CMD')),
2702 2508 "debugrebuildstate":
2703 2509 (debugrebuildstate,
2704 2510 [('r', 'rev', '', _('revision to rebuild to'))],
2705 2511 _('debugrebuildstate [-r REV] [REV]')),
2706 2512 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2707 2513 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2708 2514 "debugstate": (debugstate, [], _('debugstate')),
2709 2515 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2710 2516 "debugindex": (debugindex, [], _('debugindex FILE')),
2711 2517 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2712 2518 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2713 2519 "debugwalk":
2714 2520 (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2715 2521 "^diff":
2716 2522 (diff,
2717 2523 [('r', 'rev', [], _('revision')),
2718 2524 ('a', 'text', None, _('treat all files as text')),
2719 2525 ('p', 'show-function', None,
2720 2526 _('show which function each change is in')),
2721 2527 ('g', 'git', None, _('use git extended diff format')),
2722 2528 ('', 'nodates', None, _("don't include dates in diff headers")),
2723 2529 ('w', 'ignore-all-space', None,
2724 2530 _('ignore white space when comparing lines')),
2725 2531 ('b', 'ignore-space-change', None,
2726 2532 _('ignore changes in the amount of white space')),
2727 2533 ('B', 'ignore-blank-lines', None,
2728 2534 _('ignore changes whose lines are all blank')),
2729 2535 ] + walkopts,
2730 2536 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2731 2537 "^export":
2732 2538 (export,
2733 2539 [('o', 'output', '', _('print output to file with formatted name')),
2734 2540 ('a', 'text', None, _('treat all files as text')),
2735 2541 ('g', 'git', None, _('use git extended diff format')),
2736 2542 ('', 'nodates', None, _("don't include dates in diff headers")),
2737 2543 ('', 'switch-parent', None, _('diff against the second parent'))],
2738 2544 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2739 2545 "grep":
2740 2546 (grep,
2741 2547 [('0', 'print0', None, _('end fields with NUL')),
2742 2548 ('', 'all', None, _('print all revisions that match')),
2743 2549 ('f', 'follow', None,
2744 2550 _('follow changeset history, or file history across copies and renames')),
2745 2551 ('i', 'ignore-case', None, _('ignore case when matching')),
2746 2552 ('l', 'files-with-matches', None,
2747 2553 _('print only filenames and revs that match')),
2748 2554 ('n', 'line-number', None, _('print matching line numbers')),
2749 2555 ('r', 'rev', [], _('search in given revision range')),
2750 2556 ('u', 'user', None, _('print user who committed change')),
2751 2557 ] + walkopts,
2752 2558 _('hg grep [OPTION]... PATTERN [FILE]...')),
2753 2559 "heads":
2754 2560 (heads,
2755 2561 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2756 2562 ('', 'style', '', _('display using template map file')),
2757 2563 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2758 2564 ('', 'template', '', _('display with template'))],
2759 2565 _('hg heads [-r REV]')),
2760 2566 "help": (help_, [], _('hg help [COMMAND]')),
2761 2567 "identify|id": (identify, [], _('hg identify')),
2762 2568 "import|patch":
2763 2569 (import_,
2764 2570 [('p', 'strip', 1,
2765 2571 _('directory strip option for patch. This has the same\n'
2766 2572 'meaning as the corresponding patch option')),
2767 2573 ('m', 'message', '', _('use <text> as commit message')),
2768 2574 ('b', 'base', '', _('base path (DEPRECATED)')),
2769 2575 ('f', 'force', None,
2770 2576 _('skip check for outstanding uncommitted changes'))],
2771 2577 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2772 2578 "incoming|in": (incoming,
2773 2579 [('M', 'no-merges', None, _('do not show merges')),
2774 2580 ('f', 'force', None,
2775 2581 _('run even when remote repository is unrelated')),
2776 2582 ('', 'style', '', _('display using template map file')),
2777 2583 ('n', 'newest-first', None, _('show newest record first')),
2778 2584 ('', 'bundle', '', _('file to store the bundles into')),
2779 2585 ('p', 'patch', None, _('show patch')),
2780 2586 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2781 2587 ('', 'template', '', _('display with template')),
2782 2588 ] + remoteopts,
2783 2589 _('hg incoming [-p] [-n] [-M] [-r REV]...'
2784 2590 ' [--bundle FILENAME] [SOURCE]')),
2785 2591 "^init":
2786 2592 (init, remoteopts, _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
2787 2593 "locate":
2788 2594 (locate,
2789 2595 [('r', 'rev', '', _('search the repository as it stood at rev')),
2790 2596 ('0', 'print0', None,
2791 2597 _('end filenames with NUL, for use with xargs')),
2792 2598 ('f', 'fullpath', None,
2793 2599 _('print complete paths from the filesystem root')),
2794 2600 ] + walkopts,
2795 2601 _('hg locate [OPTION]... [PATTERN]...')),
2796 2602 "^log|history":
2797 2603 (log,
2798 2604 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2799 2605 ('f', 'follow', None,
2800 2606 _('follow changeset history, or file history across copies and renames')),
2801 2607 ('', 'follow-first', None,
2802 2608 _('only follow the first parent of merge changesets')),
2803 2609 ('C', 'copies', None, _('show copied files')),
2804 2610 ('k', 'keyword', [], _('search for a keyword')),
2805 2611 ('l', 'limit', '', _('limit number of changes displayed')),
2806 2612 ('r', 'rev', [], _('show the specified revision or range')),
2807 2613 ('M', 'no-merges', None, _('do not show merges')),
2808 2614 ('', 'style', '', _('display using template map file')),
2809 2615 ('m', 'only-merges', None, _('show only merges')),
2810 2616 ('p', 'patch', None, _('show patch')),
2811 2617 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2812 2618 ('', 'template', '', _('display with template')),
2813 2619 ] + walkopts,
2814 2620 _('hg log [OPTION]... [FILE]')),
2815 2621 "manifest": (manifest, [], _('hg manifest [REV]')),
2816 2622 "merge":
2817 2623 (merge,
2818 2624 [('b', 'branch', '', _('merge with head of a specific branch (DEPRECATED)')),
2819 2625 ('f', 'force', None, _('force a merge with outstanding changes'))],
2820 2626 _('hg merge [-f] [REV]')),
2821 2627 "outgoing|out": (outgoing,
2822 2628 [('M', 'no-merges', None, _('do not show merges')),
2823 2629 ('f', 'force', None,
2824 2630 _('run even when remote repository is unrelated')),
2825 2631 ('p', 'patch', None, _('show patch')),
2826 2632 ('', 'style', '', _('display using template map file')),
2827 2633 ('r', 'rev', [], _('a specific revision you would like to push')),
2828 2634 ('n', 'newest-first', None, _('show newest record first')),
2829 2635 ('', 'template', '', _('display with template')),
2830 2636 ] + remoteopts,
2831 2637 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
2832 2638 "^parents":
2833 2639 (parents,
2834 2640 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2835 2641 ('r', 'rev', '', _('show parents from the specified rev')),
2836 2642 ('', 'style', '', _('display using template map file')),
2837 2643 ('', 'template', '', _('display with template'))],
2838 2644 _('hg parents [-r REV] [FILE]')),
2839 2645 "paths": (paths, [], _('hg paths [NAME]')),
2840 2646 "^pull":
2841 2647 (pull,
2842 2648 [('u', 'update', None,
2843 2649 _('update to new tip if changesets were pulled')),
2844 2650 ('f', 'force', None,
2845 2651 _('run even when remote repository is unrelated')),
2846 2652 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2847 2653 ] + remoteopts,
2848 2654 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
2849 2655 "^push":
2850 2656 (push,
2851 2657 [('f', 'force', None, _('force push')),
2852 2658 ('r', 'rev', [], _('a specific revision you would like to push')),
2853 2659 ] + remoteopts,
2854 2660 _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
2855 2661 "debugrawcommit|rawcommit":
2856 2662 (rawcommit,
2857 2663 [('p', 'parent', [], _('parent')),
2858 2664 ('d', 'date', '', _('date code')),
2859 2665 ('u', 'user', '', _('user')),
2860 2666 ('F', 'files', '', _('file list')),
2861 2667 ('m', 'message', '', _('commit message')),
2862 2668 ('l', 'logfile', '', _('commit message file'))],
2863 2669 _('hg debugrawcommit [OPTION]... [FILE]...')),
2864 2670 "recover": (recover, [], _('hg recover')),
2865 2671 "^remove|rm":
2866 2672 (remove,
2867 2673 [('A', 'after', None, _('record remove that has already occurred')),
2868 2674 ('f', 'force', None, _('remove file even if modified')),
2869 2675 ] + walkopts,
2870 2676 _('hg remove [OPTION]... FILE...')),
2871 2677 "rename|mv":
2872 2678 (rename,
2873 2679 [('A', 'after', None, _('record a rename that has already occurred')),
2874 2680 ('f', 'force', None,
2875 2681 _('forcibly copy over an existing managed file')),
2876 2682 ] + walkopts + dryrunopts,
2877 2683 _('hg rename [OPTION]... SOURCE... DEST')),
2878 2684 "^revert":
2879 2685 (revert,
2880 2686 [('a', 'all', None, _('revert all changes when no arguments given')),
2881 2687 ('r', 'rev', '', _('revision to revert to')),
2882 2688 ('', 'no-backup', None, _('do not save backup copies of files')),
2883 2689 ] + walkopts + dryrunopts,
2884 2690 _('hg revert [-r REV] [NAME]...')),
2885 2691 "rollback": (rollback, [], _('hg rollback')),
2886 2692 "root": (root, [], _('hg root')),
2887 2693 "showconfig|debugconfig":
2888 2694 (showconfig,
2889 2695 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2890 2696 _('showconfig [-u] [NAME]...')),
2891 2697 "^serve":
2892 2698 (serve,
2893 2699 [('A', 'accesslog', '', _('name of access log file to write to')),
2894 2700 ('d', 'daemon', None, _('run server in background')),
2895 2701 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2896 2702 ('E', 'errorlog', '', _('name of error log file to write to')),
2897 2703 ('p', 'port', 0, _('port to use (default: 8000)')),
2898 2704 ('a', 'address', '', _('address to use')),
2899 2705 ('n', 'name', '',
2900 2706 _('name to show in web pages (default: working dir)')),
2901 2707 ('', 'webdir-conf', '', _('name of the webdir config file'
2902 2708 ' (serve more than one repo)')),
2903 2709 ('', 'pid-file', '', _('name of file to write process ID to')),
2904 2710 ('', 'stdio', None, _('for remote clients')),
2905 2711 ('t', 'templates', '', _('web templates to use')),
2906 2712 ('', 'style', '', _('template style to use')),
2907 2713 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2908 2714 _('hg serve [OPTION]...')),
2909 2715 "^status|st":
2910 2716 (status,
2911 2717 [('A', 'all', None, _('show status of all files')),
2912 2718 ('m', 'modified', None, _('show only modified files')),
2913 2719 ('a', 'added', None, _('show only added files')),
2914 2720 ('r', 'removed', None, _('show only removed files')),
2915 2721 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2916 2722 ('c', 'clean', None, _('show only files without changes')),
2917 2723 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2918 2724 ('i', 'ignored', None, _('show ignored files')),
2919 2725 ('n', 'no-status', None, _('hide status prefix')),
2920 2726 ('C', 'copies', None, _('show source of copied files')),
2921 2727 ('0', 'print0', None,
2922 2728 _('end filenames with NUL, for use with xargs')),
2923 2729 ('', 'rev', [], _('show difference from revision')),
2924 2730 ] + walkopts,
2925 2731 _('hg status [OPTION]... [FILE]...')),
2926 2732 "tag":
2927 2733 (tag,
2928 2734 [('l', 'local', None, _('make the tag local')),
2929 2735 ('m', 'message', '', _('message for tag commit log entry')),
2930 2736 ('d', 'date', '', _('record datecode as commit date')),
2931 2737 ('u', 'user', '', _('record user as commiter')),
2932 2738 ('r', 'rev', '', _('revision to tag'))],
2933 2739 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2934 2740 "tags": (tags, [], _('hg tags')),
2935 2741 "tip":
2936 2742 (tip,
2937 2743 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2938 2744 ('', 'style', '', _('display using template map file')),
2939 2745 ('p', 'patch', None, _('show patch')),
2940 2746 ('', 'template', '', _('display with template'))],
2941 2747 _('hg tip [-p]')),
2942 2748 "unbundle":
2943 2749 (unbundle,
2944 2750 [('u', 'update', None,
2945 2751 _('update to new tip if changesets were unbundled'))],
2946 2752 _('hg unbundle [-u] FILE')),
2947 2753 "^update|up|checkout|co":
2948 2754 (update,
2949 2755 [('b', 'branch', '',
2950 2756 _('checkout the head of a specific branch (DEPRECATED)')),
2951 2757 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
2952 2758 ('C', 'clean', None, _('overwrite locally modified files')),
2953 2759 ('f', 'force', None, _('force a merge with outstanding changes'))],
2954 2760 _('hg update [-C] [-f] [REV]')),
2955 2761 "verify": (verify, [], _('hg verify')),
2956 2762 "version": (show_version, [], _('hg version')),
2957 2763 }
2958 2764
2959 2765 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2960 2766 " debugindex debugindexdot")
2961 2767 optionalrepo = ("paths serve showconfig")
2962 2768
2963 2769 def findpossible(ui, cmd):
2964 2770 """
2965 2771 Return cmd -> (aliases, command table entry)
2966 2772 for each matching command.
2967 2773 Return debug commands (or their aliases) only if no normal command matches.
2968 2774 """
2969 2775 choice = {}
2970 2776 debugchoice = {}
2971 2777 for e in table.keys():
2972 2778 aliases = e.lstrip("^").split("|")
2973 2779 found = None
2974 2780 if cmd in aliases:
2975 2781 found = cmd
2976 2782 elif not ui.config("ui", "strict"):
2977 2783 for a in aliases:
2978 2784 if a.startswith(cmd):
2979 2785 found = a
2980 2786 break
2981 2787 if found is not None:
2982 2788 if aliases[0].startswith("debug") or found.startswith("debug"):
2983 2789 debugchoice[found] = (aliases, table[e])
2984 2790 else:
2985 2791 choice[found] = (aliases, table[e])
2986 2792
2987 2793 if not choice and debugchoice:
2988 2794 choice = debugchoice
2989 2795
2990 2796 return choice
2991 2797
2992 2798 def findcmd(ui, cmd):
2993 2799 """Return (aliases, command table entry) for command string."""
2994 2800 choice = findpossible(ui, cmd)
2995 2801
2996 2802 if choice.has_key(cmd):
2997 2803 return choice[cmd]
2998 2804
2999 2805 if len(choice) > 1:
3000 2806 clist = choice.keys()
3001 2807 clist.sort()
3002 2808 raise AmbiguousCommand(cmd, clist)
3003 2809
3004 2810 if choice:
3005 2811 return choice.values()[0]
3006 2812
3007 2813 raise UnknownCommand(cmd)
3008 2814
3009 2815 def catchterm(*args):
3010 2816 raise util.SignalInterrupt
3011 2817
3012 2818 def run():
3013 2819 sys.exit(dispatch(sys.argv[1:]))
3014 2820
3015 2821 class ParseError(Exception):
3016 2822 """Exception raised on errors in parsing the command line."""
3017 2823
3018 2824 def parse(ui, args):
3019 2825 options = {}
3020 2826 cmdoptions = {}
3021 2827
3022 2828 try:
3023 2829 args = fancyopts.fancyopts(args, globalopts, options)
3024 2830 except fancyopts.getopt.GetoptError, inst:
3025 2831 raise ParseError(None, inst)
3026 2832
3027 2833 if args:
3028 2834 cmd, args = args[0], args[1:]
3029 2835 aliases, i = findcmd(ui, cmd)
3030 2836 cmd = aliases[0]
3031 2837 defaults = ui.config("defaults", cmd)
3032 2838 if defaults:
3033 2839 args = shlex.split(defaults) + args
3034 2840 c = list(i[1])
3035 2841 else:
3036 2842 cmd = None
3037 2843 c = []
3038 2844
3039 2845 # combine global options into local
3040 2846 for o in globalopts:
3041 2847 c.append((o[0], o[1], options[o[1]], o[3]))
3042 2848
3043 2849 try:
3044 2850 args = fancyopts.fancyopts(args, c, cmdoptions)
3045 2851 except fancyopts.getopt.GetoptError, inst:
3046 2852 raise ParseError(cmd, inst)
3047 2853
3048 2854 # separate global options back out
3049 2855 for o in globalopts:
3050 2856 n = o[1]
3051 2857 options[n] = cmdoptions[n]
3052 2858 del cmdoptions[n]
3053 2859
3054 2860 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3055 2861
3056 2862 external = {}
3057 2863
3058 2864 def findext(name):
3059 2865 '''return module with given extension name'''
3060 2866 try:
3061 2867 return sys.modules[external[name]]
3062 2868 except KeyError:
3063 2869 for k, v in external.iteritems():
3064 2870 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3065 2871 return sys.modules[v]
3066 2872 raise KeyError(name)
3067 2873
3068 2874 def load_extensions(ui):
3069 2875 added = []
3070 2876 for ext_name, load_from_name in ui.extensions():
3071 2877 if ext_name in external:
3072 2878 continue
3073 2879 try:
3074 2880 if load_from_name:
3075 2881 # the module will be loaded in sys.modules
3076 2882 # choose an unique name so that it doesn't
3077 2883 # conflicts with other modules
3078 2884 module_name = "hgext_%s" % ext_name.replace('.', '_')
3079 2885 mod = imp.load_source(module_name, load_from_name)
3080 2886 else:
3081 2887 def importh(name):
3082 2888 mod = __import__(name)
3083 2889 components = name.split('.')
3084 2890 for comp in components[1:]:
3085 2891 mod = getattr(mod, comp)
3086 2892 return mod
3087 2893 try:
3088 2894 mod = importh("hgext.%s" % ext_name)
3089 2895 except ImportError:
3090 2896 mod = importh(ext_name)
3091 2897 external[ext_name] = mod.__name__
3092 2898 added.append((mod, ext_name))
3093 2899 except (util.SignalInterrupt, KeyboardInterrupt):
3094 2900 raise
3095 2901 except Exception, inst:
3096 2902 ui.warn(_("*** failed to import extension %s: %s\n") %
3097 2903 (ext_name, inst))
3098 2904 if ui.print_exc():
3099 2905 return 1
3100 2906
3101 2907 for mod, name in added:
3102 2908 uisetup = getattr(mod, 'uisetup', None)
3103 2909 if uisetup:
3104 2910 uisetup(ui)
3105 2911 cmdtable = getattr(mod, 'cmdtable', {})
3106 2912 for t in cmdtable:
3107 2913 if t in table:
3108 2914 ui.warn(_("module %s overrides %s\n") % (name, t))
3109 2915 table.update(cmdtable)
3110 2916
3111 2917 def parseconfig(config):
3112 2918 """parse the --config options from the command line"""
3113 2919 parsed = []
3114 2920 for cfg in config:
3115 2921 try:
3116 2922 name, value = cfg.split('=', 1)
3117 2923 section, name = name.split('.', 1)
3118 2924 if not section or not name:
3119 2925 raise IndexError
3120 2926 parsed.append((section, name, value))
3121 2927 except (IndexError, ValueError):
3122 2928 raise util.Abort(_('malformed --config option: %s') % cfg)
3123 2929 return parsed
3124 2930
3125 2931 def dispatch(args):
3126 2932 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3127 2933 num = getattr(signal, name, None)
3128 2934 if num: signal.signal(num, catchterm)
3129 2935
3130 2936 try:
3131 2937 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3132 2938 except util.Abort, inst:
3133 2939 sys.stderr.write(_("abort: %s\n") % inst)
3134 2940 return -1
3135 2941
3136 2942 load_extensions(u)
3137 2943 u.addreadhook(load_extensions)
3138 2944
3139 2945 try:
3140 2946 cmd, func, args, options, cmdoptions = parse(u, args)
3141 2947 if options["time"]:
3142 2948 def get_times():
3143 2949 t = os.times()
3144 2950 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3145 2951 t = (t[0], t[1], t[2], t[3], time.clock())
3146 2952 return t
3147 2953 s = get_times()
3148 2954 def print_time():
3149 2955 t = get_times()
3150 2956 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3151 2957 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3152 2958 atexit.register(print_time)
3153 2959
3154 2960 # enter the debugger before command execution
3155 2961 if options['debugger']:
3156 2962 pdb.set_trace()
3157 2963
3158 2964 try:
3159 2965 if options['cwd']:
3160 2966 try:
3161 2967 os.chdir(options['cwd'])
3162 2968 except OSError, inst:
3163 2969 raise util.Abort('%s: %s' %
3164 2970 (options['cwd'], inst.strerror))
3165 2971
3166 2972 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3167 2973 not options["noninteractive"], options["traceback"],
3168 2974 parseconfig(options["config"]))
3169 2975
3170 2976 path = u.expandpath(options["repository"]) or ""
3171 2977 repo = path and hg.repository(u, path=path) or None
3172 2978 if repo and not repo.local():
3173 2979 raise util.Abort(_("repository '%s' is not local") % path)
3174 2980
3175 2981 if options['help']:
3176 2982 return help_(u, cmd, options['version'])
3177 2983 elif options['version']:
3178 2984 return show_version(u)
3179 2985 elif not cmd:
3180 2986 return help_(u, 'shortlist')
3181 2987
3182 2988 if cmd not in norepo.split():
3183 2989 try:
3184 2990 if not repo:
3185 2991 repo = hg.repository(u, path=path)
3186 2992 u = repo.ui
3187 2993 for name in external.itervalues():
3188 2994 mod = sys.modules[name]
3189 2995 if hasattr(mod, 'reposetup'):
3190 2996 mod.reposetup(u, repo)
3191 2997 hg.repo_setup_hooks.append(mod.reposetup)
3192 2998 except hg.RepoError:
3193 2999 if cmd not in optionalrepo.split():
3194 3000 raise
3195 3001 d = lambda: func(u, repo, *args, **cmdoptions)
3196 3002 else:
3197 3003 d = lambda: func(u, *args, **cmdoptions)
3198 3004
3199 3005 try:
3200 3006 if options['profile']:
3201 3007 import hotshot, hotshot.stats
3202 3008 prof = hotshot.Profile("hg.prof")
3203 3009 try:
3204 3010 try:
3205 3011 return prof.runcall(d)
3206 3012 except:
3207 3013 try:
3208 3014 u.warn(_('exception raised - generating '
3209 3015 'profile anyway\n'))
3210 3016 except:
3211 3017 pass
3212 3018 raise
3213 3019 finally:
3214 3020 prof.close()
3215 3021 stats = hotshot.stats.load("hg.prof")
3216 3022 stats.strip_dirs()
3217 3023 stats.sort_stats('time', 'calls')
3218 3024 stats.print_stats(40)
3219 3025 elif options['lsprof']:
3220 3026 try:
3221 3027 from mercurial import lsprof
3222 3028 except ImportError:
3223 3029 raise util.Abort(_(
3224 3030 'lsprof not available - install from '
3225 3031 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3226 3032 p = lsprof.Profiler()
3227 3033 p.enable(subcalls=True)
3228 3034 try:
3229 3035 return d()
3230 3036 finally:
3231 3037 p.disable()
3232 3038 stats = lsprof.Stats(p.getstats())
3233 3039 stats.sort()
3234 3040 stats.pprint(top=10, file=sys.stderr, climit=5)
3235 3041 else:
3236 3042 return d()
3237 3043 finally:
3238 3044 u.flush()
3239 3045 except:
3240 3046 # enter the debugger when we hit an exception
3241 3047 if options['debugger']:
3242 3048 pdb.post_mortem(sys.exc_info()[2])
3243 3049 u.print_exc()
3244 3050 raise
3245 3051 except ParseError, inst:
3246 3052 if inst.args[0]:
3247 3053 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3248 3054 help_(u, inst.args[0])
3249 3055 else:
3250 3056 u.warn(_("hg: %s\n") % inst.args[1])
3251 3057 help_(u, 'shortlist')
3252 3058 except AmbiguousCommand, inst:
3253 3059 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3254 3060 (inst.args[0], " ".join(inst.args[1])))
3255 3061 except UnknownCommand, inst:
3256 3062 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3257 3063 help_(u, 'shortlist')
3258 3064 except hg.RepoError, inst:
3259 3065 u.warn(_("abort: %s!\n") % inst)
3260 3066 except lock.LockHeld, inst:
3261 3067 if inst.errno == errno.ETIMEDOUT:
3262 3068 reason = _('timed out waiting for lock held by %s') % inst.locker
3263 3069 else:
3264 3070 reason = _('lock held by %s') % inst.locker
3265 3071 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3266 3072 except lock.LockUnavailable, inst:
3267 3073 u.warn(_("abort: could not lock %s: %s\n") %
3268 3074 (inst.desc or inst.filename, inst.strerror))
3269 3075 except revlog.RevlogError, inst:
3270 3076 u.warn(_("abort: %s!\n") % inst)
3271 3077 except util.SignalInterrupt:
3272 3078 u.warn(_("killed!\n"))
3273 3079 except KeyboardInterrupt:
3274 3080 try:
3275 3081 u.warn(_("interrupted!\n"))
3276 3082 except IOError, inst:
3277 3083 if inst.errno == errno.EPIPE:
3278 3084 if u.debugflag:
3279 3085 u.warn(_("\nbroken pipe\n"))
3280 3086 else:
3281 3087 raise
3282 3088 except IOError, inst:
3283 3089 if hasattr(inst, "code"):
3284 3090 u.warn(_("abort: %s\n") % inst)
3285 3091 elif hasattr(inst, "reason"):
3286 3092 u.warn(_("abort: error: %s\n") % inst.reason[1])
3287 3093 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3288 3094 if u.debugflag:
3289 3095 u.warn(_("broken pipe\n"))
3290 3096 elif getattr(inst, "strerror", None):
3291 3097 if getattr(inst, "filename", None):
3292 3098 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3293 3099 else:
3294 3100 u.warn(_("abort: %s\n") % inst.strerror)
3295 3101 else:
3296 3102 raise
3297 3103 except OSError, inst:
3298 3104 if getattr(inst, "filename", None):
3299 3105 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3300 3106 else:
3301 3107 u.warn(_("abort: %s\n") % inst.strerror)
3302 3108 except util.UnexpectedOutput, inst:
3303 3109 u.warn(_("abort: %s") % inst[0])
3304 3110 if not isinstance(inst[1], basestring):
3305 3111 u.warn(" %r\n" % (inst[1],))
3306 3112 elif not inst[1]:
3307 3113 u.warn(_(" empty string\n"))
3308 3114 else:
3309 3115 u.warn("\n%r%s\n" %
3310 3116 (inst[1][:400], len(inst[1]) > 400 and '...' or ''))
3311 3117 except util.Abort, inst:
3312 3118 u.warn(_("abort: %s\n") % inst)
3313 3119 except TypeError, inst:
3314 3120 # was this an argument error?
3315 3121 tb = traceback.extract_tb(sys.exc_info()[2])
3316 3122 if len(tb) > 2: # no
3317 3123 raise
3318 3124 u.debug(inst, "\n")
3319 3125 u.warn(_("%s: invalid arguments\n") % cmd)
3320 3126 help_(u, cmd)
3321 3127 except SystemExit, inst:
3322 3128 # Commands shouldn't sys.exit directly, but give a return code.
3323 3129 # Just in case catch this and and pass exit code to caller.
3324 3130 return inst.code
3325 3131 except:
3326 3132 u.warn(_("** unknown exception encountered, details follow\n"))
3327 3133 u.warn(_("** report bug details to "
3328 3134 "http://www.selenic.com/mercurial/bts\n"))
3329 3135 u.warn(_("** or mercurial@selenic.com\n"))
3330 3136 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3331 3137 % version.get_version())
3332 3138 raise
3333 3139
3334 3140 return -1
General Comments 0
You need to be logged in to leave comments. Login now