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