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