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