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