##// END OF EJS Templates
show_changeset: -q shows short hash
mpm@selenic.com -
r921:1a51fa1b default
parent child Browse files
Show More
@@ -1,1567 +1,1567 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 ui.write("%d:%s\n" % (rev, hg.hex(node)))
219 ui.write("%d:%s\n" % (rev, hg.short(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 458 try:
459 459 # assume all revision numbers are for changesets
460 460 n = repo.lookup(rev)
461 461 change = repo.changelog.read(n)
462 462 m = repo.manifest.read(change[0])
463 463 n = m[relpath(repo, [file1])[0]]
464 464 except hg.RepoError, KeyError:
465 465 n = r.lookup(rev)
466 466 else:
467 467 n = r.tip()
468 468 fp = make_file(repo, r, opts['output'], node=n)
469 469 fp.write(r.read(n))
470 470
471 471 def clone(ui, source, dest=None, **opts):
472 472 """make a copy of an existing repository"""
473 473 if dest is None:
474 474 dest = os.path.basename(os.path.normpath(source))
475 475
476 476 if os.path.exists(dest):
477 477 ui.warn("abort: destination '%s' already exists\n" % dest)
478 478 return 1
479 479
480 480 dest = os.path.realpath(dest)
481 481
482 482 class Dircleanup:
483 483 def __init__(self, dir_):
484 484 self.rmtree = shutil.rmtree
485 485 self.dir_ = dir_
486 486 os.mkdir(dir_)
487 487 def close(self):
488 488 self.dir_ = None
489 489 def __del__(self):
490 490 if self.dir_:
491 491 self.rmtree(self.dir_, True)
492 492
493 493 d = Dircleanup(dest)
494 494 abspath = source
495 495 source = ui.expandpath(source)
496 496 other = hg.repository(ui, source)
497 497
498 498 if other.dev() != -1:
499 499 abspath = os.path.abspath(source)
500 500 copyfile = (os.stat(dest).st_dev == other.dev()
501 501 and getattr(os, 'link', None) or shutil.copy2)
502 502 if copyfile is not shutil.copy2:
503 503 ui.note("cloning by hardlink\n")
504 504 # we use a lock here because because we're not nicely ordered
505 505 l = lock.lock(os.path.join(source, ".hg", "lock"))
506 506
507 507 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
508 508 copyfile)
509 509 try:
510 510 os.unlink(os.path.join(dest, ".hg", "dirstate"))
511 511 except OSError:
512 512 pass
513 513
514 514 repo = hg.repository(ui, dest)
515 515
516 516 else:
517 517 repo = hg.repository(ui, dest, create=1)
518 518 repo.pull(other)
519 519
520 520 f = repo.opener("hgrc", "w")
521 521 f.write("[paths]\n")
522 522 f.write("default = %s\n" % abspath)
523 523
524 524 if not opts['noupdate']:
525 525 update(ui, repo)
526 526
527 527 d.close()
528 528
529 529 def commit(ui, repo, *pats, **opts):
530 530 """commit the specified files or all outstanding changes"""
531 531 if opts['text']:
532 532 ui.warn("Warning: -t and --text is deprecated,"
533 533 " please use -m or --message instead.\n")
534 534 message = opts['message'] or opts['text']
535 535 logfile = opts['logfile']
536 536 if not message and logfile:
537 537 try:
538 538 if logfile == '-':
539 539 message = sys.stdin.read()
540 540 else:
541 541 message = open(logfile).read()
542 542 except IOError, why:
543 543 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
544 544
545 545 if opts['addremove']:
546 546 addremove(ui, repo, *pats, **opts)
547 547 cwd = repo.getcwd()
548 548 if not pats and cwd:
549 549 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
550 550 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
551 551 fns, match = matchpats(repo, (pats and repo.getcwd()) or '', pats, opts)
552 552 if pats:
553 553 c, a, d, u = repo.changes(files = fns, match = match)
554 554 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
555 555 else:
556 556 files = []
557 557 repo.commit(files, message, opts['user'], opts['date'], match)
558 558
559 559 def copy(ui, repo, source, dest):
560 560 """mark a file as copied or renamed for the next commit"""
561 561 return repo.copy(*relpath(repo, (source, dest)))
562 562
563 563 def debugcheckstate(ui, repo):
564 564 """validate the correctness of the current dirstate"""
565 565 parent1, parent2 = repo.dirstate.parents()
566 566 repo.dirstate.read()
567 567 dc = repo.dirstate.map
568 568 keys = dc.keys()
569 569 keys.sort()
570 570 m1n = repo.changelog.read(parent1)[0]
571 571 m2n = repo.changelog.read(parent2)[0]
572 572 m1 = repo.manifest.read(m1n)
573 573 m2 = repo.manifest.read(m2n)
574 574 errors = 0
575 575 for f in dc:
576 576 state = repo.dirstate.state(f)
577 577 if state in "nr" and f not in m1:
578 578 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
579 579 errors += 1
580 580 if state in "a" and f in m1:
581 581 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
582 582 errors += 1
583 583 if state in "m" and f not in m1 and f not in m2:
584 584 ui.warn("%s in state %s, but not in either manifest\n" %
585 585 (f, state))
586 586 errors += 1
587 587 for f in m1:
588 588 state = repo.dirstate.state(f)
589 589 if state not in "nrm":
590 590 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
591 591 errors += 1
592 592 if errors:
593 593 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
594 594
595 595 def debugstate(ui, repo):
596 596 """show the contents of the current dirstate"""
597 597 repo.dirstate.read()
598 598 dc = repo.dirstate.map
599 599 keys = dc.keys()
600 600 keys.sort()
601 601 for file_ in keys:
602 602 ui.write("%c %3o %10d %s %s\n"
603 603 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
604 604 time.strftime("%x %X",
605 605 time.localtime(dc[file_][3])), file_))
606 606
607 607 def debugindex(ui, file_):
608 608 """dump the contents of an index file"""
609 609 r = hg.revlog(hg.opener(""), file_, "")
610 610 ui.write(" rev offset length base linkrev" +
611 611 " p1 p2 nodeid\n")
612 612 for i in range(r.count()):
613 613 e = r.index[i]
614 614 ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
615 615 i, e[0], e[1], e[2], e[3],
616 616 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
617 617
618 618 def debugindexdot(ui, file_):
619 619 """dump an index DAG as a .dot file"""
620 620 r = hg.revlog(hg.opener(""), file_, "")
621 621 ui.write("digraph G {\n")
622 622 for i in range(r.count()):
623 623 e = r.index[i]
624 624 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
625 625 if e[5] != hg.nullid:
626 626 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
627 627 ui.write("}\n")
628 628
629 629 def debugwalk(ui, repo, *pats, **opts):
630 630 items = list(walk(repo, pats, opts))
631 631 if not items: return
632 632 fmt = '%%s %%-%ds %%s' % max([len(abs) for (src, abs, rel) in items])
633 633 for i in items: print fmt % i
634 634
635 635 def diff(ui, repo, *pats, **opts):
636 636 """diff working directory (or selected files)"""
637 637 revs = []
638 638 if opts['rev']:
639 639 revs = map(lambda x: repo.lookup(x), opts['rev'])
640 640
641 641 if len(revs) > 2:
642 642 raise util.Abort("too many revisions to diff")
643 643
644 644 files = []
645 645 match = util.always
646 646 if pats:
647 647 roots, match, results = makewalk(repo, pats, opts)
648 648 for src, abs, rel in results:
649 649 files.append(abs)
650 650 dodiff(sys.stdout, ui, repo, files, *revs, **{'match': match})
651 651
652 652 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
653 653 node = repo.lookup(changeset)
654 654 prev, other = repo.changelog.parents(node)
655 655 change = repo.changelog.read(node)
656 656
657 657 fp = make_file(repo, repo.changelog, opts['output'],
658 658 node=node, total=total, seqno=seqno,
659 659 revwidth=revwidth)
660 660 if fp != sys.stdout:
661 661 ui.note("%s\n" % fp.name)
662 662
663 663 fp.write("# HG changeset patch\n")
664 664 fp.write("# User %s\n" % change[1])
665 665 fp.write("# Node ID %s\n" % hg.hex(node))
666 666 fp.write("# Parent %s\n" % hg.hex(prev))
667 667 if other != hg.nullid:
668 668 fp.write("# Parent %s\n" % hg.hex(other))
669 669 fp.write(change[4].rstrip())
670 670 fp.write("\n\n")
671 671
672 672 dodiff(fp, ui, repo, None, prev, node)
673 673 if fp != sys.stdout: fp.close()
674 674
675 675 def export(ui, repo, *changesets, **opts):
676 676 """dump the header and diffs for one or more changesets"""
677 677 if not changesets:
678 678 raise util.Abort("export requires at least one changeset")
679 679 seqno = 0
680 680 revs = list(revrange(ui, repo, changesets))
681 681 total = len(revs)
682 682 revwidth = max(len(revs[0]), len(revs[-1]))
683 683 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
684 684 for cset in revs:
685 685 seqno += 1
686 686 doexport(ui, repo, cset, seqno, total, revwidth, opts)
687 687
688 688 def forget(ui, repo, *pats, **opts):
689 689 """don't add the specified files on the next commit"""
690 690 q = dict(zip(pats, pats))
691 691 forget = []
692 692 for src, abs, rel in walk(repo, pats, opts):
693 693 if repo.dirstate.state(abs) == 'a':
694 694 forget.append(abs)
695 695 if rel not in q: ui.status('forgetting ', rel, '\n')
696 696 repo.forget(forget)
697 697
698 698 def heads(ui, repo, **opts):
699 699 """show current repository heads"""
700 700 heads = repo.changelog.heads()
701 701 br = None
702 702 if opts['branches']:
703 703 br = repo.branchlookup(heads)
704 704 for n in repo.changelog.heads():
705 705 show_changeset(ui, repo, changenode=n, brinfo=br)
706 706
707 707 def identify(ui, repo):
708 708 """print information about the working copy"""
709 709 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
710 710 if not parents:
711 711 ui.write("unknown\n")
712 712 return
713 713
714 714 hexfunc = ui.verbose and hg.hex or hg.short
715 715 (c, a, d, u) = repo.changes()
716 716 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
717 717 (c or a or d) and "+" or "")]
718 718
719 719 if not ui.quiet:
720 720 # multiple tags for a single parent separated by '/'
721 721 parenttags = ['/'.join(tags)
722 722 for tags in map(repo.nodetags, parents) if tags]
723 723 # tags for multiple parents separated by ' + '
724 724 if parenttags:
725 725 output.append(' + '.join(parenttags))
726 726
727 727 ui.write("%s\n" % ' '.join(output))
728 728
729 729 def import_(ui, repo, patch1, *patches, **opts):
730 730 """import an ordered set of patches"""
731 731 patches = (patch1,) + patches
732 732
733 733 d = opts["base"]
734 734 strip = opts["strip"]
735 735
736 736 for patch in patches:
737 737 ui.status("applying %s\n" % patch)
738 738 pf = os.path.join(d, patch)
739 739
740 740 message = []
741 741 user = None
742 742 hgpatch = False
743 743 for line in file(pf):
744 744 line = line.rstrip()
745 745 if line.startswith("--- ") or line.startswith("diff -r"):
746 746 break
747 747 elif hgpatch:
748 748 # parse values when importing the result of an hg export
749 749 if line.startswith("# User "):
750 750 user = line[7:]
751 751 ui.debug('User: %s\n' % user)
752 752 elif not line.startswith("# ") and line:
753 753 message.append(line)
754 754 hgpatch = False
755 755 elif line == '# HG changeset patch':
756 756 hgpatch = True
757 757 message = [] # We may have collected garbage
758 758 else:
759 759 message.append(line)
760 760
761 761 # make sure message isn't empty
762 762 if not message:
763 763 message = "imported patch %s\n" % patch
764 764 else:
765 765 message = "%s\n" % '\n'.join(message)
766 766 ui.debug('message:\n%s\n' % message)
767 767
768 768 f = os.popen("patch -p%d < '%s'" % (strip, pf))
769 769 files = []
770 770 for l in f.read().splitlines():
771 771 l.rstrip('\r\n');
772 772 ui.status("%s\n" % l)
773 773 if l.startswith('patching file '):
774 774 pf = l[14:]
775 775 if pf not in files:
776 776 files.append(pf)
777 777 patcherr = f.close()
778 778 if patcherr:
779 779 raise util.Abort("patch failed")
780 780
781 781 if len(files) > 0:
782 782 addremove(ui, repo, *files)
783 783 repo.commit(files, message, user)
784 784
785 785 def init(ui, dest="."):
786 786 """create a new repository in the given directory"""
787 787 if not os.path.exists(dest):
788 788 os.mkdir(dest)
789 789 hg.repository(ui, dest, create=1)
790 790
791 791 def locate(ui, repo, *pats, **opts):
792 792 """locate files matching specific patterns"""
793 793 end = '\n'
794 794 if opts['print0']: end = '\0'
795 795
796 796 for src, abs, rel in walk(repo, pats, opts, '(?:.*/|)'):
797 797 if repo.dirstate.state(abs) == '?': continue
798 798 if opts['fullpath']:
799 799 ui.write(os.path.join(repo.root, abs), end)
800 800 else:
801 801 ui.write(rel, end)
802 802
803 803 def log(ui, repo, f=None, **opts):
804 804 """show the revision history of the repository or a single file"""
805 805 if f:
806 806 files = relpath(repo, [f])
807 807 filelog = repo.file(files[0])
808 808 log = filelog
809 809 lookup = filelog.lookup
810 810 else:
811 811 files = None
812 812 filelog = None
813 813 log = repo.changelog
814 814 lookup = repo.lookup
815 815 revlist = []
816 816 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
817 817 while revs:
818 818 if len(revs) == 1:
819 819 revlist.append(revs.pop(0))
820 820 else:
821 821 a = revs.pop(0)
822 822 b = revs.pop(0)
823 823 off = a > b and -1 or 1
824 824 revlist.extend(range(a, b + off, off))
825 825
826 826 for i in revlist or range(log.count() - 1, -1, -1):
827 827 show_changeset(ui, repo, filelog=filelog, rev=i)
828 828 if opts['patch']:
829 829 if filelog:
830 830 filenode = filelog.node(i)
831 831 i = filelog.linkrev(filenode)
832 832 changenode = repo.changelog.node(i)
833 833 prev, other = repo.changelog.parents(changenode)
834 834 dodiff(sys.stdout, ui, repo, files, prev, changenode)
835 835 ui.write("\n\n")
836 836
837 837 def manifest(ui, repo, rev=None):
838 838 """output the latest or given revision of the project manifest"""
839 839 if rev:
840 840 try:
841 841 # assume all revision numbers are for changesets
842 842 n = repo.lookup(rev)
843 843 change = repo.changelog.read(n)
844 844 n = change[0]
845 845 except hg.RepoError:
846 846 n = repo.manifest.lookup(rev)
847 847 else:
848 848 n = repo.manifest.tip()
849 849 m = repo.manifest.read(n)
850 850 mf = repo.manifest.readflags(n)
851 851 files = m.keys()
852 852 files.sort()
853 853
854 854 for f in files:
855 855 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
856 856
857 857 def outgoing(ui, repo, dest="default-push"):
858 858 """show changesets not found in destination"""
859 859 dest = ui.expandpath(dest)
860 860 other = hg.repository(ui, dest)
861 861 o = repo.findoutgoing(other)
862 862 o = repo.newer(o)
863 863 o.reverse()
864 864 for n in o:
865 865 show_changeset(ui, repo, changenode=n)
866 866
867 867 def parents(ui, repo, rev=None):
868 868 """show the parents of the working dir or revision"""
869 869 if rev:
870 870 p = repo.changelog.parents(repo.lookup(rev))
871 871 else:
872 872 p = repo.dirstate.parents()
873 873
874 874 for n in p:
875 875 if n != hg.nullid:
876 876 show_changeset(ui, repo, changenode=n)
877 877
878 878 def paths(ui, search = None):
879 879 """show path or list of available paths"""
880 880 try:
881 881 repo = hg.repository(ui=ui)
882 882 except:
883 883 pass
884 884
885 885 if search:
886 886 for name, path in ui.configitems("paths"):
887 887 if name == search:
888 888 ui.write("%s\n" % path)
889 889 return
890 890 ui.warn("not found!\n")
891 891 return 1
892 892 else:
893 893 for name, path in ui.configitems("paths"):
894 894 ui.write("%s = %s\n" % (name, path))
895 895
896 896 def pull(ui, repo, source="default", **opts):
897 897 """pull changes from the specified source"""
898 898 source = ui.expandpath(source)
899 899 ui.status('pulling from %s\n' % (source))
900 900
901 901 other = hg.repository(ui, source)
902 902 r = repo.pull(other)
903 903 if not r:
904 904 if opts['update']:
905 905 return update(ui, repo)
906 906 else:
907 907 ui.status("(run 'hg update' to get a working copy)\n")
908 908
909 909 return r
910 910
911 911 def push(ui, repo, dest="default-push", force=False):
912 912 """push changes to the specified destination"""
913 913 dest = ui.expandpath(dest)
914 914 ui.status('pushing to %s\n' % (dest))
915 915
916 916 other = hg.repository(ui, dest)
917 917 r = repo.push(other, force)
918 918 return r
919 919
920 920 def rawcommit(ui, repo, *flist, **rc):
921 921 "raw commit interface"
922 922 if rc['text']:
923 923 ui.warn("Warning: -t and --text is deprecated,"
924 924 " please use -m or --message instead.\n")
925 925 message = rc['message'] or rc['text']
926 926 if not message and rc['logfile']:
927 927 try:
928 928 message = open(rc['logfile']).read()
929 929 except IOError:
930 930 pass
931 931 if not message and not rc['logfile']:
932 932 ui.warn("abort: missing commit message\n")
933 933 return 1
934 934
935 935 files = relpath(repo, list(flist))
936 936 if rc['files']:
937 937 files += open(rc['files']).read().splitlines()
938 938
939 939 rc['parent'] = map(repo.lookup, rc['parent'])
940 940
941 941 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
942 942
943 943 def recover(ui, repo):
944 944 """roll back an interrupted transaction"""
945 945 repo.recover()
946 946
947 947 def remove(ui, repo, file1, *files):
948 948 """remove the specified files on the next commit"""
949 949 repo.remove(relpath(repo, (file1,) + files))
950 950
951 951 def revert(ui, repo, *names, **opts):
952 952 """revert modified files or dirs back to their unmodified states"""
953 953 node = opts['rev'] and repo.lookup(opts['rev']) or \
954 954 repo.dirstate.parents()[0]
955 955 root = os.path.realpath(repo.root)
956 956
957 957 def trimpath(p):
958 958 p = os.path.realpath(p)
959 959 if p.startswith(root):
960 960 rest = p[len(root):]
961 961 if not rest:
962 962 return rest
963 963 if p.startswith(os.sep):
964 964 return rest[1:]
965 965 return p
966 966
967 967 relnames = map(trimpath, names or [os.getcwd()])
968 968 chosen = {}
969 969
970 970 def choose(name):
971 971 def body(name):
972 972 for r in relnames:
973 973 if not name.startswith(r):
974 974 continue
975 975 rest = name[len(r):]
976 976 if not rest:
977 977 return r, True
978 978 depth = rest.count(os.sep)
979 979 if not r:
980 980 if depth == 0 or not opts['nonrecursive']:
981 981 return r, True
982 982 elif rest[0] == os.sep:
983 983 if depth == 1 or not opts['nonrecursive']:
984 984 return r, True
985 985 return None, False
986 986 relname, ret = body(name)
987 987 if ret:
988 988 chosen[relname] = 1
989 989 return ret
990 990
991 991 r = repo.update(node, False, True, choose, False)
992 992 for n in relnames:
993 993 if n not in chosen:
994 994 ui.warn('error: no matches for %s\n' % n)
995 995 r = 1
996 996 sys.stdout.flush()
997 997 return r
998 998
999 999 def root(ui, repo):
1000 1000 """print the root (top) of the current working dir"""
1001 1001 ui.write(repo.root + "\n")
1002 1002
1003 1003 def serve(ui, repo, **opts):
1004 1004 """export the repository via HTTP"""
1005 1005
1006 1006 if opts["stdio"]:
1007 1007 fin, fout = sys.stdin, sys.stdout
1008 1008 sys.stdout = sys.stderr
1009 1009
1010 1010 def getarg():
1011 1011 argline = fin.readline()[:-1]
1012 1012 arg, l = argline.split()
1013 1013 val = fin.read(int(l))
1014 1014 return arg, val
1015 1015 def respond(v):
1016 1016 fout.write("%d\n" % len(v))
1017 1017 fout.write(v)
1018 1018 fout.flush()
1019 1019
1020 1020 lock = None
1021 1021
1022 1022 while 1:
1023 1023 cmd = fin.readline()[:-1]
1024 1024 if cmd == '':
1025 1025 return
1026 1026 if cmd == "heads":
1027 1027 h = repo.heads()
1028 1028 respond(" ".join(map(hg.hex, h)) + "\n")
1029 1029 if cmd == "lock":
1030 1030 lock = repo.lock()
1031 1031 respond("")
1032 1032 if cmd == "unlock":
1033 1033 if lock:
1034 1034 lock.release()
1035 1035 lock = None
1036 1036 respond("")
1037 1037 elif cmd == "branches":
1038 1038 arg, nodes = getarg()
1039 1039 nodes = map(hg.bin, nodes.split(" "))
1040 1040 r = []
1041 1041 for b in repo.branches(nodes):
1042 1042 r.append(" ".join(map(hg.hex, b)) + "\n")
1043 1043 respond("".join(r))
1044 1044 elif cmd == "between":
1045 1045 arg, pairs = getarg()
1046 1046 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
1047 1047 r = []
1048 1048 for b in repo.between(pairs):
1049 1049 r.append(" ".join(map(hg.hex, b)) + "\n")
1050 1050 respond("".join(r))
1051 1051 elif cmd == "changegroup":
1052 1052 nodes = []
1053 1053 arg, roots = getarg()
1054 1054 nodes = map(hg.bin, roots.split(" "))
1055 1055
1056 1056 cg = repo.changegroup(nodes)
1057 1057 while 1:
1058 1058 d = cg.read(4096)
1059 1059 if not d:
1060 1060 break
1061 1061 fout.write(d)
1062 1062
1063 1063 fout.flush()
1064 1064
1065 1065 elif cmd == "addchangegroup":
1066 1066 if not lock:
1067 1067 respond("not locked")
1068 1068 continue
1069 1069 respond("")
1070 1070
1071 1071 r = repo.addchangegroup(fin)
1072 1072 respond("")
1073 1073
1074 1074 def openlog(opt, default):
1075 1075 if opts[opt] and opts[opt] != '-':
1076 1076 return open(opts[opt], 'w')
1077 1077 else:
1078 1078 return default
1079 1079
1080 1080 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
1081 1081 opts["address"], opts["port"], opts["ipv6"],
1082 1082 openlog('accesslog', sys.stdout),
1083 1083 openlog('errorlog', sys.stderr))
1084 1084 if ui.verbose:
1085 1085 addr, port = httpd.socket.getsockname()
1086 1086 if addr == '0.0.0.0':
1087 1087 addr = socket.gethostname()
1088 1088 else:
1089 1089 try:
1090 1090 addr = socket.gethostbyaddr(addr)[0]
1091 1091 except socket.error:
1092 1092 pass
1093 1093 if port != 80:
1094 1094 ui.status('listening at http://%s:%d/\n' % (addr, port))
1095 1095 else:
1096 1096 ui.status('listening at http://%s/\n' % addr)
1097 1097 httpd.serve_forever()
1098 1098
1099 1099 def status(ui, repo, *pats, **opts):
1100 1100 '''show changed files in the working directory
1101 1101
1102 1102 M = modified
1103 1103 A = added
1104 1104 R = removed
1105 1105 ? = not tracked
1106 1106 '''
1107 1107
1108 1108 cwd = repo.getcwd()
1109 1109 files, matchfn = matchpats(repo, cwd, pats, opts)
1110 1110 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1111 1111 for n in repo.changes(files=files, match=matchfn)]
1112 1112
1113 1113 changetypes = [('modified', 'M', c),
1114 1114 ('added', 'A', a),
1115 1115 ('removed', 'R', d),
1116 1116 ('unknown', '?', u)]
1117 1117
1118 1118 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1119 1119 or changetypes):
1120 1120 for f in changes:
1121 1121 ui.write("%s %s\n" % (char, f))
1122 1122
1123 1123 def tag(ui, repo, name, rev=None, **opts):
1124 1124 """add a tag for the current tip or a given revision"""
1125 1125 if opts['text']:
1126 1126 ui.warn("Warning: -t and --text is deprecated,"
1127 1127 " please use -m or --message instead.\n")
1128 1128 if name == "tip":
1129 1129 ui.warn("abort: 'tip' is a reserved name!\n")
1130 1130 return -1
1131 1131 if rev:
1132 1132 r = hg.hex(repo.lookup(rev))
1133 1133 else:
1134 1134 r = hg.hex(repo.changelog.tip())
1135 1135
1136 1136 if name.find(revrangesep) >= 0:
1137 1137 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1138 1138 return -1
1139 1139
1140 1140 if opts['local']:
1141 1141 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1142 1142 return
1143 1143
1144 1144 (c, a, d, u) = repo.changes()
1145 1145 for x in (c, a, d, u):
1146 1146 if ".hgtags" in x:
1147 1147 ui.warn("abort: working copy of .hgtags is changed!\n")
1148 1148 ui.status("(please commit .hgtags manually)\n")
1149 1149 return -1
1150 1150
1151 1151 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1152 1152 if repo.dirstate.state(".hgtags") == '?':
1153 1153 repo.add([".hgtags"])
1154 1154
1155 1155 message = (opts['message'] or opts['text'] or
1156 1156 "Added tag %s for changeset %s" % (name, r))
1157 1157 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1158 1158
1159 1159 def tags(ui, repo):
1160 1160 """list repository tags"""
1161 1161
1162 1162 l = repo.tagslist()
1163 1163 l.reverse()
1164 1164 for t, n in l:
1165 1165 try:
1166 1166 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1167 1167 except KeyError:
1168 1168 r = " ?:?"
1169 1169 ui.write("%-30s %s\n" % (t, r))
1170 1170
1171 1171 def tip(ui, repo):
1172 1172 """show the tip revision"""
1173 1173 n = repo.changelog.tip()
1174 1174 show_changeset(ui, repo, changenode=n)
1175 1175
1176 1176 def undo(ui, repo):
1177 1177 """undo the last commit or pull
1178 1178
1179 1179 Roll back the last pull or commit transaction on the
1180 1180 repository, restoring the project to its earlier state.
1181 1181
1182 1182 This command should be used with care. There is only one level of
1183 1183 undo and there is no redo.
1184 1184
1185 1185 This command is not intended for use on public repositories. Once
1186 1186 a change is visible for pull by other users, undoing it locally is
1187 1187 ineffective.
1188 1188 """
1189 1189 repo.undo()
1190 1190
1191 1191 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1192 1192 '''update or merge working directory
1193 1193
1194 1194 If there are no outstanding changes in the working directory and
1195 1195 there is a linear relationship between the current version and the
1196 1196 requested version, the result is the requested version.
1197 1197
1198 1198 Otherwise the result is a merge between the contents of the
1199 1199 current working directory and the requested version. Files that
1200 1200 changed between either parent are marked as changed for the next
1201 1201 commit and a commit must be performed before any further updates
1202 1202 are allowed.
1203 1203 '''
1204 1204 if branch:
1205 1205 br = repo.branchlookup(branch=branch)
1206 1206 found = []
1207 1207 for x in br:
1208 1208 if branch in br[x]:
1209 1209 found.append(x)
1210 1210 if len(found) > 1:
1211 1211 ui.warn("Found multiple heads for %s\n" % branch)
1212 1212 for x in found:
1213 1213 show_changeset(ui, repo, changenode=x, brinfo=br)
1214 1214 return 1
1215 1215 if len(found) == 1:
1216 1216 node = found[0]
1217 1217 ui.warn("Using head %s for branch %s\n" % (hg.short(node), branch))
1218 1218 else:
1219 1219 ui.warn("branch %s not found\n" % (branch))
1220 1220 return 1
1221 1221 else:
1222 1222 node = node and repo.lookup(node) or repo.changelog.tip()
1223 1223 return repo.update(node, allow=merge, force=clean)
1224 1224
1225 1225 def verify(ui, repo):
1226 1226 """verify the integrity of the repository"""
1227 1227 return repo.verify()
1228 1228
1229 1229 # Command options and aliases are listed here, alphabetically
1230 1230
1231 1231 table = {
1232 1232 "^add":
1233 1233 (add,
1234 1234 [('I', 'include', [], 'include path in search'),
1235 1235 ('X', 'exclude', [], 'exclude path from search')],
1236 1236 "hg add [OPTION]... [FILE]..."),
1237 1237 "addremove":
1238 1238 (addremove,
1239 1239 [('I', 'include', [], 'include path in search'),
1240 1240 ('X', 'exclude', [], 'exclude path from search')],
1241 1241 "hg addremove [OPTION]... [FILE]..."),
1242 1242 "^annotate":
1243 1243 (annotate,
1244 1244 [('r', 'rev', '', 'revision'),
1245 1245 ('u', 'user', None, 'show user'),
1246 1246 ('n', 'number', None, 'show revision number'),
1247 1247 ('c', 'changeset', None, 'show changeset'),
1248 1248 ('I', 'include', [], 'include path in search'),
1249 1249 ('X', 'exclude', [], 'exclude path from search')],
1250 1250 'hg annotate [OPTION]... FILE...'),
1251 1251 "cat":
1252 1252 (cat,
1253 1253 [('o', 'output', "", 'output to file')],
1254 1254 'hg cat [-o OUTFILE] FILE [REV]'),
1255 1255 "^clone":
1256 1256 (clone,
1257 1257 [('U', 'noupdate', None, 'skip update after cloning')],
1258 1258 'hg clone [-U] SOURCE [DEST]'),
1259 1259 "^commit|ci":
1260 1260 (commit,
1261 1261 [('A', 'addremove', None, 'run add/remove during commit'),
1262 1262 ('I', 'include', [], 'include path in search'),
1263 1263 ('X', 'exclude', [], 'exclude path from search'),
1264 1264 ('m', 'message', "", 'commit message'),
1265 1265 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1266 1266 ('l', 'logfile', "", 'commit message file'),
1267 1267 ('d', 'date', "", 'date code'),
1268 1268 ('u', 'user', "", 'user')],
1269 1269 'hg commit [OPTION]... [FILE]...'),
1270 1270 "copy": (copy, [], 'hg copy SOURCE DEST'),
1271 1271 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1272 1272 "debugstate": (debugstate, [], 'debugstate'),
1273 1273 "debugindex": (debugindex, [], 'debugindex FILE'),
1274 1274 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1275 1275 "debugwalk":
1276 1276 (debugwalk,
1277 1277 [('I', 'include', [], 'include path in search'),
1278 1278 ('X', 'exclude', [], 'exclude path from search')],
1279 1279 'debugwalk [OPTION]... [FILE]...'),
1280 1280 "^diff":
1281 1281 (diff,
1282 1282 [('r', 'rev', [], 'revision'),
1283 1283 ('I', 'include', [], 'include path in search'),
1284 1284 ('X', 'exclude', [], 'exclude path from search')],
1285 1285 'hg diff [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1286 1286 "^export":
1287 1287 (export,
1288 1288 [('o', 'output', "", 'output to file')],
1289 1289 "hg export [-o OUTFILE] REV..."),
1290 1290 "forget":
1291 1291 (forget,
1292 1292 [('I', 'include', [], 'include path in search'),
1293 1293 ('X', 'exclude', [], 'exclude path from search')],
1294 1294 "hg forget [OPTION]... FILE..."),
1295 1295 "heads":
1296 1296 (heads,
1297 1297 [('b', 'branches', None, 'find branch info')],
1298 1298 'hg [-b] heads'),
1299 1299 "help": (help_, [], 'hg help [COMMAND]'),
1300 1300 "identify|id": (identify, [], 'hg identify'),
1301 1301 "import|patch":
1302 1302 (import_,
1303 1303 [('p', 'strip', 1, 'path strip'),
1304 1304 ('b', 'base', "", 'base path')],
1305 1305 "hg import [-p NUM] [-b BASE] PATCH..."),
1306 1306 "^init": (init, [], 'hg init [DEST]'),
1307 1307 "locate":
1308 1308 (locate,
1309 1309 [('r', 'rev', '', 'revision'),
1310 1310 ('0', 'print0', None, 'end records with NUL'),
1311 1311 ('f', 'fullpath', None, 'print complete paths'),
1312 1312 ('I', 'include', [], 'include path in search'),
1313 1313 ('X', 'exclude', [], 'exclude path from search')],
1314 1314 'hg locate [OPTION]... [PATTERN]...'),
1315 1315 "^log|history":
1316 1316 (log,
1317 1317 [('r', 'rev', [], 'revision'),
1318 1318 ('p', 'patch', None, 'show patch')],
1319 1319 'hg log [-r REV1 [-r REV2]] [-p] [FILE]'),
1320 1320 "manifest": (manifest, [], 'hg manifest [REV]'),
1321 1321 "outgoing": (outgoing, [], 'hg outgoing [DEST]'),
1322 1322 "parents": (parents, [], 'hg parents [REV]'),
1323 1323 "paths": (paths, [], 'hg paths [name]'),
1324 1324 "^pull":
1325 1325 (pull,
1326 1326 [('u', 'update', None, 'update working directory')],
1327 1327 'hg pull [-u] [SOURCE]'),
1328 1328 "^push":
1329 1329 (push,
1330 1330 [('f', 'force', None, 'force push')],
1331 1331 'hg push [-f] [DEST]'),
1332 1332 "rawcommit":
1333 1333 (rawcommit,
1334 1334 [('p', 'parent', [], 'parent'),
1335 1335 ('d', 'date', "", 'date code'),
1336 1336 ('u', 'user', "", 'user'),
1337 1337 ('F', 'files', "", 'file list'),
1338 1338 ('m', 'message', "", 'commit message'),
1339 1339 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1340 1340 ('l', 'logfile', "", 'commit message file')],
1341 1341 'hg rawcommit [OPTION]... [FILE]...'),
1342 1342 "recover": (recover, [], "hg recover"),
1343 1343 "^remove|rm": (remove, [], "hg remove FILE..."),
1344 1344 "^revert":
1345 1345 (revert,
1346 1346 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1347 1347 ("r", "rev", "", "revision")],
1348 1348 "hg revert [-n] [-r REV] [NAME]..."),
1349 1349 "root": (root, [], "hg root"),
1350 1350 "^serve":
1351 1351 (serve,
1352 1352 [('A', 'accesslog', '', 'access log file'),
1353 1353 ('E', 'errorlog', '', 'error log file'),
1354 1354 ('p', 'port', 8000, 'listen port'),
1355 1355 ('a', 'address', '', 'interface address'),
1356 1356 ('n', 'name', os.getcwd(), 'repository name'),
1357 1357 ('', 'stdio', None, 'for remote clients'),
1358 1358 ('t', 'templates', "", 'template map'),
1359 1359 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1360 1360 "hg serve [OPTION]..."),
1361 1361 "^status":
1362 1362 (status,
1363 1363 [('m', 'modified', None, 'show only modified files'),
1364 1364 ('a', 'added', None, 'show only added files'),
1365 1365 ('r', 'removed', None, 'show only removed files'),
1366 1366 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1367 1367 ('I', 'include', [], 'include path in search'),
1368 1368 ('X', 'exclude', [], 'exclude path from search')],
1369 1369 "hg status [OPTION]... [FILE]..."),
1370 1370 "tag":
1371 1371 (tag,
1372 1372 [('l', 'local', None, 'make the tag local'),
1373 1373 ('m', 'message', "", 'commit message'),
1374 1374 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1375 1375 ('d', 'date', "", 'date code'),
1376 1376 ('u', 'user', "", 'user')],
1377 1377 'hg tag [OPTION]... NAME [REV]'),
1378 1378 "tags": (tags, [], 'hg tags'),
1379 1379 "tip": (tip, [], 'hg tip'),
1380 1380 "undo": (undo, [], 'hg undo'),
1381 1381 "^update|up|checkout|co":
1382 1382 (update,
1383 1383 [('b', 'branch', "", 'checkout the head of a specific branch'),
1384 1384 ('m', 'merge', None, 'allow merging of conflicts'),
1385 1385 ('C', 'clean', None, 'overwrite locally modified files')],
1386 1386 'hg update [-b TAG] [-m] [-C] [REV]'),
1387 1387 "verify": (verify, [], 'hg verify'),
1388 1388 "version": (show_version, [], 'hg version'),
1389 1389 }
1390 1390
1391 1391 globalopts = [('v', 'verbose', None, 'verbose mode'),
1392 1392 ('', 'debug', None, 'debug mode'),
1393 1393 ('q', 'quiet', None, 'quiet mode'),
1394 1394 ('', 'profile', None, 'profile'),
1395 1395 ('R', 'repository', "", 'repository root directory'),
1396 1396 ('', 'traceback', None, 'print traceback on exception'),
1397 1397 ('y', 'noninteractive', None, 'run non-interactively'),
1398 1398 ('', 'version', None, 'output version information and exit'),
1399 1399 ('', 'time', None, 'time how long the command takes'),
1400 1400 ]
1401 1401
1402 1402 norepo = "clone init version help debugindex debugindexdot paths"
1403 1403
1404 1404 def find(cmd):
1405 1405 for e in table.keys():
1406 1406 if re.match("(%s)$" % e, cmd):
1407 1407 return e, table[e]
1408 1408
1409 1409 raise UnknownCommand(cmd)
1410 1410
1411 1411 class SignalInterrupt(Exception):
1412 1412 """Exception raised on SIGTERM and SIGHUP."""
1413 1413
1414 1414 def catchterm(*args):
1415 1415 raise SignalInterrupt
1416 1416
1417 1417 def run():
1418 1418 sys.exit(dispatch(sys.argv[1:]))
1419 1419
1420 1420 class ParseError(Exception):
1421 1421 """Exception raised on errors in parsing the command line."""
1422 1422
1423 1423 def parse(args):
1424 1424 options = {}
1425 1425 cmdoptions = {}
1426 1426
1427 1427 try:
1428 1428 args = fancyopts.fancyopts(args, globalopts, options)
1429 1429 except fancyopts.getopt.GetoptError, inst:
1430 1430 raise ParseError(None, inst)
1431 1431
1432 1432 if options["version"]:
1433 1433 return ("version", show_version, [], options, cmdoptions)
1434 1434 elif not args:
1435 1435 return ("help", help_, ["shortlist"], options, cmdoptions)
1436 1436 else:
1437 1437 cmd, args = args[0], args[1:]
1438 1438
1439 1439 i = find(cmd)[1]
1440 1440
1441 1441 # combine global options into local
1442 1442 c = list(i[1])
1443 1443 for o in globalopts:
1444 1444 c.append((o[0], o[1], options[o[1]], o[3]))
1445 1445
1446 1446 try:
1447 1447 args = fancyopts.fancyopts(args, c, cmdoptions)
1448 1448 except fancyopts.getopt.GetoptError, inst:
1449 1449 raise ParseError(cmd, inst)
1450 1450
1451 1451 # separate global options back out
1452 1452 for o in globalopts:
1453 1453 n = o[1]
1454 1454 options[n] = cmdoptions[n]
1455 1455 del cmdoptions[n]
1456 1456
1457 1457 return (cmd, i[0], args, options, cmdoptions)
1458 1458
1459 1459 def dispatch(args):
1460 1460 signal.signal(signal.SIGTERM, catchterm)
1461 1461 try:
1462 1462 signal.signal(signal.SIGHUP, catchterm)
1463 1463 except AttributeError:
1464 1464 pass
1465 1465
1466 1466 try:
1467 1467 cmd, func, args, options, cmdoptions = parse(args)
1468 1468 except ParseError, inst:
1469 1469 u = ui.ui()
1470 1470 if inst.args[0]:
1471 1471 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1472 1472 help_(u, inst.args[0])
1473 1473 else:
1474 1474 u.warn("hg: %s\n" % inst.args[1])
1475 1475 help_(u, 'shortlist')
1476 1476 sys.exit(-1)
1477 1477 except UnknownCommand, inst:
1478 1478 u = ui.ui()
1479 1479 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1480 1480 help_(u, 'shortlist')
1481 1481 sys.exit(1)
1482 1482
1483 1483 if options["time"]:
1484 1484 def get_times():
1485 1485 t = os.times()
1486 1486 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1487 1487 t = (t[0], t[1], t[2], t[3], time.clock())
1488 1488 return t
1489 1489 s = get_times()
1490 1490 def print_time():
1491 1491 t = get_times()
1492 1492 u = ui.ui()
1493 1493 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1494 1494 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1495 1495 atexit.register(print_time)
1496 1496
1497 1497 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1498 1498 not options["noninteractive"])
1499 1499
1500 1500 try:
1501 1501 try:
1502 1502 if cmd not in norepo.split():
1503 1503 path = options["repository"] or ""
1504 1504 repo = hg.repository(ui=u, path=path)
1505 1505 d = lambda: func(u, repo, *args, **cmdoptions)
1506 1506 else:
1507 1507 d = lambda: func(u, *args, **cmdoptions)
1508 1508
1509 1509 if options['profile']:
1510 1510 import hotshot, hotshot.stats
1511 1511 prof = hotshot.Profile("hg.prof")
1512 1512 r = prof.runcall(d)
1513 1513 prof.close()
1514 1514 stats = hotshot.stats.load("hg.prof")
1515 1515 stats.strip_dirs()
1516 1516 stats.sort_stats('time', 'calls')
1517 1517 stats.print_stats(40)
1518 1518 return r
1519 1519 else:
1520 1520 return d()
1521 1521 except:
1522 1522 if options['traceback']:
1523 1523 traceback.print_exc()
1524 1524 raise
1525 1525 except hg.RepoError, inst:
1526 1526 u.warn("abort: ", inst, "!\n")
1527 1527 except SignalInterrupt:
1528 1528 u.warn("killed!\n")
1529 1529 except KeyboardInterrupt:
1530 1530 try:
1531 1531 u.warn("interrupted!\n")
1532 1532 except IOError, inst:
1533 1533 if inst.errno == errno.EPIPE:
1534 1534 if u.debugflag:
1535 1535 u.warn("\nbroken pipe\n")
1536 1536 else:
1537 1537 raise
1538 1538 except IOError, inst:
1539 1539 if hasattr(inst, "code"):
1540 1540 u.warn("abort: %s\n" % inst)
1541 1541 elif hasattr(inst, "reason"):
1542 1542 u.warn("abort: error: %s\n" % inst.reason[1])
1543 1543 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1544 1544 if u.debugflag: u.warn("broken pipe\n")
1545 1545 else:
1546 1546 raise
1547 1547 except OSError, inst:
1548 1548 if hasattr(inst, "filename"):
1549 1549 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1550 1550 else:
1551 1551 u.warn("abort: %s\n" % inst.strerror)
1552 1552 except util.Abort, inst:
1553 1553 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1554 1554 sys.exit(1)
1555 1555 except TypeError, inst:
1556 1556 # was this an argument error?
1557 1557 tb = traceback.extract_tb(sys.exc_info()[2])
1558 1558 if len(tb) > 2: # no
1559 1559 raise
1560 1560 u.debug(inst, "\n")
1561 1561 u.warn("%s: invalid arguments\n" % cmd)
1562 1562 help_(u, cmd)
1563 1563 except UnknownCommand, inst:
1564 1564 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1565 1565 help_(u, 'shortlist')
1566 1566
1567 1567 sys.exit(-1)
General Comments 0
You need to be logged in to leave comments. Login now