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