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