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