##// END OF EJS Templates
grep: speed up matching, and only return one match per line.
bos@serpentine.internal.keyresearch.com -
r1059:4eab07ef default
parent child Browse files
Show More
@@ -1,1821 +1,1825
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 lock")
11 11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
12 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 def filterfiles(filters, files):
18 18 l = [x for x in files if x in filters]
19 19
20 20 for t in filters:
21 21 if t and t[-1] != "/":
22 22 t += "/"
23 23 l += [x for x in files if x.startswith(t)]
24 24 return l
25 25
26 26 def relpath(repo, args):
27 27 cwd = repo.getcwd()
28 28 if cwd:
29 29 return [util.normpath(os.path.join(cwd, x)) for x in args]
30 30 return args
31 31
32 32 def matchpats(repo, cwd, pats = [], opts = {}, head = ''):
33 33 return util.matcher(repo, cwd, pats or ['.'], opts.get('include'),
34 34 opts.get('exclude'), head)
35 35
36 36 def makewalk(repo, pats, opts, head = ''):
37 37 cwd = repo.getcwd()
38 38 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
39 39 exact = dict(zip(files, files))
40 40 def walk():
41 41 for src, fn in repo.walk(files = files, match = matchfn):
42 42 yield src, fn, util.pathto(cwd, fn), fn in exact
43 43 return files, matchfn, walk()
44 44
45 45 def walk(repo, pats, opts, head = ''):
46 46 files, matchfn, results = makewalk(repo, pats, opts, head)
47 47 for r in results: yield r
48 48
49 49 def walkchangerevs(ui, repo, cwd, pats, opts):
50 50 # This code most commonly needs to iterate backwards over the
51 51 # history it is interested in. Doing so has awful
52 52 # (quadratic-looking) performance, so we use iterators in a
53 53 # "windowed" way. Walk forwards through a window of revisions,
54 54 # yielding them in the desired order, and walk the windows
55 55 # themselves backwards.
56 56 cwd = repo.getcwd()
57 57 if not pats and cwd:
58 58 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
59 59 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
60 60 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
61 61 pats, opts)
62 62 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
63 63 wanted = {}
64 64 slowpath = anypats
65 65 window = 300
66 66 fncache = {}
67 67 if not slowpath and not files:
68 68 # No files, no patterns. Display all revs.
69 69 wanted = dict(zip(revs, revs))
70 70 if not slowpath:
71 71 # Only files, no patterns. Check the history of each file.
72 72 def filerevgen(filelog):
73 73 for i in xrange(filelog.count() - 1, -1, -window):
74 74 revs = []
75 75 for j in xrange(max(0, i - window), i + 1):
76 76 revs.append(filelog.linkrev(filelog.node(j)))
77 77 revs.reverse()
78 78 for rev in revs:
79 79 yield rev
80 80
81 81 minrev, maxrev = min(revs), max(revs)
82 82 for file in files:
83 83 filelog = repo.file(file)
84 84 # A zero count may be a directory or deleted file, so
85 85 # try to find matching entries on the slow path.
86 86 if filelog.count() == 0:
87 87 slowpath = True
88 88 break
89 89 for rev in filerevgen(filelog):
90 90 if rev <= maxrev:
91 91 if rev < minrev: break
92 92 fncache.setdefault(rev, [])
93 93 fncache[rev].append(file)
94 94 wanted[rev] = 1
95 95 if slowpath:
96 96 # The slow path checks files modified in every changeset.
97 97 def changerevgen():
98 98 for i in xrange(repo.changelog.count() - 1, -1, -window):
99 99 for j in xrange(max(0, i - window), i + 1):
100 100 yield j, repo.changelog.read(repo.lookup(str(j)))[3]
101 101
102 102 for rev, changefiles in changerevgen():
103 103 matches = filter(matchfn, changefiles)
104 104 if matches:
105 105 fncache[rev] = matches
106 106 wanted[rev] = 1
107 107
108 108 for i in xrange(0, len(revs), window):
109 109 yield 'window', revs[0] < revs[-1], revs[-1]
110 110 nrevs = [rev for rev in revs[i : min(i + window, len(revs))]
111 111 if rev in wanted]
112 112 srevs = list(nrevs)
113 113 srevs.sort()
114 114 for rev in srevs:
115 115 fns = fncache.get(rev)
116 116 if not fns:
117 117 fns = repo.changelog.read(repo.lookup(str(rev)))[3]
118 118 fns = filter(matchfn, fns)
119 119 yield 'add', rev, fns
120 120 for rev in nrevs:
121 121 yield 'iter', rev, None
122 122
123 123 revrangesep = ':'
124 124
125 125 def revrange(ui, repo, revs, revlog=None):
126 126 if revlog is None:
127 127 revlog = repo.changelog
128 128 revcount = revlog.count()
129 129 def fix(val, defval):
130 130 if not val:
131 131 return defval
132 132 try:
133 133 num = int(val)
134 134 if str(num) != val:
135 135 raise ValueError
136 136 if num < 0:
137 137 num += revcount
138 138 if not (0 <= num < revcount):
139 139 raise ValueError
140 140 except ValueError:
141 141 try:
142 142 num = repo.changelog.rev(repo.lookup(val))
143 143 except KeyError:
144 144 try:
145 145 num = revlog.rev(revlog.lookup(val))
146 146 except KeyError:
147 147 raise util.Abort('invalid revision identifier %s', val)
148 148 return num
149 149 for spec in revs:
150 150 if spec.find(revrangesep) >= 0:
151 151 start, end = spec.split(revrangesep, 1)
152 152 start = fix(start, 0)
153 153 end = fix(end, revcount - 1)
154 154 if end > start:
155 155 end += 1
156 156 step = 1
157 157 else:
158 158 end -= 1
159 159 step = -1
160 160 for rev in xrange(start, end, step):
161 161 yield str(rev)
162 162 else:
163 163 yield str(fix(spec, None))
164 164
165 165 def make_filename(repo, r, pat, node=None,
166 166 total=None, seqno=None, revwidth=None):
167 167 node_expander = {
168 168 'H': lambda: hg.hex(node),
169 169 'R': lambda: str(r.rev(node)),
170 170 'h': lambda: hg.short(node),
171 171 }
172 172 expander = {
173 173 '%': lambda: '%',
174 174 'b': lambda: os.path.basename(repo.root),
175 175 }
176 176
177 177 try:
178 178 if node:
179 179 expander.update(node_expander)
180 180 if node and revwidth is not None:
181 181 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
182 182 if total is not None:
183 183 expander['N'] = lambda: str(total)
184 184 if seqno is not None:
185 185 expander['n'] = lambda: str(seqno)
186 186 if total is not None and seqno is not None:
187 187 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
188 188
189 189 newname = []
190 190 patlen = len(pat)
191 191 i = 0
192 192 while i < patlen:
193 193 c = pat[i]
194 194 if c == '%':
195 195 i += 1
196 196 c = pat[i]
197 197 c = expander[c]()
198 198 newname.append(c)
199 199 i += 1
200 200 return ''.join(newname)
201 201 except KeyError, inst:
202 202 raise util.Abort("invalid format spec '%%%s' in output file name",
203 203 inst.args[0])
204 204
205 205 def make_file(repo, r, pat, node=None,
206 206 total=None, seqno=None, revwidth=None, mode='wb'):
207 207 if not pat or pat == '-':
208 208 if 'w' in mode: return sys.stdout
209 209 else: return sys.stdin
210 210 if hasattr(pat, 'write') and 'w' in mode:
211 211 return pat
212 212 if hasattr(pat, 'read') and 'r' in mode:
213 213 return pat
214 214 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
215 215 mode)
216 216
217 217 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
218 218 changes=None, text=False):
219 219 def date(c):
220 220 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
221 221
222 222 if not changes:
223 223 (c, a, d, u) = repo.changes(node1, node2, files, match = match)
224 224 else:
225 225 (c, a, d, u) = changes
226 226 if files:
227 227 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
228 228
229 229 if not c and not a and not d:
230 230 return
231 231
232 232 if node2:
233 233 change = repo.changelog.read(node2)
234 234 mmap2 = repo.manifest.read(change[0])
235 235 date2 = date(change)
236 236 def read(f):
237 237 return repo.file(f).read(mmap2[f])
238 238 else:
239 239 date2 = time.asctime()
240 240 if not node1:
241 241 node1 = repo.dirstate.parents()[0]
242 242 def read(f):
243 243 return repo.wfile(f).read()
244 244
245 245 if ui.quiet:
246 246 r = None
247 247 else:
248 248 hexfunc = ui.verbose and hg.hex or hg.short
249 249 r = [hexfunc(node) for node in [node1, node2] if node]
250 250
251 251 change = repo.changelog.read(node1)
252 252 mmap = repo.manifest.read(change[0])
253 253 date1 = date(change)
254 254
255 255 for f in c:
256 256 to = None
257 257 if f in mmap:
258 258 to = repo.file(f).read(mmap[f])
259 259 tn = read(f)
260 260 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
261 261 for f in a:
262 262 to = None
263 263 tn = read(f)
264 264 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
265 265 for f in d:
266 266 to = repo.file(f).read(mmap[f])
267 267 tn = None
268 268 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
269 269
270 270 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
271 271 """show a single changeset or file revision"""
272 272 log = repo.changelog
273 273 if changenode is None:
274 274 changenode = log.node(rev)
275 275 elif not rev:
276 276 rev = log.rev(changenode)
277 277
278 278 if ui.quiet:
279 279 ui.write("%d:%s\n" % (rev, hg.short(changenode)))
280 280 return
281 281
282 282 changes = log.read(changenode)
283 283
284 284 t, tz = changes[2].split(' ')
285 285 # a conversion tool was sticking non-integer offsets into repos
286 286 try:
287 287 tz = int(tz)
288 288 except ValueError:
289 289 tz = 0
290 290 date = time.asctime(time.localtime(float(t))) + " %+05d" % (int(tz)/-36)
291 291
292 292 parents = [(log.rev(p), ui.verbose and hg.hex(p) or hg.short(p))
293 293 for p in log.parents(changenode)
294 294 if ui.debugflag or p != hg.nullid]
295 295 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
296 296 parents = []
297 297
298 298 if ui.verbose:
299 299 ui.write("changeset: %d:%s\n" % (rev, hg.hex(changenode)))
300 300 else:
301 301 ui.write("changeset: %d:%s\n" % (rev, hg.short(changenode)))
302 302
303 303 for tag in repo.nodetags(changenode):
304 304 ui.status("tag: %s\n" % tag)
305 305 for parent in parents:
306 306 ui.write("parent: %d:%s\n" % parent)
307 307
308 308 if brinfo and changenode in brinfo:
309 309 br = brinfo[changenode]
310 310 ui.write("branch: %s\n" % " ".join(br))
311 311
312 312 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
313 313 hg.hex(changes[0])))
314 314 ui.status("user: %s\n" % changes[1])
315 315 ui.status("date: %s\n" % date)
316 316
317 317 if ui.debugflag:
318 318 files = repo.changes(log.parents(changenode)[0], changenode)
319 319 for key, value in zip(["files:", "files+:", "files-:"], files):
320 320 if value:
321 321 ui.note("%-12s %s\n" % (key, " ".join(value)))
322 322 else:
323 323 ui.note("files: %s\n" % " ".join(changes[3]))
324 324
325 325 description = changes[4].strip()
326 326 if description:
327 327 if ui.verbose:
328 328 ui.status("description:\n")
329 329 ui.status(description)
330 330 ui.status("\n\n")
331 331 else:
332 332 ui.status("summary: %s\n" % description.splitlines()[0])
333 333 ui.status("\n")
334 334
335 335 def show_version(ui):
336 336 """output version and copyright information"""
337 337 ui.write("Mercurial Distributed SCM (version %s)\n"
338 338 % version.get_version())
339 339 ui.status(
340 340 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
341 341 "This is free software; see the source for copying conditions. "
342 342 "There is NO\nwarranty; "
343 343 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
344 344 )
345 345
346 346 def help_(ui, cmd=None):
347 347 """show help for a given command or all commands"""
348 348 if cmd and cmd != 'shortlist':
349 349 key, i = find(cmd)
350 350 # synopsis
351 351 ui.write("%s\n\n" % i[2])
352 352
353 353 # description
354 354 doc = i[0].__doc__
355 355 if ui.quiet:
356 356 doc = doc.splitlines(0)[0]
357 357 ui.write("%s\n" % doc.rstrip())
358 358
359 359 # aliases
360 360 if not ui.quiet:
361 361 aliases = ', '.join(key.split('|')[1:])
362 362 if aliases:
363 363 ui.write("\naliases: %s\n" % aliases)
364 364
365 365 # options
366 366 if not ui.quiet and i[1]:
367 367 ui.write("\noptions:\n\n")
368 368 for s, l, d, c in i[1]:
369 369 opt = ' '
370 370 if s:
371 371 opt = opt + '-' + s + ' '
372 372 if l:
373 373 opt = opt + '--' + l + ' '
374 374 if d:
375 375 opt = opt + '(' + str(d) + ')'
376 376 ui.write(opt, "\n")
377 377 if c:
378 378 ui.write(' %s\n' % c)
379 379
380 380 else:
381 381 # program name
382 382 if ui.verbose:
383 383 show_version(ui)
384 384 else:
385 385 ui.status("Mercurial Distributed SCM\n")
386 386 ui.status('\n')
387 387
388 388 # list of commands
389 389 if cmd == "shortlist":
390 390 ui.status('basic commands (use "hg help" '
391 391 'for the full list or option "-v" for details):\n\n')
392 392 elif ui.verbose:
393 393 ui.status('list of commands:\n\n')
394 394 else:
395 395 ui.status('list of commands (use "hg help -v" '
396 396 'to show aliases and global options):\n\n')
397 397
398 398 h = {}
399 399 cmds = {}
400 400 for c, e in table.items():
401 401 f = c.split("|")[0]
402 402 if cmd == "shortlist" and not f.startswith("^"):
403 403 continue
404 404 f = f.lstrip("^")
405 405 if not ui.debugflag and f.startswith("debug"):
406 406 continue
407 407 d = ""
408 408 if e[0].__doc__:
409 409 d = e[0].__doc__.splitlines(0)[0].rstrip()
410 410 h[f] = d
411 411 cmds[f]=c.lstrip("^")
412 412
413 413 fns = h.keys()
414 414 fns.sort()
415 415 m = max(map(len, fns))
416 416 for f in fns:
417 417 if ui.verbose:
418 418 commands = cmds[f].replace("|",", ")
419 419 ui.write(" %s:\n %s\n"%(commands,h[f]))
420 420 else:
421 421 ui.write(' %-*s %s\n' % (m, f, h[f]))
422 422
423 423 # global options
424 424 if ui.verbose:
425 425 ui.write("\nglobal options:\n\n")
426 426 for s, l, d, c in globalopts:
427 427 opt = ' '
428 428 if s:
429 429 opt = opt + '-' + s + ' '
430 430 if l:
431 431 opt = opt + '--' + l + ' '
432 432 if d:
433 433 opt = opt + '(' + str(d) + ')'
434 434 ui.write(opt, "\n")
435 435 if c:
436 436 ui.write(' %s\n' % c)
437 437
438 438 # Commands start here, listed alphabetically
439 439
440 440 def add(ui, repo, *pats, **opts):
441 441 '''add the specified files on the next commit'''
442 442 names = []
443 443 for src, abs, rel, exact in walk(repo, pats, opts):
444 444 if exact:
445 445 names.append(abs)
446 446 elif repo.dirstate.state(abs) == '?':
447 447 ui.status('adding %s\n' % rel)
448 448 names.append(abs)
449 449 repo.add(names)
450 450
451 451 def addremove(ui, repo, *pats, **opts):
452 452 """add all new files, delete all missing files"""
453 453 add, remove = [], []
454 454 for src, abs, rel, exact in walk(repo, pats, opts):
455 455 if src == 'f' and repo.dirstate.state(abs) == '?':
456 456 add.append(abs)
457 457 if not exact: ui.status('adding ', rel, '\n')
458 458 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
459 459 remove.append(abs)
460 460 if not exact: ui.status('removing ', rel, '\n')
461 461 repo.add(add)
462 462 repo.remove(remove)
463 463
464 464 def annotate(ui, repo, *pats, **opts):
465 465 """show changeset information per file line"""
466 466 def getnode(rev):
467 467 return hg.short(repo.changelog.node(rev))
468 468
469 469 def getname(rev):
470 470 try:
471 471 return bcache[rev]
472 472 except KeyError:
473 473 cl = repo.changelog.read(repo.changelog.node(rev))
474 474 name = cl[1]
475 475 f = name.find('@')
476 476 if f >= 0:
477 477 name = name[:f]
478 478 f = name.find('<')
479 479 if f >= 0:
480 480 name = name[f+1:]
481 481 bcache[rev] = name
482 482 return name
483 483
484 484 if not pats:
485 485 raise util.Abort('at least one file name or pattern required')
486 486
487 487 bcache = {}
488 488 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
489 489 if not opts['user'] and not opts['changeset']:
490 490 opts['number'] = 1
491 491
492 492 if opts['rev']:
493 493 node = repo.changelog.lookup(opts['rev'])
494 494 else:
495 495 node = repo.dirstate.parents()[0]
496 496 change = repo.changelog.read(node)
497 497 mmap = repo.manifest.read(change[0])
498 498
499 499 for src, abs, rel, exact in walk(repo, pats, opts):
500 500 if abs not in mmap:
501 501 ui.warn("warning: %s is not in the repository!\n" % rel)
502 502 continue
503 503
504 504 f = repo.file(abs)
505 505 if not opts['text'] and util.binary(f.read(mmap[abs])):
506 506 ui.write("%s: binary file\n" % rel)
507 507 continue
508 508
509 509 lines = f.annotate(mmap[abs])
510 510 pieces = []
511 511
512 512 for o, f in opmap:
513 513 if opts[o]:
514 514 l = [f(n) for n, dummy in lines]
515 515 if l:
516 516 m = max(map(len, l))
517 517 pieces.append(["%*s" % (m, x) for x in l])
518 518
519 519 if pieces:
520 520 for p, l in zip(zip(*pieces), lines):
521 521 ui.write("%s: %s" % (" ".join(p), l[1]))
522 522
523 523 def cat(ui, repo, file1, rev=None, **opts):
524 524 """output the latest or given revision of a file"""
525 525 r = repo.file(relpath(repo, [file1])[0])
526 526 if rev:
527 527 try:
528 528 # assume all revision numbers are for changesets
529 529 n = repo.lookup(rev)
530 530 change = repo.changelog.read(n)
531 531 m = repo.manifest.read(change[0])
532 532 n = m[relpath(repo, [file1])[0]]
533 533 except hg.RepoError, KeyError:
534 534 n = r.lookup(rev)
535 535 else:
536 536 n = r.tip()
537 537 fp = make_file(repo, r, opts['output'], node=n)
538 538 fp.write(r.read(n))
539 539
540 540 def clone(ui, source, dest=None, **opts):
541 541 """make a copy of an existing repository"""
542 542 if dest is None:
543 543 dest = os.path.basename(os.path.normpath(source))
544 544
545 545 if os.path.exists(dest):
546 546 ui.warn("abort: destination '%s' already exists\n" % dest)
547 547 return 1
548 548
549 549 dest = os.path.realpath(dest)
550 550
551 551 class Dircleanup:
552 552 def __init__(self, dir_):
553 553 self.rmtree = shutil.rmtree
554 554 self.dir_ = dir_
555 555 os.mkdir(dir_)
556 556 def close(self):
557 557 self.dir_ = None
558 558 def __del__(self):
559 559 if self.dir_:
560 560 self.rmtree(self.dir_, True)
561 561
562 562 if opts['ssh']:
563 563 ui.setconfig("ui", "ssh", opts['ssh'])
564 564 if opts['remotecmd']:
565 565 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
566 566
567 567 d = Dircleanup(dest)
568 568 source = ui.expandpath(source)
569 569 abspath = source
570 570 other = hg.repository(ui, source)
571 571
572 572 if other.dev() != -1:
573 573 abspath = os.path.abspath(source)
574 574 copyfile = (os.stat(dest).st_dev == other.dev()
575 575 and getattr(os, 'link', None) or shutil.copy2)
576 576 if copyfile is not shutil.copy2:
577 577 ui.note("cloning by hardlink\n")
578 578 # we use a lock here because because we're not nicely ordered
579 579 l = lock.lock(os.path.join(source, ".hg", "lock"))
580 580
581 581 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
582 582 copyfile)
583 583 try:
584 584 os.unlink(os.path.join(dest, ".hg", "dirstate"))
585 585 except OSError:
586 586 pass
587 587
588 588 repo = hg.repository(ui, dest)
589 589
590 590 else:
591 591 repo = hg.repository(ui, dest, create=1)
592 592 repo.pull(other)
593 593
594 594 f = repo.opener("hgrc", "w")
595 595 f.write("[paths]\n")
596 596 f.write("default = %s\n" % abspath)
597 597
598 598 if not opts['noupdate']:
599 599 update(ui, repo)
600 600
601 601 d.close()
602 602
603 603 def commit(ui, repo, *pats, **opts):
604 604 """commit the specified files or all outstanding changes"""
605 605 if opts['text']:
606 606 ui.warn("Warning: -t and --text is deprecated,"
607 607 " please use -m or --message instead.\n")
608 608 message = opts['message'] or opts['text']
609 609 logfile = opts['logfile']
610 610 if not message and logfile:
611 611 try:
612 612 if logfile == '-':
613 613 message = sys.stdin.read()
614 614 else:
615 615 message = open(logfile).read()
616 616 except IOError, why:
617 617 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
618 618
619 619 if opts['addremove']:
620 620 addremove(ui, repo, *pats, **opts)
621 621 cwd = repo.getcwd()
622 622 if not pats and cwd:
623 623 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
624 624 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
625 625 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
626 626 pats, opts)
627 627 if pats:
628 628 c, a, d, u = repo.changes(files = fns, match = match)
629 629 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
630 630 else:
631 631 files = []
632 632 repo.commit(files, message, opts['user'], opts['date'], match)
633 633
634 634 def copy(ui, repo, source, dest):
635 635 """mark a file as copied or renamed for the next commit"""
636 636 return repo.copy(*relpath(repo, (source, dest)))
637 637
638 638 def debugcheckstate(ui, repo):
639 639 """validate the correctness of the current dirstate"""
640 640 parent1, parent2 = repo.dirstate.parents()
641 641 repo.dirstate.read()
642 642 dc = repo.dirstate.map
643 643 keys = dc.keys()
644 644 keys.sort()
645 645 m1n = repo.changelog.read(parent1)[0]
646 646 m2n = repo.changelog.read(parent2)[0]
647 647 m1 = repo.manifest.read(m1n)
648 648 m2 = repo.manifest.read(m2n)
649 649 errors = 0
650 650 for f in dc:
651 651 state = repo.dirstate.state(f)
652 652 if state in "nr" and f not in m1:
653 653 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
654 654 errors += 1
655 655 if state in "a" and f in m1:
656 656 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
657 657 errors += 1
658 658 if state in "m" and f not in m1 and f not in m2:
659 659 ui.warn("%s in state %s, but not in either manifest\n" %
660 660 (f, state))
661 661 errors += 1
662 662 for f in m1:
663 663 state = repo.dirstate.state(f)
664 664 if state not in "nrm":
665 665 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
666 666 errors += 1
667 667 if errors:
668 668 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
669 669
670 670 def debugconfig(ui):
671 671 try:
672 672 repo = hg.repository(ui)
673 673 except: pass
674 674 for section, name, value in ui.walkconfig():
675 675 ui.write('%s.%s=%s\n' % (section, name, value))
676 676
677 677 def debugstate(ui, repo):
678 678 """show the contents of the current dirstate"""
679 679 repo.dirstate.read()
680 680 dc = repo.dirstate.map
681 681 keys = dc.keys()
682 682 keys.sort()
683 683 for file_ in keys:
684 684 ui.write("%c %3o %10d %s %s\n"
685 685 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
686 686 time.strftime("%x %X",
687 687 time.localtime(dc[file_][3])), file_))
688 688
689 689 def debugdata(ui, file_, rev):
690 690 """dump the contents of an data file revision"""
691 691 r = hg.revlog(hg.opener(""), file_[:-2] + ".i", file_)
692 692 ui.write(r.revision(r.lookup(rev)))
693 693
694 694 def debugindex(ui, file_):
695 695 """dump the contents of an index file"""
696 696 r = hg.revlog(hg.opener(""), file_, "")
697 697 ui.write(" rev offset length base linkrev" +
698 698 " nodeid p1 p2\n")
699 699 for i in range(r.count()):
700 700 e = r.index[i]
701 701 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
702 702 i, e[0], e[1], e[2], e[3],
703 703 hg.short(e[6]), hg.short(e[4]), hg.short(e[5])))
704 704
705 705 def debugindexdot(ui, file_):
706 706 """dump an index DAG as a .dot file"""
707 707 r = hg.revlog(hg.opener(""), file_, "")
708 708 ui.write("digraph G {\n")
709 709 for i in range(r.count()):
710 710 e = r.index[i]
711 711 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
712 712 if e[5] != hg.nullid:
713 713 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
714 714 ui.write("}\n")
715 715
716 716 def debugwalk(ui, repo, *pats, **opts):
717 717 items = list(walk(repo, pats, opts))
718 718 if not items: return
719 719 fmt = '%%s %%-%ds %%-%ds %%s\n' % (
720 720 max([len(abs) for (src, abs, rel, exact) in items]),
721 721 max([len(rel) for (src, abs, rel, exact) in items]))
722 722 exactly = {True: 'exact', False: ''}
723 723 for src, abs, rel, exact in items:
724 724 ui.write(fmt % (src, abs, rel, exactly[exact]))
725 725
726 726 def diff(ui, repo, *pats, **opts):
727 727 """diff working directory (or selected files)"""
728 728 node1, node2 = None, None
729 729 revs = [repo.lookup(x) for x in opts['rev']]
730 730
731 731 if len(revs) > 0:
732 732 node1 = revs[0]
733 733 if len(revs) > 1:
734 734 node2 = revs[1]
735 735 if len(revs) > 2:
736 736 raise util.Abort("too many revisions to diff")
737 737
738 738 files = []
739 739 match = util.always
740 740 if pats:
741 741 roots, match, results = makewalk(repo, pats, opts)
742 742 for src, abs, rel, exact in results:
743 743 files.append(abs)
744 744
745 745 dodiff(sys.stdout, ui, repo, node1, node2, files, match=match,
746 746 text=opts['text'])
747 747
748 748 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
749 749 node = repo.lookup(changeset)
750 750 prev, other = repo.changelog.parents(node)
751 751 change = repo.changelog.read(node)
752 752
753 753 fp = make_file(repo, repo.changelog, opts['output'],
754 754 node=node, total=total, seqno=seqno,
755 755 revwidth=revwidth)
756 756 if fp != sys.stdout:
757 757 ui.note("%s\n" % fp.name)
758 758
759 759 fp.write("# HG changeset patch\n")
760 760 fp.write("# User %s\n" % change[1])
761 761 fp.write("# Node ID %s\n" % hg.hex(node))
762 762 fp.write("# Parent %s\n" % hg.hex(prev))
763 763 if other != hg.nullid:
764 764 fp.write("# Parent %s\n" % hg.hex(other))
765 765 fp.write(change[4].rstrip())
766 766 fp.write("\n\n")
767 767
768 768 dodiff(fp, ui, repo, prev, node, text=opts['text'])
769 769 if fp != sys.stdout: fp.close()
770 770
771 771 def export(ui, repo, *changesets, **opts):
772 772 """dump the header and diffs for one or more changesets"""
773 773 if not changesets:
774 774 raise util.Abort("export requires at least one changeset")
775 775 seqno = 0
776 776 revs = list(revrange(ui, repo, changesets))
777 777 total = len(revs)
778 778 revwidth = max(len(revs[0]), len(revs[-1]))
779 779 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
780 780 for cset in revs:
781 781 seqno += 1
782 782 doexport(ui, repo, cset, seqno, total, revwidth, opts)
783 783
784 784 def forget(ui, repo, *pats, **opts):
785 785 """don't add the specified files on the next commit"""
786 786 forget = []
787 787 for src, abs, rel, exact in walk(repo, pats, opts):
788 788 if repo.dirstate.state(abs) == 'a':
789 789 forget.append(abs)
790 790 if not exact: ui.status('forgetting ', rel, '\n')
791 791 repo.forget(forget)
792 792
793 793 def grep(ui, repo, pattern = None, *pats, **opts):
794 794 if pattern is None: pattern = opts['regexp']
795 795 if not pattern: raise util.Abort('no pattern to search for')
796 796 reflags = 0
797 797 if opts['ignore_case']: reflags |= re.I
798 798 regexp = re.compile(pattern, reflags)
799 799 sep, end = ':', '\n'
800 800 if opts['null'] or opts['print0']: sep = end = '\0'
801 801
802 802 fcache = {}
803 803 def getfile(fn):
804 804 if fn not in fcache:
805 805 fcache[fn] = repo.file(fn)
806 806 return fcache[fn]
807 807
808 808 def matchlines(body):
809 # massively inefficient. rewrite.
810 for match in regexp.finditer(body):
811 start, end = match.span()
812 lnum = body.count('\n', 0, start) + 1
813 lstart = body.rfind('\n', 0, start) + 1
814 lend = body.find('\n', end)
815 yield lnum, start - lstart, end - lstart, body[lstart:lend]
809 begin = 0
810 linenum = 0
811 while True:
812 match = regexp.search(body, begin)
813 if not match: break
814 mstart, mend = match.span()
815 linenum += body.count('\n', begin, mstart) + 1
816 lstart = body.rfind('\n', begin, mstart) + 1 or begin
817 lend = body.find('\n', mend)
818 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
819 begin = lend + 1
816 820
817 821 class linestate:
818 822 def __init__(self, line, linenum, colstart, colend):
819 823 self.line = line
820 824 self.linenum = linenum
821 825 self.colstart = colstart
822 826 self.colend = colend
823 827 def __eq__(self, other): return self.line == other.line
824 828 def __hash__(self): return hash(self.line)
825 829
826 830 matches = {}
827 831 def grepbody(fn, rev, body):
828 832 matches[rev].setdefault(fn, {})
829 833 m = matches[rev][fn]
830 834 for lnum, cstart, cend, line in matchlines(body):
831 835 s = linestate(line, lnum, cstart, cend)
832 836 m[s] = s
833 837
834 838 prev = {}
835 839 def display(fn, rev, states, prevstates):
836 840 diff = list(set(states).symmetric_difference(set(prevstates)))
837 841 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
838 842 for l in diff:
839 843 if incrementing:
840 844 change = ((l in prevstates) and '-') or '+'
841 845 r = rev
842 846 else:
843 847 change = ((l in states) and '-') or '+'
844 848 r = prev[fn]
845 849 ui.write('%s:%s:%s:%s%s\n' % (fn, r, l.linenum, change, l.line))
846 850
847 851 fstate = {}
848 852 for st, rev, fns in walkchangerevs(ui, repo, repo.getcwd(), pats, opts):
849 853 if st == 'window':
850 854 incrementing = rev
851 855 matches.clear()
852 856 elif st == 'add':
853 857 change = repo.changelog.read(repo.lookup(str(rev)))
854 858 mf = repo.manifest.read(change[0])
855 859 matches[rev] = {}
856 860 for fn in fns:
857 861 fstate.setdefault(fn, {})
858 862 try:
859 863 grepbody(fn, rev, getfile(fn).read(mf[fn]))
860 864 except KeyError:
861 865 pass
862 866 elif st == 'iter':
863 867 states = matches[rev].items()
864 868 states.sort()
865 869 for fn, m in states:
866 870 if incrementing or fstate[fn]:
867 871 display(fn, rev, m, fstate[fn])
868 872 fstate[fn] = m
869 873 prev[fn] = rev
870 874
871 875 if not incrementing:
872 876 fstate = fstate.items()
873 877 fstate.sort()
874 878 for fn, state in fstate:
875 879 display(fn, rev, {}, state)
876 880
877 881 def heads(ui, repo, **opts):
878 882 """show current repository heads"""
879 883 heads = repo.changelog.heads()
880 884 br = None
881 885 if opts['branches']:
882 886 br = repo.branchlookup(heads)
883 887 for n in repo.changelog.heads():
884 888 show_changeset(ui, repo, changenode=n, brinfo=br)
885 889
886 890 def identify(ui, repo):
887 891 """print information about the working copy"""
888 892 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
889 893 if not parents:
890 894 ui.write("unknown\n")
891 895 return
892 896
893 897 hexfunc = ui.verbose and hg.hex or hg.short
894 898 (c, a, d, u) = repo.changes()
895 899 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
896 900 (c or a or d) and "+" or "")]
897 901
898 902 if not ui.quiet:
899 903 # multiple tags for a single parent separated by '/'
900 904 parenttags = ['/'.join(tags)
901 905 for tags in map(repo.nodetags, parents) if tags]
902 906 # tags for multiple parents separated by ' + '
903 907 if parenttags:
904 908 output.append(' + '.join(parenttags))
905 909
906 910 ui.write("%s\n" % ' '.join(output))
907 911
908 912 def import_(ui, repo, patch1, *patches, **opts):
909 913 """import an ordered set of patches"""
910 914 patches = (patch1,) + patches
911 915
912 916 if not opts['force']:
913 917 (c, a, d, u) = repo.changes()
914 918 if c or a or d:
915 919 ui.warn("abort: outstanding uncommitted changes!\n")
916 920 return 1
917 921
918 922 d = opts["base"]
919 923 strip = opts["strip"]
920 924
921 925 for patch in patches:
922 926 ui.status("applying %s\n" % patch)
923 927 pf = os.path.join(d, patch)
924 928
925 929 message = []
926 930 user = None
927 931 hgpatch = False
928 932 for line in file(pf):
929 933 line = line.rstrip()
930 934 if line.startswith("--- ") or line.startswith("diff -r"):
931 935 break
932 936 elif hgpatch:
933 937 # parse values when importing the result of an hg export
934 938 if line.startswith("# User "):
935 939 user = line[7:]
936 940 ui.debug('User: %s\n' % user)
937 941 elif not line.startswith("# ") and line:
938 942 message.append(line)
939 943 hgpatch = False
940 944 elif line == '# HG changeset patch':
941 945 hgpatch = True
942 946 message = [] # We may have collected garbage
943 947 else:
944 948 message.append(line)
945 949
946 950 # make sure message isn't empty
947 951 if not message:
948 952 message = "imported patch %s\n" % patch
949 953 else:
950 954 message = "%s\n" % '\n'.join(message)
951 955 ui.debug('message:\n%s\n' % message)
952 956
953 957 f = os.popen("patch -p%d < '%s'" % (strip, pf))
954 958 files = []
955 959 for l in f.read().splitlines():
956 960 l.rstrip('\r\n');
957 961 ui.status("%s\n" % l)
958 962 if l.startswith('patching file '):
959 963 pf = l[14:]
960 964 if pf not in files:
961 965 files.append(pf)
962 966 patcherr = f.close()
963 967 if patcherr:
964 968 raise util.Abort("patch failed")
965 969
966 970 if len(files) > 0:
967 971 addremove(ui, repo, *files)
968 972 repo.commit(files, message, user)
969 973
970 974 def incoming(ui, repo, source="default"):
971 975 """show new changesets found in source"""
972 976 source = ui.expandpath(source)
973 977 other = hg.repository(ui, source)
974 978 if not other.local():
975 979 ui.warn("abort: incoming doesn't work for remote"
976 980 + " repositories yet, sorry!\n")
977 981 return 1
978 982 o = repo.findincoming(other)
979 983 if not o:
980 984 return
981 985 o = other.newer(o)
982 986 o.reverse()
983 987 for n in o:
984 988 show_changeset(ui, other, changenode=n)
985 989
986 990 def init(ui, dest="."):
987 991 """create a new repository in the given directory"""
988 992 if not os.path.exists(dest):
989 993 os.mkdir(dest)
990 994 hg.repository(ui, dest, create=1)
991 995
992 996 def locate(ui, repo, *pats, **opts):
993 997 """locate files matching specific patterns"""
994 998 end = '\n'
995 999 if opts['print0']: end = '\0'
996 1000
997 1001 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
998 1002 if repo.dirstate.state(abs) == '?': continue
999 1003 if opts['fullpath']:
1000 1004 ui.write(os.path.join(repo.root, abs), end)
1001 1005 else:
1002 1006 ui.write(rel, end)
1003 1007
1004 1008 def log(ui, repo, *pats, **opts):
1005 1009 """show revision history of entire repository or files"""
1006 1010 class dui:
1007 1011 # Implement and delegate some ui protocol. Save hunks of
1008 1012 # output for later display in the desired order.
1009 1013 def __init__(self, ui):
1010 1014 self.ui = ui
1011 1015 self.hunk = {}
1012 1016 def bump(self, rev):
1013 1017 self.rev = rev
1014 1018 self.hunk[rev] = []
1015 1019 def note(self, *args):
1016 1020 if self.verbose: self.write(*args)
1017 1021 def status(self, *args):
1018 1022 if not self.quiet: self.write(*args)
1019 1023 def write(self, *args):
1020 1024 self.hunk[self.rev].append(args)
1021 1025 def __getattr__(self, key):
1022 1026 return getattr(self.ui, key)
1023 1027 cwd = repo.getcwd()
1024 1028 if not pats and cwd:
1025 1029 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1026 1030 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1027 1031 for st, rev, fns in walkchangerevs(ui, repo, (pats and cwd) or '', pats,
1028 1032 opts):
1029 1033 if st == 'window':
1030 1034 du = dui(ui)
1031 1035 elif st == 'add':
1032 1036 du.bump(rev)
1033 1037 show_changeset(du, repo, rev)
1034 1038 if opts['patch']:
1035 1039 changenode = repo.changelog.node(rev)
1036 1040 prev, other = repo.changelog.parents(changenode)
1037 1041 dodiff(du, du, repo, prev, changenode, fns)
1038 1042 du.write("\n\n")
1039 1043 elif st == 'iter':
1040 1044 for args in du.hunk[rev]:
1041 1045 ui.write(*args)
1042 1046
1043 1047 def manifest(ui, repo, rev=None):
1044 1048 """output the latest or given revision of the project manifest"""
1045 1049 if rev:
1046 1050 try:
1047 1051 # assume all revision numbers are for changesets
1048 1052 n = repo.lookup(rev)
1049 1053 change = repo.changelog.read(n)
1050 1054 n = change[0]
1051 1055 except hg.RepoError:
1052 1056 n = repo.manifest.lookup(rev)
1053 1057 else:
1054 1058 n = repo.manifest.tip()
1055 1059 m = repo.manifest.read(n)
1056 1060 mf = repo.manifest.readflags(n)
1057 1061 files = m.keys()
1058 1062 files.sort()
1059 1063
1060 1064 for f in files:
1061 1065 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
1062 1066
1063 1067 def outgoing(ui, repo, dest="default-push"):
1064 1068 """show changesets not found in destination"""
1065 1069 dest = ui.expandpath(dest)
1066 1070 other = hg.repository(ui, dest)
1067 1071 o = repo.findoutgoing(other)
1068 1072 o = repo.newer(o)
1069 1073 o.reverse()
1070 1074 for n in o:
1071 1075 show_changeset(ui, repo, changenode=n)
1072 1076
1073 1077 def parents(ui, repo, rev=None):
1074 1078 """show the parents of the working dir or revision"""
1075 1079 if rev:
1076 1080 p = repo.changelog.parents(repo.lookup(rev))
1077 1081 else:
1078 1082 p = repo.dirstate.parents()
1079 1083
1080 1084 for n in p:
1081 1085 if n != hg.nullid:
1082 1086 show_changeset(ui, repo, changenode=n)
1083 1087
1084 1088 def paths(ui, search = None):
1085 1089 """show definition of symbolic path names"""
1086 1090 try:
1087 1091 repo = hg.repository(ui=ui)
1088 1092 except:
1089 1093 pass
1090 1094
1091 1095 if search:
1092 1096 for name, path in ui.configitems("paths"):
1093 1097 if name == search:
1094 1098 ui.write("%s\n" % path)
1095 1099 return
1096 1100 ui.warn("not found!\n")
1097 1101 return 1
1098 1102 else:
1099 1103 for name, path in ui.configitems("paths"):
1100 1104 ui.write("%s = %s\n" % (name, path))
1101 1105
1102 1106 def pull(ui, repo, source="default", **opts):
1103 1107 """pull changes from the specified source"""
1104 1108 source = ui.expandpath(source)
1105 1109 ui.status('pulling from %s\n' % (source))
1106 1110
1107 1111 if opts['ssh']:
1108 1112 ui.setconfig("ui", "ssh", opts['ssh'])
1109 1113 if opts['remotecmd']:
1110 1114 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1111 1115
1112 1116 other = hg.repository(ui, source)
1113 1117 r = repo.pull(other)
1114 1118 if not r:
1115 1119 if opts['update']:
1116 1120 return update(ui, repo)
1117 1121 else:
1118 1122 ui.status("(run 'hg update' to get a working copy)\n")
1119 1123
1120 1124 return r
1121 1125
1122 1126 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1123 1127 """push changes to the specified destination"""
1124 1128 dest = ui.expandpath(dest)
1125 1129 ui.status('pushing to %s\n' % (dest))
1126 1130
1127 1131 if ssh:
1128 1132 ui.setconfig("ui", "ssh", ssh)
1129 1133 if remotecmd:
1130 1134 ui.setconfig("ui", "remotecmd", remotecmd)
1131 1135
1132 1136 other = hg.repository(ui, dest)
1133 1137 r = repo.push(other, force)
1134 1138 return r
1135 1139
1136 1140 def rawcommit(ui, repo, *flist, **rc):
1137 1141 "raw commit interface"
1138 1142 if rc['text']:
1139 1143 ui.warn("Warning: -t and --text is deprecated,"
1140 1144 " please use -m or --message instead.\n")
1141 1145 message = rc['message'] or rc['text']
1142 1146 if not message and rc['logfile']:
1143 1147 try:
1144 1148 message = open(rc['logfile']).read()
1145 1149 except IOError:
1146 1150 pass
1147 1151 if not message and not rc['logfile']:
1148 1152 ui.warn("abort: missing commit message\n")
1149 1153 return 1
1150 1154
1151 1155 files = relpath(repo, list(flist))
1152 1156 if rc['files']:
1153 1157 files += open(rc['files']).read().splitlines()
1154 1158
1155 1159 rc['parent'] = map(repo.lookup, rc['parent'])
1156 1160
1157 1161 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1158 1162
1159 1163 def recover(ui, repo):
1160 1164 """roll back an interrupted transaction"""
1161 1165 repo.recover()
1162 1166
1163 1167 def remove(ui, repo, file1, *files):
1164 1168 """remove the specified files on the next commit"""
1165 1169 repo.remove(relpath(repo, (file1,) + files))
1166 1170
1167 1171 def revert(ui, repo, *names, **opts):
1168 1172 """revert modified files or dirs back to their unmodified states"""
1169 1173 node = opts['rev'] and repo.lookup(opts['rev']) or \
1170 1174 repo.dirstate.parents()[0]
1171 1175 root = os.path.realpath(repo.root)
1172 1176
1173 1177 def trimpath(p):
1174 1178 p = os.path.realpath(p)
1175 1179 if p.startswith(root):
1176 1180 rest = p[len(root):]
1177 1181 if not rest:
1178 1182 return rest
1179 1183 if p.startswith(os.sep):
1180 1184 return rest[1:]
1181 1185 return p
1182 1186
1183 1187 relnames = map(trimpath, names or [os.getcwd()])
1184 1188 chosen = {}
1185 1189
1186 1190 def choose(name):
1187 1191 def body(name):
1188 1192 for r in relnames:
1189 1193 if not name.startswith(r):
1190 1194 continue
1191 1195 rest = name[len(r):]
1192 1196 if not rest:
1193 1197 return r, True
1194 1198 depth = rest.count(os.sep)
1195 1199 if not r:
1196 1200 if depth == 0 or not opts['nonrecursive']:
1197 1201 return r, True
1198 1202 elif rest[0] == os.sep:
1199 1203 if depth == 1 or not opts['nonrecursive']:
1200 1204 return r, True
1201 1205 return None, False
1202 1206 relname, ret = body(name)
1203 1207 if ret:
1204 1208 chosen[relname] = 1
1205 1209 return ret
1206 1210
1207 1211 r = repo.update(node, False, True, choose, False)
1208 1212 for n in relnames:
1209 1213 if n not in chosen:
1210 1214 ui.warn('error: no matches for %s\n' % n)
1211 1215 r = 1
1212 1216 sys.stdout.flush()
1213 1217 return r
1214 1218
1215 1219 def root(ui, repo):
1216 1220 """print the root (top) of the current working dir"""
1217 1221 ui.write(repo.root + "\n")
1218 1222
1219 1223 def serve(ui, repo, **opts):
1220 1224 """export the repository via HTTP"""
1221 1225
1222 1226 if opts["stdio"]:
1223 1227 fin, fout = sys.stdin, sys.stdout
1224 1228 sys.stdout = sys.stderr
1225 1229
1226 1230 def getarg():
1227 1231 argline = fin.readline()[:-1]
1228 1232 arg, l = argline.split()
1229 1233 val = fin.read(int(l))
1230 1234 return arg, val
1231 1235 def respond(v):
1232 1236 fout.write("%d\n" % len(v))
1233 1237 fout.write(v)
1234 1238 fout.flush()
1235 1239
1236 1240 lock = None
1237 1241
1238 1242 while 1:
1239 1243 cmd = fin.readline()[:-1]
1240 1244 if cmd == '':
1241 1245 return
1242 1246 if cmd == "heads":
1243 1247 h = repo.heads()
1244 1248 respond(" ".join(map(hg.hex, h)) + "\n")
1245 1249 if cmd == "lock":
1246 1250 lock = repo.lock()
1247 1251 respond("")
1248 1252 if cmd == "unlock":
1249 1253 if lock:
1250 1254 lock.release()
1251 1255 lock = None
1252 1256 respond("")
1253 1257 elif cmd == "branches":
1254 1258 arg, nodes = getarg()
1255 1259 nodes = map(hg.bin, nodes.split(" "))
1256 1260 r = []
1257 1261 for b in repo.branches(nodes):
1258 1262 r.append(" ".join(map(hg.hex, b)) + "\n")
1259 1263 respond("".join(r))
1260 1264 elif cmd == "between":
1261 1265 arg, pairs = getarg()
1262 1266 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
1263 1267 r = []
1264 1268 for b in repo.between(pairs):
1265 1269 r.append(" ".join(map(hg.hex, b)) + "\n")
1266 1270 respond("".join(r))
1267 1271 elif cmd == "changegroup":
1268 1272 nodes = []
1269 1273 arg, roots = getarg()
1270 1274 nodes = map(hg.bin, roots.split(" "))
1271 1275
1272 1276 cg = repo.changegroup(nodes)
1273 1277 while 1:
1274 1278 d = cg.read(4096)
1275 1279 if not d:
1276 1280 break
1277 1281 fout.write(d)
1278 1282
1279 1283 fout.flush()
1280 1284
1281 1285 elif cmd == "addchangegroup":
1282 1286 if not lock:
1283 1287 respond("not locked")
1284 1288 continue
1285 1289 respond("")
1286 1290
1287 1291 r = repo.addchangegroup(fin)
1288 1292 respond("")
1289 1293
1290 1294 optlist = "name templates style address port ipv6 accesslog errorlog"
1291 1295 for o in optlist.split():
1292 1296 if opts[o]:
1293 1297 ui.setconfig("web", o, opts[o])
1294 1298
1295 1299 httpd = hgweb.create_server(repo)
1296 1300
1297 1301 if ui.verbose:
1298 1302 addr, port = httpd.socket.getsockname()
1299 1303 if addr == '0.0.0.0':
1300 1304 addr = socket.gethostname()
1301 1305 else:
1302 1306 try:
1303 1307 addr = socket.gethostbyaddr(addr)[0]
1304 1308 except socket.error:
1305 1309 pass
1306 1310 if port != 80:
1307 1311 ui.status('listening at http://%s:%d/\n' % (addr, port))
1308 1312 else:
1309 1313 ui.status('listening at http://%s/\n' % addr)
1310 1314 httpd.serve_forever()
1311 1315
1312 1316 def status(ui, repo, *pats, **opts):
1313 1317 '''show changed files in the working directory
1314 1318
1315 1319 M = modified
1316 1320 A = added
1317 1321 R = removed
1318 1322 ? = not tracked
1319 1323 '''
1320 1324
1321 1325 cwd = repo.getcwd()
1322 1326 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1323 1327 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1324 1328 for n in repo.changes(files=files, match=matchfn)]
1325 1329
1326 1330 changetypes = [('modified', 'M', c),
1327 1331 ('added', 'A', a),
1328 1332 ('removed', 'R', d),
1329 1333 ('unknown', '?', u)]
1330 1334
1331 1335 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1332 1336 or changetypes):
1333 1337 for f in changes:
1334 1338 ui.write("%s %s\n" % (char, f))
1335 1339
1336 1340 def tag(ui, repo, name, rev=None, **opts):
1337 1341 """add a tag for the current tip or a given revision"""
1338 1342 if opts['text']:
1339 1343 ui.warn("Warning: -t and --text is deprecated,"
1340 1344 " please use -m or --message instead.\n")
1341 1345 if name == "tip":
1342 1346 ui.warn("abort: 'tip' is a reserved name!\n")
1343 1347 return -1
1344 1348 if rev:
1345 1349 r = hg.hex(repo.lookup(rev))
1346 1350 else:
1347 1351 r = hg.hex(repo.changelog.tip())
1348 1352
1349 1353 if name.find(revrangesep) >= 0:
1350 1354 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1351 1355 return -1
1352 1356
1353 1357 if opts['local']:
1354 1358 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1355 1359 return
1356 1360
1357 1361 (c, a, d, u) = repo.changes()
1358 1362 for x in (c, a, d, u):
1359 1363 if ".hgtags" in x:
1360 1364 ui.warn("abort: working copy of .hgtags is changed!\n")
1361 1365 ui.status("(please commit .hgtags manually)\n")
1362 1366 return -1
1363 1367
1364 1368 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1365 1369 if repo.dirstate.state(".hgtags") == '?':
1366 1370 repo.add([".hgtags"])
1367 1371
1368 1372 message = (opts['message'] or opts['text'] or
1369 1373 "Added tag %s for changeset %s" % (name, r))
1370 1374 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1371 1375
1372 1376 def tags(ui, repo):
1373 1377 """list repository tags"""
1374 1378
1375 1379 l = repo.tagslist()
1376 1380 l.reverse()
1377 1381 for t, n in l:
1378 1382 try:
1379 1383 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1380 1384 except KeyError:
1381 1385 r = " ?:?"
1382 1386 ui.write("%-30s %s\n" % (t, r))
1383 1387
1384 1388 def tip(ui, repo):
1385 1389 """show the tip revision"""
1386 1390 n = repo.changelog.tip()
1387 1391 show_changeset(ui, repo, changenode=n)
1388 1392
1389 1393 def undo(ui, repo):
1390 1394 """undo the last commit or pull
1391 1395
1392 1396 Roll back the last pull or commit transaction on the
1393 1397 repository, restoring the project to its earlier state.
1394 1398
1395 1399 This command should be used with care. There is only one level of
1396 1400 undo and there is no redo.
1397 1401
1398 1402 This command is not intended for use on public repositories. Once
1399 1403 a change is visible for pull by other users, undoing it locally is
1400 1404 ineffective.
1401 1405 """
1402 1406 repo.undo()
1403 1407
1404 1408 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1405 1409 '''update or merge working directory
1406 1410
1407 1411 If there are no outstanding changes in the working directory and
1408 1412 there is a linear relationship between the current version and the
1409 1413 requested version, the result is the requested version.
1410 1414
1411 1415 Otherwise the result is a merge between the contents of the
1412 1416 current working directory and the requested version. Files that
1413 1417 changed between either parent are marked as changed for the next
1414 1418 commit and a commit must be performed before any further updates
1415 1419 are allowed.
1416 1420 '''
1417 1421 if branch:
1418 1422 br = repo.branchlookup(branch=branch)
1419 1423 found = []
1420 1424 for x in br:
1421 1425 if branch in br[x]:
1422 1426 found.append(x)
1423 1427 if len(found) > 1:
1424 1428 ui.warn("Found multiple heads for %s\n" % branch)
1425 1429 for x in found:
1426 1430 show_changeset(ui, repo, changenode=x, brinfo=br)
1427 1431 return 1
1428 1432 if len(found) == 1:
1429 1433 node = found[0]
1430 1434 ui.warn("Using head %s for branch %s\n" % (hg.short(node), branch))
1431 1435 else:
1432 1436 ui.warn("branch %s not found\n" % (branch))
1433 1437 return 1
1434 1438 else:
1435 1439 node = node and repo.lookup(node) or repo.changelog.tip()
1436 1440 return repo.update(node, allow=merge, force=clean)
1437 1441
1438 1442 def verify(ui, repo):
1439 1443 """verify the integrity of the repository"""
1440 1444 return repo.verify()
1441 1445
1442 1446 # Command options and aliases are listed here, alphabetically
1443 1447
1444 1448 table = {
1445 1449 "^add":
1446 1450 (add,
1447 1451 [('I', 'include', [], 'include path in search'),
1448 1452 ('X', 'exclude', [], 'exclude path from search')],
1449 1453 "hg add [OPTION]... [FILE]..."),
1450 1454 "addremove":
1451 1455 (addremove,
1452 1456 [('I', 'include', [], 'include path in search'),
1453 1457 ('X', 'exclude', [], 'exclude path from search')],
1454 1458 "hg addremove [OPTION]... [FILE]..."),
1455 1459 "^annotate":
1456 1460 (annotate,
1457 1461 [('r', 'rev', '', 'revision'),
1458 1462 ('a', 'text', None, 'treat all files as text'),
1459 1463 ('u', 'user', None, 'show user'),
1460 1464 ('n', 'number', None, 'show revision number'),
1461 1465 ('c', 'changeset', None, 'show changeset'),
1462 1466 ('I', 'include', [], 'include path in search'),
1463 1467 ('X', 'exclude', [], 'exclude path from search')],
1464 1468 'hg annotate [OPTION]... FILE...'),
1465 1469 "cat":
1466 1470 (cat,
1467 1471 [('o', 'output', "", 'output to file')],
1468 1472 'hg cat [-o OUTFILE] FILE [REV]'),
1469 1473 "^clone":
1470 1474 (clone,
1471 1475 [('U', 'noupdate', None, 'skip update after cloning'),
1472 1476 ('e', 'ssh', "", 'ssh command'),
1473 1477 ('', 'remotecmd', "", 'remote hg command')],
1474 1478 'hg clone [OPTIONS] SOURCE [DEST]'),
1475 1479 "^commit|ci":
1476 1480 (commit,
1477 1481 [('A', 'addremove', None, 'run add/remove during commit'),
1478 1482 ('I', 'include', [], 'include path in search'),
1479 1483 ('X', 'exclude', [], 'exclude path from search'),
1480 1484 ('m', 'message', "", 'commit message'),
1481 1485 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1482 1486 ('l', 'logfile', "", 'commit message file'),
1483 1487 ('d', 'date', "", 'date code'),
1484 1488 ('u', 'user', "", 'user')],
1485 1489 'hg commit [OPTION]... [FILE]...'),
1486 1490 "copy": (copy, [], 'hg copy SOURCE DEST'),
1487 1491 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1488 1492 "debugconfig": (debugconfig, [], 'debugconfig'),
1489 1493 "debugstate": (debugstate, [], 'debugstate'),
1490 1494 "debugdata": (debugdata, [], 'debugdata FILE REV'),
1491 1495 "debugindex": (debugindex, [], 'debugindex FILE'),
1492 1496 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1493 1497 "debugwalk":
1494 1498 (debugwalk,
1495 1499 [('I', 'include', [], 'include path in search'),
1496 1500 ('X', 'exclude', [], 'exclude path from search')],
1497 1501 'debugwalk [OPTION]... [FILE]...'),
1498 1502 "^diff":
1499 1503 (diff,
1500 1504 [('r', 'rev', [], 'revision'),
1501 1505 ('a', 'text', None, 'treat all files as text'),
1502 1506 ('I', 'include', [], 'include path in search'),
1503 1507 ('X', 'exclude', [], 'exclude path from search')],
1504 1508 'hg diff [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1505 1509 "^export":
1506 1510 (export,
1507 1511 [('o', 'output', "", 'output to file'),
1508 1512 ('a', 'text', None, 'treat all files as text')],
1509 1513 "hg export [-o OUTFILE] REV..."),
1510 1514 "forget":
1511 1515 (forget,
1512 1516 [('I', 'include', [], 'include path in search'),
1513 1517 ('X', 'exclude', [], 'exclude path from search')],
1514 1518 "hg forget [OPTION]... FILE..."),
1515 1519 "grep": (grep,
1516 1520 [('0', 'print0', None, 'terminate file names with NUL'),
1517 1521 ('I', 'include', [], 'include path in search'),
1518 1522 ('X', 'exclude', [], 'include path in search'),
1519 1523 ('Z', 'null', None, 'terminate file names with NUL'),
1520 1524 ('a', 'all-revs', '', 'search all revs'),
1521 1525 ('e', 'regexp', '', 'pattern to search for'),
1522 1526 ('f', 'full-path', None, 'print complete paths'),
1523 1527 ('i', 'ignore-case', None, 'ignore case when matching'),
1524 1528 ('l', 'files-with-matches', None, 'print names of files with matches'),
1525 1529 ('n', 'line-number', '', 'print line numbers'),
1526 1530 ('r', 'rev', [], 'search in revision rev'),
1527 1531 ('s', 'no-messages', None, 'do not print error messages'),
1528 1532 ('v', 'invert-match', None, 'select non-matching lines')],
1529 1533 "hg grep [options] [pat] [files]"),
1530 1534 "heads":
1531 1535 (heads,
1532 1536 [('b', 'branches', None, 'find branch info')],
1533 1537 'hg [-b] heads'),
1534 1538 "help": (help_, [], 'hg help [COMMAND]'),
1535 1539 "identify|id": (identify, [], 'hg identify'),
1536 1540 "import|patch":
1537 1541 (import_,
1538 1542 [('p', 'strip', 1, 'path strip'),
1539 1543 ('f', 'force', None, 'skip check for outstanding changes'),
1540 1544 ('b', 'base', "", 'base path')],
1541 1545 "hg import [-p NUM] [-b BASE] PATCH..."),
1542 1546 "incoming|in": (incoming, [], 'hg incoming [SOURCE]'),
1543 1547 "^init": (init, [], 'hg init [DEST]'),
1544 1548 "locate":
1545 1549 (locate,
1546 1550 [('r', 'rev', '', 'revision'),
1547 1551 ('0', 'print0', None, 'end records with NUL'),
1548 1552 ('f', 'fullpath', None, 'print complete paths'),
1549 1553 ('I', 'include', [], 'include path in search'),
1550 1554 ('X', 'exclude', [], 'exclude path from search')],
1551 1555 'hg locate [OPTION]... [PATTERN]...'),
1552 1556 "^log|history":
1553 1557 (log,
1554 1558 [('I', 'include', [], 'include path in search'),
1555 1559 ('X', 'exclude', [], 'exclude path from search'),
1556 1560 ('r', 'rev', [], 'revision'),
1557 1561 ('p', 'patch', None, 'show patch')],
1558 1562 'hg log [-r REV1 [-r REV2]] [-p] [FILE]'),
1559 1563 "manifest": (manifest, [], 'hg manifest [REV]'),
1560 1564 "outgoing|out": (outgoing, [], 'hg outgoing [DEST]'),
1561 1565 "parents": (parents, [], 'hg parents [REV]'),
1562 1566 "paths": (paths, [], 'hg paths [NAME]'),
1563 1567 "^pull":
1564 1568 (pull,
1565 1569 [('u', 'update', None, 'update working directory'),
1566 1570 ('e', 'ssh', "", 'ssh command'),
1567 1571 ('', 'remotecmd', "", 'remote hg command')],
1568 1572 'hg pull [OPTIONS] [SOURCE]'),
1569 1573 "^push":
1570 1574 (push,
1571 1575 [('f', 'force', None, 'force push'),
1572 1576 ('e', 'ssh', "", 'ssh command'),
1573 1577 ('', 'remotecmd', "", 'remote hg command')],
1574 1578 'hg push [-f] [DEST]'),
1575 1579 "rawcommit":
1576 1580 (rawcommit,
1577 1581 [('p', 'parent', [], 'parent'),
1578 1582 ('d', 'date', "", 'date code'),
1579 1583 ('u', 'user', "", 'user'),
1580 1584 ('F', 'files', "", 'file list'),
1581 1585 ('m', 'message', "", 'commit message'),
1582 1586 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1583 1587 ('l', 'logfile', "", 'commit message file')],
1584 1588 'hg rawcommit [OPTION]... [FILE]...'),
1585 1589 "recover": (recover, [], "hg recover"),
1586 1590 "^remove|rm": (remove, [], "hg remove FILE..."),
1587 1591 "^revert":
1588 1592 (revert,
1589 1593 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1590 1594 ("r", "rev", "", "revision")],
1591 1595 "hg revert [-n] [-r REV] [NAME]..."),
1592 1596 "root": (root, [], "hg root"),
1593 1597 "^serve":
1594 1598 (serve,
1595 1599 [('A', 'accesslog', '', 'access log file'),
1596 1600 ('E', 'errorlog', '', 'error log file'),
1597 1601 ('p', 'port', 0, 'listen port'),
1598 1602 ('a', 'address', '', 'interface address'),
1599 1603 ('n', 'name', "", 'repository name'),
1600 1604 ('', 'stdio', None, 'for remote clients'),
1601 1605 ('t', 'templates', "", 'template directory'),
1602 1606 ('', 'style', "", 'template style'),
1603 1607 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1604 1608 "hg serve [OPTION]..."),
1605 1609 "^status":
1606 1610 (status,
1607 1611 [('m', 'modified', None, 'show only modified files'),
1608 1612 ('a', 'added', None, 'show only added files'),
1609 1613 ('r', 'removed', None, 'show only removed files'),
1610 1614 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1611 1615 ('I', 'include', [], 'include path in search'),
1612 1616 ('X', 'exclude', [], 'exclude path from search')],
1613 1617 "hg status [OPTION]... [FILE]..."),
1614 1618 "tag":
1615 1619 (tag,
1616 1620 [('l', 'local', None, 'make the tag local'),
1617 1621 ('m', 'message', "", 'commit message'),
1618 1622 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1619 1623 ('d', 'date', "", 'date code'),
1620 1624 ('u', 'user', "", 'user')],
1621 1625 'hg tag [OPTION]... NAME [REV]'),
1622 1626 "tags": (tags, [], 'hg tags'),
1623 1627 "tip": (tip, [], 'hg tip'),
1624 1628 "undo": (undo, [], 'hg undo'),
1625 1629 "^update|up|checkout|co":
1626 1630 (update,
1627 1631 [('b', 'branch', "", 'checkout the head of a specific branch'),
1628 1632 ('m', 'merge', None, 'allow merging of conflicts'),
1629 1633 ('C', 'clean', None, 'overwrite locally modified files')],
1630 1634 'hg update [-b TAG] [-m] [-C] [REV]'),
1631 1635 "verify": (verify, [], 'hg verify'),
1632 1636 "version": (show_version, [], 'hg version'),
1633 1637 }
1634 1638
1635 1639 globalopts = [('v', 'verbose', None, 'verbose mode'),
1636 1640 ('', 'debug', None, 'debug mode'),
1637 1641 ('q', 'quiet', None, 'quiet mode'),
1638 1642 ('', 'profile', None, 'profile'),
1639 1643 ('', 'cwd', '', 'change working directory'),
1640 1644 ('R', 'repository', "", 'repository root directory'),
1641 1645 ('', 'traceback', None, 'print traceback on exception'),
1642 1646 ('y', 'noninteractive', None, 'run non-interactively'),
1643 1647 ('', 'version', None, 'output version information and exit'),
1644 1648 ('', 'time', None, 'time how long the command takes'),
1645 1649 ]
1646 1650
1647 1651 norepo = "clone init version help debugconfig debugdata" + \
1648 1652 " debugindex debugindexdot paths"
1649 1653
1650 1654 def find(cmd):
1651 1655 for e in table.keys():
1652 1656 if re.match("(%s)$" % e, cmd):
1653 1657 return e, table[e]
1654 1658
1655 1659 raise UnknownCommand(cmd)
1656 1660
1657 1661 class SignalInterrupt(Exception):
1658 1662 """Exception raised on SIGTERM and SIGHUP."""
1659 1663
1660 1664 def catchterm(*args):
1661 1665 raise SignalInterrupt
1662 1666
1663 1667 def run():
1664 1668 sys.exit(dispatch(sys.argv[1:]))
1665 1669
1666 1670 class ParseError(Exception):
1667 1671 """Exception raised on errors in parsing the command line."""
1668 1672
1669 1673 def parse(args):
1670 1674 options = {}
1671 1675 cmdoptions = {}
1672 1676
1673 1677 try:
1674 1678 args = fancyopts.fancyopts(args, globalopts, options)
1675 1679 except fancyopts.getopt.GetoptError, inst:
1676 1680 raise ParseError(None, inst)
1677 1681
1678 1682 if options["version"]:
1679 1683 return ("version", show_version, [], options, cmdoptions)
1680 1684 elif not args:
1681 1685 return ("help", help_, ["shortlist"], options, cmdoptions)
1682 1686 else:
1683 1687 cmd, args = args[0], args[1:]
1684 1688
1685 1689 i = find(cmd)[1]
1686 1690
1687 1691 # combine global options into local
1688 1692 c = list(i[1])
1689 1693 for o in globalopts:
1690 1694 c.append((o[0], o[1], options[o[1]], o[3]))
1691 1695
1692 1696 try:
1693 1697 args = fancyopts.fancyopts(args, c, cmdoptions)
1694 1698 except fancyopts.getopt.GetoptError, inst:
1695 1699 raise ParseError(cmd, inst)
1696 1700
1697 1701 # separate global options back out
1698 1702 for o in globalopts:
1699 1703 n = o[1]
1700 1704 options[n] = cmdoptions[n]
1701 1705 del cmdoptions[n]
1702 1706
1703 1707 return (cmd, i[0], args, options, cmdoptions)
1704 1708
1705 1709 def dispatch(args):
1706 1710 signal.signal(signal.SIGTERM, catchterm)
1707 1711 try:
1708 1712 signal.signal(signal.SIGHUP, catchterm)
1709 1713 except AttributeError:
1710 1714 pass
1711 1715
1712 1716 try:
1713 1717 cmd, func, args, options, cmdoptions = parse(args)
1714 1718 except ParseError, inst:
1715 1719 u = ui.ui()
1716 1720 if inst.args[0]:
1717 1721 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1718 1722 help_(u, inst.args[0])
1719 1723 else:
1720 1724 u.warn("hg: %s\n" % inst.args[1])
1721 1725 help_(u, 'shortlist')
1722 1726 sys.exit(-1)
1723 1727 except UnknownCommand, inst:
1724 1728 u = ui.ui()
1725 1729 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1726 1730 help_(u, 'shortlist')
1727 1731 sys.exit(1)
1728 1732
1729 1733 if options['cwd']:
1730 1734 try:
1731 1735 os.chdir(options['cwd'])
1732 1736 except OSError, inst:
1733 1737 u = ui.ui()
1734 1738 u.warn('abort: %s: %s\n' % (options['cwd'], inst.strerror))
1735 1739 sys.exit(1)
1736 1740
1737 1741 if options["time"]:
1738 1742 def get_times():
1739 1743 t = os.times()
1740 1744 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1741 1745 t = (t[0], t[1], t[2], t[3], time.clock())
1742 1746 return t
1743 1747 s = get_times()
1744 1748 def print_time():
1745 1749 t = get_times()
1746 1750 u = ui.ui()
1747 1751 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1748 1752 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1749 1753 atexit.register(print_time)
1750 1754
1751 1755 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1752 1756 not options["noninteractive"])
1753 1757
1754 1758 try:
1755 1759 try:
1756 1760 if cmd not in norepo.split():
1757 1761 path = options["repository"] or ""
1758 1762 repo = hg.repository(ui=u, path=path)
1759 1763 d = lambda: func(u, repo, *args, **cmdoptions)
1760 1764 else:
1761 1765 d = lambda: func(u, *args, **cmdoptions)
1762 1766
1763 1767 if options['profile']:
1764 1768 import hotshot, hotshot.stats
1765 1769 prof = hotshot.Profile("hg.prof")
1766 1770 r = prof.runcall(d)
1767 1771 prof.close()
1768 1772 stats = hotshot.stats.load("hg.prof")
1769 1773 stats.strip_dirs()
1770 1774 stats.sort_stats('time', 'calls')
1771 1775 stats.print_stats(40)
1772 1776 return r
1773 1777 else:
1774 1778 return d()
1775 1779 except:
1776 1780 if options['traceback']:
1777 1781 traceback.print_exc()
1778 1782 raise
1779 1783 except hg.RepoError, inst:
1780 1784 u.warn("abort: ", inst, "!\n")
1781 1785 except SignalInterrupt:
1782 1786 u.warn("killed!\n")
1783 1787 except KeyboardInterrupt:
1784 1788 try:
1785 1789 u.warn("interrupted!\n")
1786 1790 except IOError, inst:
1787 1791 if inst.errno == errno.EPIPE:
1788 1792 if u.debugflag:
1789 1793 u.warn("\nbroken pipe\n")
1790 1794 else:
1791 1795 raise
1792 1796 except IOError, inst:
1793 1797 if hasattr(inst, "code"):
1794 1798 u.warn("abort: %s\n" % inst)
1795 1799 elif hasattr(inst, "reason"):
1796 1800 u.warn("abort: error: %s\n" % inst.reason[1])
1797 1801 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1798 1802 if u.debugflag: u.warn("broken pipe\n")
1799 1803 else:
1800 1804 raise
1801 1805 except OSError, inst:
1802 1806 if hasattr(inst, "filename"):
1803 1807 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1804 1808 else:
1805 1809 u.warn("abort: %s\n" % inst.strerror)
1806 1810 except util.Abort, inst:
1807 1811 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1808 1812 sys.exit(1)
1809 1813 except TypeError, inst:
1810 1814 # was this an argument error?
1811 1815 tb = traceback.extract_tb(sys.exc_info()[2])
1812 1816 if len(tb) > 2: # no
1813 1817 raise
1814 1818 u.debug(inst, "\n")
1815 1819 u.warn("%s: invalid arguments\n" % cmd)
1816 1820 help_(u, cmd)
1817 1821 except UnknownCommand, inst:
1818 1822 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1819 1823 help_(u, 'shortlist')
1820 1824
1821 1825 sys.exit(-1)
General Comments 0
You need to be logged in to leave comments. Login now