##// END OF EJS Templates
Make annotate use option --rev instead od --revision like other commands....
Thomas Arendsen Hein -
r715:938dd667 default
parent child Browse files
Show More
@@ -1,1356 +1,1356 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 demandload(globals(), "os re sys signal shutil")
10 10 demandload(globals(), "fancyopts ui hg util")
11 11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
12 12 demandload(globals(), "errno socket version struct")
13 13
14 14 class UnknownCommand(Exception):
15 15 """Exception raised if command is not in the command table."""
16 16
17 17 def filterfiles(filters, files):
18 18 l = [x for x in files if x in filters]
19 19
20 20 for t in filters:
21 21 if t and t[-1] != "/":
22 22 t += "/"
23 23 l += [x for x in files if x.startswith(t)]
24 24 return l
25 25
26 26 def relfilter(repo, files):
27 27 cwd = repo.getcwd()
28 28 if cwd:
29 29 return filterfiles([util.pconvert(cwd)], files)
30 30 return files
31 31
32 32 def relpath(repo, args):
33 33 cwd = repo.getcwd()
34 34 if cwd:
35 35 return [util.pconvert(os.path.normpath(os.path.join(cwd, x)))
36 36 for x in args]
37 37 return args
38 38
39 39 revrangesep = ':'
40 40
41 41 def revrange(ui, repo, revs, revlog=None):
42 42 if revlog is None:
43 43 revlog = repo.changelog
44 44 revcount = revlog.count()
45 45 def fix(val, defval):
46 46 if not val:
47 47 return defval
48 48 try:
49 49 num = int(val)
50 50 if str(num) != val:
51 51 raise ValueError
52 52 if num < 0:
53 53 num += revcount
54 54 if not (0 <= num < revcount):
55 55 raise ValueError
56 56 except ValueError:
57 57 try:
58 58 num = repo.changelog.rev(repo.lookup(val))
59 59 except KeyError:
60 60 try:
61 61 num = revlog.rev(revlog.lookup(val))
62 62 except KeyError:
63 63 ui.warn('abort: invalid revision identifier %s\n' % val)
64 64 sys.exit(1)
65 65 return num
66 66 for spec in revs:
67 67 if spec.find(revrangesep) >= 0:
68 68 start, end = spec.split(revrangesep, 1)
69 69 start = fix(start, 0)
70 70 end = fix(end, revcount - 1)
71 71 if end > start:
72 72 end += 1
73 73 step = 1
74 74 else:
75 75 end -= 1
76 76 step = -1
77 77 for rev in xrange(start, end, step):
78 78 yield str(rev)
79 79 else:
80 80 yield spec
81 81
82 82 def make_filename(repo, r, pat, node=None,
83 83 total=None, seqno=None, revwidth=None):
84 84 node_expander = {
85 85 'H': lambda: hg.hex(node),
86 86 'R': lambda: str(r.rev(node)),
87 87 'h': lambda: hg.short(node),
88 88 }
89 89 expander = {
90 90 '%': lambda: '%',
91 91 'b': lambda: os.path.basename(repo.root),
92 92 }
93 93
94 94 if node:
95 95 expander.update(node_expander)
96 96 if node and revwidth is not None:
97 97 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
98 98 if total is not None:
99 99 expander['N'] = lambda: str(total)
100 100 if seqno is not None:
101 101 expander['n'] = lambda: str(seqno)
102 102 if total is not None and seqno is not None:
103 103 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
104 104
105 105 newname = []
106 106 patlen = len(pat)
107 107 i = 0
108 108 while i < patlen:
109 109 c = pat[i]
110 110 if c == '%':
111 111 i += 1
112 112 c = pat[i]
113 113 c = expander[c]()
114 114 newname.append(c)
115 115 i += 1
116 116 return ''.join(newname)
117 117
118 118 def dodiff(fp, ui, repo, files=None, node1=None, node2=None):
119 119 def date(c):
120 120 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
121 121
122 122 (c, a, d, u) = repo.changes(node1, node2, files)
123 123 if files:
124 124 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
125 125
126 126 if not c and not a and not d:
127 127 return
128 128
129 129 if node2:
130 130 change = repo.changelog.read(node2)
131 131 mmap2 = repo.manifest.read(change[0])
132 132 date2 = date(change)
133 133 def read(f):
134 134 return repo.file(f).read(mmap2[f])
135 135 else:
136 136 date2 = time.asctime()
137 137 if not node1:
138 138 node1 = repo.dirstate.parents()[0]
139 139 def read(f):
140 140 return repo.wfile(f).read()
141 141
142 142 if ui.quiet:
143 143 r = None
144 144 else:
145 145 hexfunc = ui.verbose and hg.hex or hg.short
146 146 r = [hexfunc(node) for node in [node1, node2] if node]
147 147
148 148 change = repo.changelog.read(node1)
149 149 mmap = repo.manifest.read(change[0])
150 150 date1 = date(change)
151 151
152 152 for f in c:
153 153 to = None
154 154 if f in mmap:
155 155 to = repo.file(f).read(mmap[f])
156 156 tn = read(f)
157 157 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
158 158 for f in a:
159 159 to = None
160 160 tn = read(f)
161 161 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
162 162 for f in d:
163 163 to = repo.file(f).read(mmap[f])
164 164 tn = None
165 165 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
166 166
167 167 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
168 168 """show a single changeset or file revision"""
169 169 changelog = repo.changelog
170 170 if filelog:
171 171 log = filelog
172 172 filerev = rev
173 173 node = filenode = filelog.node(filerev)
174 174 changerev = filelog.linkrev(filenode)
175 175 changenode = changenode or changelog.node(changerev)
176 176 else:
177 177 log = changelog
178 178 changerev = rev
179 179 if changenode is None:
180 180 changenode = changelog.node(changerev)
181 181 elif not changerev:
182 182 rev = changerev = changelog.rev(changenode)
183 183 node = changenode
184 184
185 185 if ui.quiet:
186 186 ui.write("%d:%s\n" % (rev, hg.hex(node)))
187 187 return
188 188
189 189 changes = changelog.read(changenode)
190 190
191 191 parents = [(log.rev(parent), hg.hex(parent))
192 192 for parent in log.parents(node)
193 193 if ui.debugflag or parent != hg.nullid]
194 194 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
195 195 parents = []
196 196
197 197 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
198 198 for tag in repo.nodetags(changenode):
199 199 ui.status("tag: %s\n" % tag)
200 200 for parent in parents:
201 201 ui.write("parent: %d:%s\n" % parent)
202 202 if filelog:
203 203 ui.debug("file rev: %d:%s\n" % (filerev, hg.hex(filenode)))
204 204 ui.note("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
205 205 hg.hex(changes[0])))
206 206 ui.status("user: %s\n" % changes[1])
207 207 ui.status("date: %s\n" % time.asctime(
208 208 time.localtime(float(changes[2].split(' ')[0]))))
209 209 if ui.debugflag:
210 210 files = repo.changes(changelog.parents(changenode)[0], changenode)
211 211 for key, value in zip(["files:", "files+:", "files-:"], files):
212 212 if value:
213 213 ui.note("%-12s %s\n" % (key, " ".join(value)))
214 214 else:
215 215 ui.note("files: %s\n" % " ".join(changes[3]))
216 216 description = changes[4].strip()
217 217 if description:
218 218 if ui.verbose:
219 219 ui.status("description:\n")
220 220 ui.status(description)
221 221 ui.status("\n\n")
222 222 else:
223 223 ui.status("summary: %s\n" % description.splitlines()[0])
224 224 ui.status("\n")
225 225
226 226 def show_version(ui):
227 227 """output version and copyright information"""
228 228 ui.write("Mercurial version %s\n" % version.get_version())
229 229 ui.status(
230 230 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
231 231 "This is free software; see the source for copying conditions. "
232 232 "There is NO\nwarranty; "
233 233 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
234 234 )
235 235
236 236 def help_(ui, cmd=None):
237 237 """show help for a given command or all commands"""
238 238 if cmd:
239 239 try:
240 240 i = find(cmd)
241 241 ui.write("%s\n\n" % i[2])
242 242
243 243 if i[1]:
244 244 for s, l, d, c in i[1]:
245 245 opt = ' '
246 246 if s:
247 247 opt = opt + '-' + s + ' '
248 248 if l:
249 249 opt = opt + '--' + l + ' '
250 250 if d:
251 251 opt = opt + '(' + str(d) + ')'
252 252 ui.write(opt, "\n")
253 253 if c:
254 254 ui.write(' %s\n' % c)
255 255 ui.write("\n")
256 256
257 257 ui.write(i[0].__doc__, "\n")
258 258 except UnknownCommand:
259 259 ui.warn("hg: unknown command %s\n" % cmd)
260 260 sys.exit(0)
261 261 else:
262 262 if ui.verbose:
263 263 show_version(ui)
264 264 ui.write('\n')
265 265 if ui.verbose:
266 266 ui.write('hg commands:\n\n')
267 267 else:
268 268 ui.write('basic hg commands (use "hg help -v" for more):\n\n')
269 269
270 270 h = {}
271 271 for c, e in table.items():
272 272 f = c.split("|")[0]
273 273 if not ui.verbose and not f.startswith("^"):
274 274 continue
275 275 if not ui.debugflag and f.startswith("debug"):
276 276 continue
277 277 f = f.lstrip("^")
278 278 d = ""
279 279 if e[0].__doc__:
280 280 d = e[0].__doc__.splitlines(0)[0].rstrip()
281 281 h[f] = d
282 282
283 283 fns = h.keys()
284 284 fns.sort()
285 285 m = max(map(len, fns))
286 286 for f in fns:
287 287 ui.write(' %-*s %s\n' % (m, f, h[f]))
288 288
289 289 # Commands start here, listed alphabetically
290 290
291 291 def add(ui, repo, file1, *files):
292 292 '''add the specified files on the next commit'''
293 293 repo.add(relpath(repo, (file1,) + files))
294 294
295 295 def addremove(ui, repo, *files):
296 296 """add all new files, delete all missing files"""
297 297 if files:
298 298 files = relpath(repo, files)
299 299 d = []
300 300 u = []
301 301 for f in files:
302 302 p = repo.wjoin(f)
303 303 s = repo.dirstate.state(f)
304 304 isfile = os.path.isfile(p)
305 305 if s != 'r' and not isfile:
306 306 d.append(f)
307 307 elif s not in 'nmai' and isfile:
308 308 u.append(f)
309 309 else:
310 310 (c, a, d, u) = repo.changes(None, None)
311 311 repo.add(u)
312 312 repo.remove(d)
313 313
314 314 def annotate(ui, repo, file1, *files, **opts):
315 315 """show changeset information per file line"""
316 316 def getnode(rev):
317 317 return hg.short(repo.changelog.node(rev))
318 318
319 319 def getname(rev):
320 320 try:
321 321 return bcache[rev]
322 322 except KeyError:
323 323 cl = repo.changelog.read(repo.changelog.node(rev))
324 324 name = cl[1]
325 325 f = name.find('@')
326 326 if f >= 0:
327 327 name = name[:f]
328 328 f = name.find('<')
329 329 if f >= 0:
330 330 name = name[f+1:]
331 331 bcache[rev] = name
332 332 return name
333 333
334 334 bcache = {}
335 335 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
336 336 if not opts['user'] and not opts['changeset']:
337 337 opts['number'] = 1
338 338
339 if opts['revision']:
340 node = repo.changelog.lookup(opts['revision'])
339 if opts['rev']:
340 node = repo.changelog.lookup(opts['rev'])
341 341 else:
342 342 node = repo.dirstate.parents()[0]
343 343 change = repo.changelog.read(node)
344 344 mmap = repo.manifest.read(change[0])
345 345 for f in relpath(repo, (file1,) + files):
346 346 lines = repo.file(f).annotate(mmap[f])
347 347 pieces = []
348 348
349 349 for o, f in opmap:
350 350 if opts[o]:
351 351 l = [f(n) for n, dummy in lines]
352 352 m = max(map(len, l))
353 353 pieces.append(["%*s" % (m, x) for x in l])
354 354
355 355 for p, l in zip(zip(*pieces), lines):
356 356 ui.write("%s: %s" % (" ".join(p), l[1]))
357 357
358 358 def cat(ui, repo, file1, rev=None, **opts):
359 359 """output the latest or given revision of a file"""
360 360 r = repo.file(relpath(repo, [file1])[0])
361 361 if rev:
362 362 n = r.lookup(rev)
363 363 else:
364 364 n = r.tip()
365 365 if opts['output'] and opts['output'] != '-':
366 366 try:
367 367 outname = make_filename(repo, r, opts['output'], node=n)
368 368 fp = open(outname, 'wb')
369 369 except KeyError, inst:
370 370 ui.warn("error: invlaid format spec '%%%s' in output file name\n" %
371 371 inst.args[0])
372 372 sys.exit(1);
373 373 else:
374 374 fp = sys.stdout
375 375 fp.write(r.read(n))
376 376
377 377 def clone(ui, source, dest=None, **opts):
378 378 """make a copy of an existing repository"""
379 379 if dest is None:
380 380 dest = os.path.basename(os.path.normpath(source))
381 381
382 382 if os.path.exists(dest):
383 383 ui.warn("abort: destination '%s' already exists\n" % dest)
384 384 return 1
385 385
386 386 class Dircleanup:
387 387 def __init__(self, dir_):
388 388 self.rmtree = shutil.rmtree
389 389 self.dir_ = dir_
390 390 os.mkdir(dir_)
391 391 def close(self):
392 392 self.dir_ = None
393 393 def __del__(self):
394 394 if self.dir_:
395 395 self.rmtree(self.dir_, True)
396 396
397 397 d = Dircleanup(dest)
398 398 abspath = source
399 399 source = ui.expandpath(source)
400 400 other = hg.repository(ui, source)
401 401
402 402 if other.dev() != -1:
403 403 abspath = os.path.abspath(source)
404 404 copyfile = (os.stat(dest).st_dev == other.dev()
405 405 and getattr(os, 'link', None) or shutil.copy2)
406 406 if copyfile is not shutil.copy2:
407 407 ui.note("cloning by hardlink\n")
408 408 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
409 409 copyfile)
410 410 try:
411 411 os.unlink(os.path.join(dest, ".hg", "dirstate"))
412 412 except IOError:
413 413 pass
414 414
415 415 repo = hg.repository(ui, dest)
416 416
417 417 else:
418 418 repo = hg.repository(ui, dest, create=1)
419 419 repo.pull(other)
420 420
421 421 f = repo.opener("hgrc", "w")
422 422 f.write("[paths]\n")
423 423 f.write("default = %s\n" % abspath)
424 424
425 425 if not opts['noupdate']:
426 426 update(ui, repo)
427 427
428 428 d.close()
429 429
430 430 def commit(ui, repo, *files, **opts):
431 431 """commit the specified files or all outstanding changes"""
432 432 text = opts['text']
433 433 logfile = opts['logfile']
434 434 if not text and logfile:
435 435 try:
436 436 text = open(logfile).read()
437 437 except IOError, why:
438 438 ui.warn("Can't read commit text %s: %s\n" % (logfile, why))
439 439
440 440 if opts['addremove']:
441 441 addremove(ui, repo, *files)
442 442 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
443 443
444 444 def copy(ui, repo, source, dest):
445 445 """mark a file as copied or renamed for the next commit"""
446 446 return repo.copy(*relpath(repo, (source, dest)))
447 447
448 448 def debugcheckstate(ui, repo):
449 449 """validate the correctness of the current dirstate"""
450 450 parent1, parent2 = repo.dirstate.parents()
451 451 repo.dirstate.read()
452 452 dc = repo.dirstate.map
453 453 keys = dc.keys()
454 454 keys.sort()
455 455 m1n = repo.changelog.read(parent1)[0]
456 456 m2n = repo.changelog.read(parent2)[0]
457 457 m1 = repo.manifest.read(m1n)
458 458 m2 = repo.manifest.read(m2n)
459 459 errors = 0
460 460 for f in dc:
461 461 state = repo.dirstate.state(f)
462 462 if state in "nr" and f not in m1:
463 463 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
464 464 errors += 1
465 465 if state in "a" and f in m1:
466 466 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
467 467 errors += 1
468 468 if state in "m" and f not in m1 and f not in m2:
469 469 ui.warn("%s in state %s, but not in either manifest\n" %
470 470 (f, state))
471 471 errors += 1
472 472 for f in m1:
473 473 state = repo.dirstate.state(f)
474 474 if state not in "nrm":
475 475 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
476 476 errors += 1
477 477 if errors:
478 478 ui.warn(".hg/dirstate inconsistent with current parent's manifest\n")
479 479 sys.exit(1)
480 480
481 481 def debugstate(ui, repo):
482 482 """show the contents of the current dirstate"""
483 483 repo.dirstate.read()
484 484 dc = repo.dirstate.map
485 485 keys = dc.keys()
486 486 keys.sort()
487 487 for file_ in keys:
488 488 ui.write("%c %s\n" % (dc[file_][0], file_))
489 489
490 490 def debugindex(ui, file_):
491 491 """dump the contents of an index file"""
492 492 r = hg.revlog(hg.opener(""), file_, "")
493 493 ui.write(" rev offset length base linkrev" +
494 494 " p1 p2 nodeid\n")
495 495 for i in range(r.count()):
496 496 e = r.index[i]
497 497 ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
498 498 i, e[0], e[1], e[2], e[3],
499 499 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
500 500
501 501 def debugindexdot(ui, file_):
502 502 """dump an index DAG as a .dot file"""
503 503 r = hg.revlog(hg.opener(""), file_, "")
504 504 ui.write("digraph G {\n")
505 505 for i in range(r.count()):
506 506 e = r.index[i]
507 507 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
508 508 if e[5] != hg.nullid:
509 509 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
510 510 ui.write("}\n")
511 511
512 512 def diff(ui, repo, *files, **opts):
513 513 """diff working directory (or selected files)"""
514 514 revs = []
515 515 if opts['rev']:
516 516 revs = map(lambda x: repo.lookup(x), opts['rev'])
517 517
518 518 if len(revs) > 2:
519 519 ui.warn("too many revisions to diff\n")
520 520 sys.exit(1)
521 521
522 522 if files:
523 523 files = relpath(repo, files)
524 524 else:
525 525 files = relpath(repo, [""])
526 526
527 527 dodiff(sys.stdout, ui, repo, files, *revs)
528 528
529 529 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
530 530 node = repo.lookup(changeset)
531 531 prev, other = repo.changelog.parents(node)
532 532 change = repo.changelog.read(node)
533 533
534 534 if opts['output'] and opts['output'] != '-':
535 535 try:
536 536 outname = make_filename(repo, repo.changelog, opts['output'],
537 537 node=node, total=total, seqno=seqno,
538 538 revwidth=revwidth)
539 539 ui.note("Exporting patch to '%s'.\n" % outname)
540 540 fp = open(outname, 'wb')
541 541 except KeyError, inst:
542 542 ui.warn("error: invalid format spec '%%%s' in output file name\n" %
543 543 inst.args[0])
544 544 sys.exit(1)
545 545 else:
546 546 fp = sys.stdout
547 547
548 548 fp.write("# HG changeset patch\n")
549 549 fp.write("# User %s\n" % change[1])
550 550 fp.write("# Node ID %s\n" % hg.hex(node))
551 551 fp.write("# Parent %s\n" % hg.hex(prev))
552 552 if other != hg.nullid:
553 553 fp.write("# Parent %s\n" % hg.hex(other))
554 554 fp.write(change[4].rstrip())
555 555 fp.write("\n\n")
556 556
557 557 dodiff(fp, ui, repo, None, prev, node)
558 558
559 559 def export(ui, repo, *changesets, **opts):
560 560 """dump the header and diffs for one or more changesets"""
561 561 if not changesets:
562 562 ui.warn("error: export requires at least one changeset\n")
563 563 sys.exit(1)
564 564 seqno = 0
565 565 revs = list(revrange(ui, repo, changesets))
566 566 total = len(revs)
567 567 revwidth = max(len(revs[0]), len(revs[-1]))
568 568 for cset in revs:
569 569 seqno += 1
570 570 doexport(ui, repo, cset, seqno, total, revwidth, opts)
571 571
572 572 def forget(ui, repo, file1, *files):
573 573 """don't add the specified files on the next commit"""
574 574 repo.forget(relpath(repo, (file1,) + files))
575 575
576 576 def heads(ui, repo):
577 577 """show current repository heads"""
578 578 for n in repo.changelog.heads():
579 579 show_changeset(ui, repo, changenode=n)
580 580
581 581 def identify(ui, repo):
582 582 """print information about the working copy"""
583 583 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
584 584 if not parents:
585 585 ui.write("unknown\n")
586 586 return
587 587
588 588 hexfunc = ui.verbose and hg.hex or hg.short
589 589 (c, a, d, u) = repo.changes(None, None)
590 590 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
591 591 (c or a or d) and "+" or "")]
592 592
593 593 if not ui.quiet:
594 594 # multiple tags for a single parent separated by '/'
595 595 parenttags = ['/'.join(tags)
596 596 for tags in map(repo.nodetags, parents) if tags]
597 597 # tags for multiple parents separated by ' + '
598 598 output.append(' + '.join(parenttags))
599 599
600 600 ui.write("%s\n" % ' '.join(output))
601 601
602 602 def import_(ui, repo, patch1, *patches, **opts):
603 603 """import an ordered set of patches"""
604 604 try:
605 605 import psyco
606 606 psyco.full()
607 607 except ImportError:
608 608 pass
609 609
610 610 patches = (patch1,) + patches
611 611
612 612 d = opts["base"]
613 613 strip = opts["strip"]
614 614
615 615 for patch in patches:
616 616 ui.status("applying %s\n" % patch)
617 617 pf = os.path.join(d, patch)
618 618
619 619 text = []
620 620 user = None
621 621 hgpatch = False
622 622 for line in file(pf):
623 623 line = line.rstrip()
624 624 if line.startswith("--- ") or line.startswith("diff -r"):
625 625 break
626 626 elif hgpatch:
627 627 # parse values when importing the result of an hg export
628 628 if line.startswith("# User "):
629 629 user = line[7:]
630 630 ui.debug('User: %s\n' % user)
631 631 elif not line.startswith("# ") and line:
632 632 text.append(line)
633 633 hgpatch = False
634 634 elif line == '# HG changeset patch':
635 635 hgpatch = True
636 636 else:
637 637 text.append(line)
638 638
639 639 # make sure text isn't empty
640 640 if not text:
641 641 text = "imported patch %s\n" % patch
642 642 else:
643 643 text = "%s\n" % '\n'.join(text)
644 644 ui.debug('text:\n%s\n' % text)
645 645
646 646 f = os.popen("patch -p%d < %s" % (strip, pf))
647 647 files = []
648 648 for l in f.read().splitlines():
649 649 l.rstrip('\r\n');
650 650 ui.status("%s\n" % l)
651 651 if l.startswith('patching file '):
652 652 pf = l[14:]
653 653 if pf not in files:
654 654 files.append(pf)
655 655 patcherr = f.close()
656 656 if patcherr:
657 657 sys.stderr.write("patch failed")
658 658 sys.exit(1)
659 659
660 660 if len(files) > 0:
661 661 addremove(ui, repo, *files)
662 662 repo.commit(files, text, user)
663 663
664 664 def init(ui, source=None):
665 665 """create a new repository in the current directory"""
666 666
667 667 if source:
668 668 ui.warn("no longer supported: use \"hg clone\" instead\n")
669 669 sys.exit(1)
670 670 hg.repository(ui, ".", create=1)
671 671
672 672 def locate(ui, repo, *pats, **opts):
673 673 """locate files matching specific patterns"""
674 674 if [p for p in pats if os.sep in p]:
675 675 ui.warn("error: patterns may not contain '%s'\n" % os.sep)
676 676 ui.warn("use '-i <dir>' instead\n")
677 677 sys.exit(1)
678 678 def compile(pats, head='^', tail=os.sep, on_empty=True):
679 679 if not pats:
680 680 class c:
681 681 def match(self, x):
682 682 return on_empty
683 683 return c()
684 684 fnpats = [fnmatch.translate(os.path.normpath(os.path.normcase(p)))[:-1]
685 685 for p in pats]
686 686 regexp = r'%s(?:%s)%s' % (head, '|'.join(fnpats), tail)
687 687 return re.compile(regexp)
688 688 exclude = compile(opts['exclude'], on_empty=False)
689 689 include = compile(opts['include'])
690 690 pat = compile(pats, head='', tail='$')
691 691 end = opts['print0'] and '\0' or '\n'
692 692 if opts['rev']:
693 693 node = repo.manifest.lookup(opts['rev'])
694 694 else:
695 695 node = repo.manifest.tip()
696 696 manifest = repo.manifest.read(node)
697 697 cwd = repo.getcwd()
698 698 cwd_plus = cwd and (cwd + os.sep)
699 699 found = []
700 700 for f in manifest:
701 701 f = os.path.normcase(f)
702 702 if exclude.match(f) or not(include.match(f) and
703 703 f.startswith(cwd_plus) and
704 704 pat.match(os.path.basename(f))):
705 705 continue
706 706 if opts['fullpath']:
707 707 f = os.path.join(repo.root, f)
708 708 elif cwd:
709 709 f = f[len(cwd_plus):]
710 710 found.append(f)
711 711 found.sort()
712 712 for f in found:
713 713 ui.write(f, end)
714 714
715 715 def log(ui, repo, f=None, **opts):
716 716 """show the revision history of the repository or a single file"""
717 717 if f:
718 718 files = relpath(repo, [f])
719 719 filelog = repo.file(files[0])
720 720 log = filelog
721 721 lookup = filelog.lookup
722 722 else:
723 723 files = None
724 724 filelog = None
725 725 log = repo.changelog
726 726 lookup = repo.lookup
727 727 revlist = []
728 728 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
729 729 while revs:
730 730 if len(revs) == 1:
731 731 revlist.append(revs.pop(0))
732 732 else:
733 733 a = revs.pop(0)
734 734 b = revs.pop(0)
735 735 off = a > b and -1 or 1
736 736 revlist.extend(range(a, b + off, off))
737 737
738 738 for i in revlist or range(log.count() - 1, -1, -1):
739 739 show_changeset(ui, repo, filelog=filelog, rev=i)
740 740 if opts['patch']:
741 741 if filelog:
742 742 filenode = filelog.node(i)
743 743 i = filelog.linkrev(filenode)
744 744 changenode = repo.changelog.node(i)
745 745 prev, other = repo.changelog.parents(changenode)
746 746 dodiff(sys.stdout, ui, repo, files, prev, changenode)
747 747 ui.write("\n\n")
748 748
749 749 def manifest(ui, repo, rev=None):
750 750 """output the latest or given revision of the project manifest"""
751 751 if rev:
752 752 try:
753 753 # assume all revision numbers are for changesets
754 754 n = repo.lookup(rev)
755 755 change = repo.changelog.read(n)
756 756 n = change[0]
757 757 except hg.RepoError:
758 758 n = repo.manifest.lookup(rev)
759 759 else:
760 760 n = repo.manifest.tip()
761 761 m = repo.manifest.read(n)
762 762 mf = repo.manifest.readflags(n)
763 763 files = m.keys()
764 764 files.sort()
765 765
766 766 for f in files:
767 767 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
768 768
769 769 def parents(ui, repo, rev=None):
770 770 """show the parents of the working dir or revision"""
771 771 if rev:
772 772 p = repo.changelog.parents(repo.lookup(rev))
773 773 else:
774 774 p = repo.dirstate.parents()
775 775
776 776 for n in p:
777 777 if n != hg.nullid:
778 778 show_changeset(ui, repo, changenode=n)
779 779
780 780 def pull(ui, repo, source="default", **opts):
781 781 """pull changes from the specified source"""
782 782 source = ui.expandpath(source)
783 783 ui.status('pulling from %s\n' % (source))
784 784
785 785 other = hg.repository(ui, source)
786 786 r = repo.pull(other)
787 787 if not r:
788 788 if opts['update']:
789 789 return update(ui, repo)
790 790 else:
791 791 ui.status("(run 'hg update' to get a working copy)\n")
792 792
793 793 return r
794 794
795 795 def push(ui, repo, dest="default-push"):
796 796 """push changes to the specified destination"""
797 797 dest = ui.expandpath(dest)
798 798 ui.status('pushing to %s\n' % (dest))
799 799
800 800 other = hg.repository(ui, dest)
801 801 r = repo.push(other)
802 802 return r
803 803
804 804 def rawcommit(ui, repo, *flist, **rc):
805 805 "raw commit interface"
806 806
807 807 text = rc['text']
808 808 if not text and rc['logfile']:
809 809 try:
810 810 text = open(rc['logfile']).read()
811 811 except IOError:
812 812 pass
813 813 if not text and not rc['logfile']:
814 814 ui.warn("abort: missing commit text\n")
815 815 return 1
816 816
817 817 files = relpath(repo, list(flist))
818 818 if rc['files']:
819 819 files += open(rc['files']).read().splitlines()
820 820
821 821 rc['parent'] = map(repo.lookup, rc['parent'])
822 822
823 823 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
824 824
825 825 def recover(ui, repo):
826 826 """roll back an interrupted transaction"""
827 827 repo.recover()
828 828
829 829 def remove(ui, repo, file1, *files):
830 830 """remove the specified files on the next commit"""
831 831 repo.remove(relpath(repo, (file1,) + files))
832 832
833 833 def revert(ui, repo, *names, **opts):
834 834 """revert modified files or dirs back to their unmodified states"""
835 835 node = opts['rev'] and repo.lookup(opts['rev']) or \
836 836 repo.dirstate.parents()[0]
837 837 root = os.path.realpath(repo.root)
838 838
839 839 def trimpath(p):
840 840 p = os.path.realpath(p)
841 841 if p.startswith(root):
842 842 rest = p[len(root):]
843 843 if not rest:
844 844 return rest
845 845 if p.startswith(os.sep):
846 846 return rest[1:]
847 847 return p
848 848
849 849 relnames = map(trimpath, names or [os.getcwd()])
850 850 chosen = {}
851 851
852 852 def choose(name):
853 853 def body(name):
854 854 for r in relnames:
855 855 if not name.startswith(r):
856 856 continue
857 857 rest = name[len(r):]
858 858 if not rest:
859 859 return r, True
860 860 depth = rest.count(os.sep)
861 861 if not r:
862 862 if depth == 0 or not opts['nonrecursive']:
863 863 return r, True
864 864 elif rest[0] == os.sep:
865 865 if depth == 1 or not opts['nonrecursive']:
866 866 return r, True
867 867 return None, False
868 868 relname, ret = body(name)
869 869 if ret:
870 870 chosen[relname] = 1
871 871 return ret
872 872
873 873 r = repo.update(node, False, True, choose, False)
874 874 for n in relnames:
875 875 if n not in chosen:
876 876 ui.warn('error: no matches for %s\n' % n)
877 877 r = 1
878 878 sys.stdout.flush()
879 879 return r
880 880
881 881 def root(ui, repo):
882 882 """print the root (top) of the current working dir"""
883 883 ui.write(repo.root + "\n")
884 884
885 885 def serve(ui, repo, **opts):
886 886 """export the repository via HTTP"""
887 887
888 888 if opts["stdio"]:
889 889 fin, fout = sys.stdin, sys.stdout
890 890 sys.stdout = sys.stderr
891 891
892 892 def getarg():
893 893 argline = fin.readline()[:-1]
894 894 arg, l = argline.split()
895 895 val = fin.read(int(l))
896 896 return arg, val
897 897 def respond(v):
898 898 fout.write("%d\n" % len(v))
899 899 fout.write(v)
900 900 fout.flush()
901 901
902 902 lock = None
903 903
904 904 while 1:
905 905 cmd = fin.readline()[:-1]
906 906 if cmd == '':
907 907 return
908 908 if cmd == "heads":
909 909 h = repo.heads()
910 910 respond(" ".join(map(hg.hex, h)) + "\n")
911 911 if cmd == "lock":
912 912 lock = repo.lock()
913 913 respond("")
914 914 if cmd == "unlock":
915 915 if lock:
916 916 lock.release()
917 917 lock = None
918 918 respond("")
919 919 elif cmd == "branches":
920 920 arg, nodes = getarg()
921 921 nodes = map(hg.bin, nodes.split(" "))
922 922 r = []
923 923 for b in repo.branches(nodes):
924 924 r.append(" ".join(map(hg.hex, b)) + "\n")
925 925 respond("".join(r))
926 926 elif cmd == "between":
927 927 arg, pairs = getarg()
928 928 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
929 929 r = []
930 930 for b in repo.between(pairs):
931 931 r.append(" ".join(map(hg.hex, b)) + "\n")
932 932 respond("".join(r))
933 933 elif cmd == "changegroup":
934 934 nodes = []
935 935 arg, roots = getarg()
936 936 nodes = map(hg.bin, roots.split(" "))
937 937
938 938 cg = repo.changegroup(nodes)
939 939 while 1:
940 940 d = cg.read(4096)
941 941 if not d:
942 942 break
943 943 fout.write(d)
944 944
945 945 fout.flush()
946 946
947 947 elif cmd == "addchangegroup":
948 948 if not lock:
949 949 respond("not locked")
950 950 continue
951 951 respond("")
952 952
953 953 r = repo.addchangegroup(fin)
954 954 respond("")
955 955
956 956 def openlog(opt, default):
957 957 if opts[opt] and opts[opt] != '-':
958 958 return open(opts[opt], 'w')
959 959 else:
960 960 return default
961 961
962 962 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
963 963 opts["address"], opts["port"],
964 964 openlog('accesslog', sys.stdout),
965 965 openlog('errorlog', sys.stderr))
966 966 if ui.verbose:
967 967 addr, port = httpd.socket.getsockname()
968 968 if addr == '0.0.0.0':
969 969 addr = socket.gethostname()
970 970 else:
971 971 try:
972 972 addr = socket.gethostbyaddr(addr)[0]
973 973 except socket.error:
974 974 pass
975 975 if port != 80:
976 976 ui.status('listening at http://%s:%d/\n' % (addr, port))
977 977 else:
978 978 ui.status('listening at http://%s/\n' % addr)
979 979 httpd.serve_forever()
980 980
981 981 def status(ui, repo):
982 982 '''show changed files in the working directory
983 983
984 984 C = changed
985 985 A = added
986 986 R = removed
987 987 ? = not tracked'''
988 988
989 989 (c, a, d, u) = repo.changes(None, None)
990 990 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
991 991
992 992 for f in c:
993 993 ui.write("C ", f, "\n")
994 994 for f in a:
995 995 ui.write("A ", f, "\n")
996 996 for f in d:
997 997 ui.write("R ", f, "\n")
998 998 for f in u:
999 999 ui.write("? ", f, "\n")
1000 1000
1001 1001 def tag(ui, repo, name, rev=None, **opts):
1002 1002 """add a tag for the current tip or a given revision"""
1003 1003
1004 1004 if name == "tip":
1005 1005 ui.warn("abort: 'tip' is a reserved name!\n")
1006 1006 return -1
1007 1007 if rev:
1008 1008 r = hg.hex(repo.lookup(rev))
1009 1009 else:
1010 1010 r = hg.hex(repo.changelog.tip())
1011 1011
1012 1012 if name.find(revrangesep) >= 0:
1013 1013 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1014 1014 return -1
1015 1015
1016 1016 if opts['local']:
1017 1017 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1018 1018 return
1019 1019
1020 1020 (c, a, d, u) = repo.changes(None, None)
1021 1021 for x in (c, a, d, u):
1022 1022 if ".hgtags" in x:
1023 1023 ui.warn("abort: working copy of .hgtags is changed!\n")
1024 1024 ui.status("(please commit .hgtags manually)\n")
1025 1025 return -1
1026 1026
1027 1027 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1028 1028 if repo.dirstate.state(".hgtags") == '?':
1029 1029 repo.add([".hgtags"])
1030 1030
1031 1031 if not opts['text']:
1032 1032 opts['text'] = "Added tag %s for changeset %s" % (name, r)
1033 1033
1034 1034 repo.commit([".hgtags"], opts['text'], opts['user'], opts['date'])
1035 1035
1036 1036 def tags(ui, repo):
1037 1037 """list repository tags"""
1038 1038
1039 1039 l = repo.tagslist()
1040 1040 l.reverse()
1041 1041 for t, n in l:
1042 1042 try:
1043 1043 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1044 1044 except KeyError:
1045 1045 r = " ?:?"
1046 1046 ui.write("%-30s %s\n" % (t, r))
1047 1047
1048 1048 def tip(ui, repo):
1049 1049 """show the tip revision"""
1050 1050 n = repo.changelog.tip()
1051 1051 show_changeset(ui, repo, changenode=n)
1052 1052
1053 1053 def undo(ui, repo):
1054 1054 """undo the last commit or pull
1055 1055
1056 1056 Roll back the last pull or commit transaction on the
1057 1057 repository, restoring the project to its earlier state.
1058 1058
1059 1059 This command should be used with care. There is only one level of
1060 1060 undo and there is no redo.
1061 1061
1062 1062 This command is not intended for use on public repositories. Once
1063 1063 a change is visible for pull by other users, undoing it locally is
1064 1064 ineffective.
1065 1065 """
1066 1066 repo.undo()
1067 1067
1068 1068 def update(ui, repo, node=None, merge=False, clean=False):
1069 1069 '''update or merge working directory
1070 1070
1071 1071 If there are no outstanding changes in the working directory and
1072 1072 there is a linear relationship between the current version and the
1073 1073 requested version, the result is the requested version.
1074 1074
1075 1075 Otherwise the result is a merge between the contents of the
1076 1076 current working directory and the requested version. Files that
1077 1077 changed between either parent are marked as changed for the next
1078 1078 commit and a commit must be performed before any further updates
1079 1079 are allowed.
1080 1080 '''
1081 1081 node = node and repo.lookup(node) or repo.changelog.tip()
1082 1082 return repo.update(node, allow=merge, force=clean)
1083 1083
1084 1084 def verify(ui, repo):
1085 1085 """verify the integrity of the repository"""
1086 1086 return repo.verify()
1087 1087
1088 1088 # Command options and aliases are listed here, alphabetically
1089 1089
1090 1090 table = {
1091 1091 "^add": (add, [], "hg add FILE..."),
1092 1092 "addremove": (addremove, [], "hg addremove [FILE]..."),
1093 1093 "^annotate":
1094 1094 (annotate,
1095 [('r', 'revision', '', 'revision'),
1095 [('r', 'rev', '', 'revision'),
1096 1096 ('u', 'user', None, 'show user'),
1097 1097 ('n', 'number', None, 'show revision number'),
1098 1098 ('c', 'changeset', None, 'show changeset')],
1099 1099 'hg annotate [-r REV] [-u] [-n] [-c] FILE...'),
1100 1100 "cat":
1101 1101 (cat,
1102 1102 [('o', 'output', "", 'output to file')],
1103 1103 'hg cat [-o OUTFILE] FILE [REV]'),
1104 1104 "^clone":
1105 1105 (clone,
1106 1106 [('U', 'noupdate', None, 'skip update after cloning')],
1107 1107 'hg clone [-U] SOURCE [DEST]'),
1108 1108 "^commit|ci":
1109 1109 (commit,
1110 1110 [('A', 'addremove', None, 'run add/remove during commit'),
1111 1111 ('t', 'text', "", 'commit text'),
1112 1112 ('l', 'logfile', "", 'commit text file'),
1113 1113 ('d', 'date', "", 'date code'),
1114 1114 ('u', 'user', "", 'user')],
1115 1115 'hg commit [OPTION]... [FILE]...'),
1116 1116 "copy": (copy, [], 'hg copy SOURCE DEST'),
1117 1117 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1118 1118 "debugstate": (debugstate, [], 'debugstate'),
1119 1119 "debugindex": (debugindex, [], 'debugindex FILE'),
1120 1120 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1121 1121 "^diff":
1122 1122 (diff,
1123 1123 [('r', 'rev', [], 'revision')],
1124 1124 'hg diff [-r REV1 [-r REV2]] [FILE]...'),
1125 1125 "^export":
1126 1126 (export,
1127 1127 [('o', 'output', "", 'output to file')],
1128 1128 "hg export [-o OUTFILE] REV..."),
1129 1129 "forget": (forget, [], "hg forget FILE..."),
1130 1130 "heads": (heads, [], 'hg heads'),
1131 1131 "help": (help_, [], 'hg help [COMMAND]'),
1132 1132 "identify|id": (identify, [], 'hg identify'),
1133 1133 "import|patch":
1134 1134 (import_,
1135 1135 [('p', 'strip', 1, 'path strip'),
1136 1136 ('b', 'base', "", 'base path')],
1137 1137 "hg import [-p NUM] [-b BASE] PATCH..."),
1138 1138 "^init": (init, [], 'hg init'),
1139 1139 "locate":
1140 1140 (locate,
1141 1141 [('0', 'print0', None, 'end records with NUL'),
1142 1142 ('f', 'fullpath', None, 'print complete paths'),
1143 1143 ('i', 'include', [], 'include path in search'),
1144 1144 ('r', 'rev', '', 'revision'),
1145 1145 ('x', 'exclude', [], 'exclude path from search')],
1146 1146 'hg locate [OPTION]... [PATTERN]...'),
1147 1147 "^log|history":
1148 1148 (log,
1149 1149 [('r', 'rev', [], 'revision'),
1150 1150 ('p', 'patch', None, 'show patch')],
1151 1151 'hg log [-r REV1 [-r REV2]] [-p] [FILE]'),
1152 1152 "manifest": (manifest, [], 'hg manifest [REV]'),
1153 1153 "parents": (parents, [], 'hg parents [REV]'),
1154 1154 "^pull":
1155 1155 (pull,
1156 1156 [('u', 'update', None, 'update working directory')],
1157 1157 'hg pull [-u] [SOURCE]'),
1158 1158 "^push": (push, [], 'hg push [DEST]'),
1159 1159 "rawcommit":
1160 1160 (rawcommit,
1161 1161 [('p', 'parent', [], 'parent'),
1162 1162 ('d', 'date', "", 'date code'),
1163 1163 ('u', 'user', "", 'user'),
1164 1164 ('F', 'files', "", 'file list'),
1165 1165 ('t', 'text', "", 'commit text'),
1166 1166 ('l', 'logfile', "", 'commit text file')],
1167 1167 'hg rawcommit [OPTION]... [FILE]...'),
1168 1168 "recover": (recover, [], "hg recover"),
1169 1169 "^remove|rm": (remove, [], "hg remove FILE..."),
1170 1170 "^revert":
1171 1171 (revert,
1172 1172 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1173 1173 ("r", "rev", "", "revision")],
1174 1174 "hg revert [-n] [-r REV] NAME..."),
1175 1175 "root": (root, [], "hg root"),
1176 1176 "^serve":
1177 1177 (serve,
1178 1178 [('A', 'accesslog', '', 'access log file'),
1179 1179 ('E', 'errorlog', '', 'error log file'),
1180 1180 ('p', 'port', 8000, 'listen port'),
1181 1181 ('a', 'address', '', 'interface address'),
1182 1182 ('n', 'name', os.getcwd(), 'repository name'),
1183 1183 ('', 'stdio', None, 'for remote clients'),
1184 1184 ('t', 'templates', "", 'template map')],
1185 1185 "hg serve [OPTION]..."),
1186 1186 "^status": (status, [], 'hg status'),
1187 1187 "tag":
1188 1188 (tag,
1189 1189 [('l', 'local', None, 'make the tag local'),
1190 1190 ('t', 'text', "", 'commit text'),
1191 1191 ('d', 'date', "", 'date code'),
1192 1192 ('u', 'user', "", 'user')],
1193 1193 'hg tag [OPTION]... NAME [REV]'),
1194 1194 "tags": (tags, [], 'hg tags'),
1195 1195 "tip": (tip, [], 'hg tip'),
1196 1196 "undo": (undo, [], 'hg undo'),
1197 1197 "^update|up|checkout|co":
1198 1198 (update,
1199 1199 [('m', 'merge', None, 'allow merging of conflicts'),
1200 1200 ('C', 'clean', None, 'overwrite locally modified files')],
1201 1201 'hg update [-m] [-C] [REV]'),
1202 1202 "verify": (verify, [], 'hg verify'),
1203 1203 "version": (show_version, [], 'hg version'),
1204 1204 }
1205 1205
1206 1206 globalopts = [('v', 'verbose', None, 'verbose'),
1207 1207 ('', 'debug', None, 'debug'),
1208 1208 ('q', 'quiet', None, 'quiet'),
1209 1209 ('', 'profile', None, 'profile'),
1210 1210 ('R', 'repository', "", 'repository root directory'),
1211 1211 ('', 'traceback', None, 'print traceback on exception'),
1212 1212 ('y', 'noninteractive', None, 'run non-interactively'),
1213 1213 ('', 'version', None, 'output version information and exit'),
1214 1214 ]
1215 1215
1216 1216 norepo = "clone init version help debugindex debugindexdot"
1217 1217
1218 1218 def find(cmd):
1219 1219 for e in table.keys():
1220 1220 if re.match("(%s)$" % e, cmd):
1221 1221 return table[e]
1222 1222
1223 1223 raise UnknownCommand(cmd)
1224 1224
1225 1225 class SignalInterrupt(Exception):
1226 1226 """Exception raised on SIGTERM and SIGHUP."""
1227 1227
1228 1228 def catchterm(*args):
1229 1229 raise SignalInterrupt
1230 1230
1231 1231 def run():
1232 1232 sys.exit(dispatch(sys.argv[1:]))
1233 1233
1234 1234 class ParseError(Exception):
1235 1235 """Exception raised on errors in parsing the command line."""
1236 1236
1237 1237 def parse(args):
1238 1238 options = {}
1239 1239 cmdoptions = {}
1240 1240
1241 1241 try:
1242 1242 args = fancyopts.fancyopts(args, globalopts, options)
1243 1243 except fancyopts.getopt.GetoptError, inst:
1244 1244 raise ParseError(None, inst)
1245 1245
1246 1246 if options["version"]:
1247 1247 return ("version", show_version, [], options, cmdoptions)
1248 1248 elif not args:
1249 1249 return ("help", help_, [], options, cmdoptions)
1250 1250 else:
1251 1251 cmd, args = args[0], args[1:]
1252 1252
1253 1253 i = find(cmd)
1254 1254
1255 1255 # combine global options into local
1256 1256 c = list(i[1])
1257 1257 for o in globalopts:
1258 1258 c.append((o[0], o[1], options[o[1]], o[3]))
1259 1259
1260 1260 try:
1261 1261 args = fancyopts.fancyopts(args, c, cmdoptions)
1262 1262 except fancyopts.getopt.GetoptError, inst:
1263 1263 raise ParseError(cmd, inst)
1264 1264
1265 1265 # separate global options back out
1266 1266 for o in globalopts:
1267 1267 n = o[1]
1268 1268 options[n] = cmdoptions[n]
1269 1269 del cmdoptions[n]
1270 1270
1271 1271 return (cmd, i[0], args, options, cmdoptions)
1272 1272
1273 1273 def dispatch(args):
1274 1274 signal.signal(signal.SIGTERM, catchterm)
1275 1275 try:
1276 1276 signal.signal(signal.SIGHUP, catchterm)
1277 1277 except AttributeError:
1278 1278 pass
1279 1279
1280 1280 try:
1281 1281 cmd, func, args, options, cmdoptions = parse(args)
1282 1282 except ParseError, inst:
1283 1283 u = ui.ui()
1284 1284 if inst.args[0]:
1285 1285 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1286 1286 help_(u, inst.args[0])
1287 1287 else:
1288 1288 u.warn("hg: %s\n" % inst.args[1])
1289 1289 help_(u)
1290 1290 sys.exit(-1)
1291 1291 except UnknownCommand, inst:
1292 1292 u = ui.ui()
1293 1293 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1294 1294 help_(u)
1295 1295 sys.exit(1)
1296 1296
1297 1297 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1298 1298 not options["noninteractive"])
1299 1299
1300 1300 try:
1301 1301 try:
1302 1302 if cmd not in norepo.split():
1303 1303 path = options["repository"] or ""
1304 1304 repo = hg.repository(ui=u, path=path)
1305 1305 d = lambda: func(u, repo, *args, **cmdoptions)
1306 1306 else:
1307 1307 d = lambda: func(u, *args, **cmdoptions)
1308 1308
1309 1309 if options['profile']:
1310 1310 import hotshot, hotshot.stats
1311 1311 prof = hotshot.Profile("hg.prof")
1312 1312 r = prof.runcall(d)
1313 1313 prof.close()
1314 1314 stats = hotshot.stats.load("hg.prof")
1315 1315 stats.strip_dirs()
1316 1316 stats.sort_stats('time', 'calls')
1317 1317 stats.print_stats(40)
1318 1318 return r
1319 1319 else:
1320 1320 return d()
1321 1321 except:
1322 1322 if options['traceback']:
1323 1323 traceback.print_exc()
1324 1324 raise
1325 1325 except util.CommandError, inst:
1326 1326 u.warn("abort: %s\n" % inst.args)
1327 1327 except hg.RepoError, inst:
1328 1328 u.warn("abort: ", inst, "!\n")
1329 1329 except SignalInterrupt:
1330 1330 u.warn("killed!\n")
1331 1331 except KeyboardInterrupt:
1332 1332 u.warn("interrupted!\n")
1333 1333 except IOError, inst:
1334 1334 if hasattr(inst, "code"):
1335 1335 u.warn("abort: %s\n" % inst)
1336 1336 elif hasattr(inst, "reason"):
1337 1337 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
1338 1338 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1339 1339 if u.debugflag: u.warn("broken pipe\n")
1340 1340 else:
1341 1341 raise
1342 1342 except OSError, inst:
1343 1343 if hasattr(inst, "filename"):
1344 1344 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1345 1345 else:
1346 1346 u.warn("abort: %s\n" % inst.strerror)
1347 1347 except TypeError, inst:
1348 1348 # was this an argument error?
1349 1349 tb = traceback.extract_tb(sys.exc_info()[2])
1350 1350 if len(tb) > 2: # no
1351 1351 raise
1352 1352 u.debug(inst, "\n")
1353 1353 u.warn("%s: invalid arguments\n" % cmd)
1354 1354 help_(u, cmd)
1355 1355
1356 1356 sys.exit(-1)
General Comments 0
You need to be logged in to leave comments. Login now