##// END OF EJS Templates
Fix incoming for empty set
mpm@selenic.com -
r931:32e8f64b default
parent child Browse files
Show More
@@ -1,1582 +1,1584 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from demandload import demandload
9 9 demandload(globals(), "os re sys signal shutil")
10 10 demandload(globals(), "fancyopts ui hg util lock")
11 11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
12 12 demandload(globals(), "errno socket version struct atexit")
13 13
14 14 class UnknownCommand(Exception):
15 15 """Exception raised if command is not in the command table."""
16 16
17 17 def filterfiles(filters, files):
18 18 l = [x for x in files if x in filters]
19 19
20 20 for t in filters:
21 21 if t and t[-1] != "/":
22 22 t += "/"
23 23 l += [x for x in files if x.startswith(t)]
24 24 return l
25 25
26 26 def relfilter(repo, files):
27 27 cwd = repo.getcwd()
28 28 if cwd:
29 29 return filterfiles([util.pconvert(cwd)], files)
30 30 return files
31 31
32 32 def relpath(repo, args):
33 33 cwd = repo.getcwd()
34 34 if cwd:
35 35 return [util.normpath(os.path.join(cwd, x)) for x in args]
36 36 return args
37 37
38 38 def matchpats(repo, cwd, pats = [], opts = {}, head = ''):
39 39 return util.matcher(repo, cwd, pats or ['.'], opts.get('include'),
40 40 opts.get('exclude'), head)
41 41
42 42 def makewalk(repo, pats, opts, head = ''):
43 43 cwd = repo.getcwd()
44 44 files, matchfn = matchpats(repo, cwd, pats, opts, head)
45 45 def walk():
46 46 for src, fn in repo.walk(files = files, match = matchfn):
47 47 yield src, fn, util.pathto(cwd, fn)
48 48 return files, matchfn, walk()
49 49
50 50 def walk(repo, pats, opts, head = ''):
51 51 files, matchfn, results = makewalk(repo, pats, opts, head)
52 52 for r in results: yield r
53 53
54 54 revrangesep = ':'
55 55
56 56 def revrange(ui, repo, revs, revlog=None):
57 57 if revlog is None:
58 58 revlog = repo.changelog
59 59 revcount = revlog.count()
60 60 def fix(val, defval):
61 61 if not val:
62 62 return defval
63 63 try:
64 64 num = int(val)
65 65 if str(num) != val:
66 66 raise ValueError
67 67 if num < 0:
68 68 num += revcount
69 69 if not (0 <= num < revcount):
70 70 raise ValueError
71 71 except ValueError:
72 72 try:
73 73 num = repo.changelog.rev(repo.lookup(val))
74 74 except KeyError:
75 75 try:
76 76 num = revlog.rev(revlog.lookup(val))
77 77 except KeyError:
78 78 raise util.Abort('invalid revision identifier %s', val)
79 79 return num
80 80 for spec in revs:
81 81 if spec.find(revrangesep) >= 0:
82 82 start, end = spec.split(revrangesep, 1)
83 83 start = fix(start, 0)
84 84 end = fix(end, revcount - 1)
85 85 if end > start:
86 86 end += 1
87 87 step = 1
88 88 else:
89 89 end -= 1
90 90 step = -1
91 91 for rev in xrange(start, end, step):
92 92 yield str(rev)
93 93 else:
94 94 yield spec
95 95
96 96 def make_filename(repo, r, pat, node=None,
97 97 total=None, seqno=None, revwidth=None):
98 98 node_expander = {
99 99 'H': lambda: hg.hex(node),
100 100 'R': lambda: str(r.rev(node)),
101 101 'h': lambda: hg.short(node),
102 102 }
103 103 expander = {
104 104 '%': lambda: '%',
105 105 'b': lambda: os.path.basename(repo.root),
106 106 }
107 107
108 108 try:
109 109 if node:
110 110 expander.update(node_expander)
111 111 if node and revwidth is not None:
112 112 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
113 113 if total is not None:
114 114 expander['N'] = lambda: str(total)
115 115 if seqno is not None:
116 116 expander['n'] = lambda: str(seqno)
117 117 if total is not None and seqno is not None:
118 118 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
119 119
120 120 newname = []
121 121 patlen = len(pat)
122 122 i = 0
123 123 while i < patlen:
124 124 c = pat[i]
125 125 if c == '%':
126 126 i += 1
127 127 c = pat[i]
128 128 c = expander[c]()
129 129 newname.append(c)
130 130 i += 1
131 131 return ''.join(newname)
132 132 except KeyError, inst:
133 133 raise util.Abort("invalid format spec '%%%s' in output file name",
134 134 inst.args[0])
135 135
136 136 def make_file(repo, r, pat, node=None,
137 137 total=None, seqno=None, revwidth=None, mode='wb'):
138 138 if not pat or pat == '-':
139 139 if 'w' in mode: return sys.stdout
140 140 else: return sys.stdin
141 141 if hasattr(pat, 'write') and 'w' in mode:
142 142 return pat
143 143 if hasattr(pat, 'read') and 'r' in mode:
144 144 return pat
145 145 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
146 146 mode)
147 147
148 148 def dodiff(fp, ui, repo, files=None, node1=None, node2=None, match=util.always, changes=None):
149 149 def date(c):
150 150 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
151 151
152 152 if not changes:
153 153 (c, a, d, u) = repo.changes(node1, node2, files, match = match)
154 154 else:
155 155 (c, a, d, u) = changes
156 156 if files:
157 157 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
158 158
159 159 if not c and not a and not d:
160 160 return
161 161
162 162 if node2:
163 163 change = repo.changelog.read(node2)
164 164 mmap2 = repo.manifest.read(change[0])
165 165 date2 = date(change)
166 166 def read(f):
167 167 return repo.file(f).read(mmap2[f])
168 168 else:
169 169 date2 = time.asctime()
170 170 if not node1:
171 171 node1 = repo.dirstate.parents()[0]
172 172 def read(f):
173 173 return repo.wfile(f).read()
174 174
175 175 if ui.quiet:
176 176 r = None
177 177 else:
178 178 hexfunc = ui.verbose and hg.hex or hg.short
179 179 r = [hexfunc(node) for node in [node1, node2] if node]
180 180
181 181 change = repo.changelog.read(node1)
182 182 mmap = repo.manifest.read(change[0])
183 183 date1 = date(change)
184 184
185 185 for f in c:
186 186 to = None
187 187 if f in mmap:
188 188 to = repo.file(f).read(mmap[f])
189 189 tn = read(f)
190 190 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
191 191 for f in a:
192 192 to = None
193 193 tn = read(f)
194 194 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
195 195 for f in d:
196 196 to = repo.file(f).read(mmap[f])
197 197 tn = None
198 198 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
199 199
200 200 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None, brinfo=None):
201 201 """show a single changeset or file revision"""
202 202 changelog = repo.changelog
203 203 if filelog:
204 204 log = filelog
205 205 filerev = rev
206 206 node = filenode = filelog.node(filerev)
207 207 changerev = filelog.linkrev(filenode)
208 208 changenode = changenode or changelog.node(changerev)
209 209 else:
210 210 log = changelog
211 211 changerev = rev
212 212 if changenode is None:
213 213 changenode = changelog.node(changerev)
214 214 elif not changerev:
215 215 rev = changerev = changelog.rev(changenode)
216 216 node = changenode
217 217
218 218 if ui.quiet:
219 219 ui.write("%d:%s\n" % (rev, hg.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 incoming(ui, repo, source="default"):
786 786 """show new changesets found in source"""
787 787 source = ui.expandpath(source)
788 788 other = hg.repository(ui, source)
789 789 if not other.local():
790 790 ui.warn("abort: incoming doesn't work for remote"
791 791 + " repositories yet, sorry!\n")
792 792 return 1
793 793 o = repo.findincoming(other)
794 if not o:
795 return
794 796 o = other.newer(o)
795 797 o.reverse()
796 798 for n in o:
797 799 show_changeset(ui, other, changenode=n)
798 800
799 801 def init(ui, dest="."):
800 802 """create a new repository in the given directory"""
801 803 if not os.path.exists(dest):
802 804 os.mkdir(dest)
803 805 hg.repository(ui, dest, create=1)
804 806
805 807 def locate(ui, repo, *pats, **opts):
806 808 """locate files matching specific patterns"""
807 809 end = '\n'
808 810 if opts['print0']: end = '\0'
809 811
810 812 for src, abs, rel in walk(repo, pats, opts, '(?:.*/|)'):
811 813 if repo.dirstate.state(abs) == '?': continue
812 814 if opts['fullpath']:
813 815 ui.write(os.path.join(repo.root, abs), end)
814 816 else:
815 817 ui.write(rel, end)
816 818
817 819 def log(ui, repo, f=None, **opts):
818 820 """show the revision history of the repository or a single file"""
819 821 if f:
820 822 files = relpath(repo, [f])
821 823 filelog = repo.file(files[0])
822 824 log = filelog
823 825 lookup = filelog.lookup
824 826 else:
825 827 files = None
826 828 filelog = None
827 829 log = repo.changelog
828 830 lookup = repo.lookup
829 831 revlist = []
830 832 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
831 833 while revs:
832 834 if len(revs) == 1:
833 835 revlist.append(revs.pop(0))
834 836 else:
835 837 a = revs.pop(0)
836 838 b = revs.pop(0)
837 839 off = a > b and -1 or 1
838 840 revlist.extend(range(a, b + off, off))
839 841
840 842 for i in revlist or range(log.count() - 1, -1, -1):
841 843 show_changeset(ui, repo, filelog=filelog, rev=i)
842 844 if opts['patch']:
843 845 if filelog:
844 846 filenode = filelog.node(i)
845 847 i = filelog.linkrev(filenode)
846 848 changenode = repo.changelog.node(i)
847 849 prev, other = repo.changelog.parents(changenode)
848 850 dodiff(sys.stdout, ui, repo, files, prev, changenode)
849 851 ui.write("\n\n")
850 852
851 853 def manifest(ui, repo, rev=None):
852 854 """output the latest or given revision of the project manifest"""
853 855 if rev:
854 856 try:
855 857 # assume all revision numbers are for changesets
856 858 n = repo.lookup(rev)
857 859 change = repo.changelog.read(n)
858 860 n = change[0]
859 861 except hg.RepoError:
860 862 n = repo.manifest.lookup(rev)
861 863 else:
862 864 n = repo.manifest.tip()
863 865 m = repo.manifest.read(n)
864 866 mf = repo.manifest.readflags(n)
865 867 files = m.keys()
866 868 files.sort()
867 869
868 870 for f in files:
869 871 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
870 872
871 873 def outgoing(ui, repo, dest="default-push"):
872 874 """show changesets not found in destination"""
873 875 dest = ui.expandpath(dest)
874 876 other = hg.repository(ui, dest)
875 877 o = repo.findoutgoing(other)
876 878 o = repo.newer(o)
877 879 o.reverse()
878 880 for n in o:
879 881 show_changeset(ui, repo, changenode=n)
880 882
881 883 def parents(ui, repo, rev=None):
882 884 """show the parents of the working dir or revision"""
883 885 if rev:
884 886 p = repo.changelog.parents(repo.lookup(rev))
885 887 else:
886 888 p = repo.dirstate.parents()
887 889
888 890 for n in p:
889 891 if n != hg.nullid:
890 892 show_changeset(ui, repo, changenode=n)
891 893
892 894 def paths(ui, search = None):
893 895 """show definition of symbolic path names"""
894 896 try:
895 897 repo = hg.repository(ui=ui)
896 898 except:
897 899 pass
898 900
899 901 if search:
900 902 for name, path in ui.configitems("paths"):
901 903 if name == search:
902 904 ui.write("%s\n" % path)
903 905 return
904 906 ui.warn("not found!\n")
905 907 return 1
906 908 else:
907 909 for name, path in ui.configitems("paths"):
908 910 ui.write("%s = %s\n" % (name, path))
909 911
910 912 def pull(ui, repo, source="default", **opts):
911 913 """pull changes from the specified source"""
912 914 source = ui.expandpath(source)
913 915 ui.status('pulling from %s\n' % (source))
914 916
915 917 other = hg.repository(ui, source)
916 918 r = repo.pull(other)
917 919 if not r:
918 920 if opts['update']:
919 921 return update(ui, repo)
920 922 else:
921 923 ui.status("(run 'hg update' to get a working copy)\n")
922 924
923 925 return r
924 926
925 927 def push(ui, repo, dest="default-push", force=False):
926 928 """push changes to the specified destination"""
927 929 dest = ui.expandpath(dest)
928 930 ui.status('pushing to %s\n' % (dest))
929 931
930 932 other = hg.repository(ui, dest)
931 933 r = repo.push(other, force)
932 934 return r
933 935
934 936 def rawcommit(ui, repo, *flist, **rc):
935 937 "raw commit interface"
936 938 if rc['text']:
937 939 ui.warn("Warning: -t and --text is deprecated,"
938 940 " please use -m or --message instead.\n")
939 941 message = rc['message'] or rc['text']
940 942 if not message and rc['logfile']:
941 943 try:
942 944 message = open(rc['logfile']).read()
943 945 except IOError:
944 946 pass
945 947 if not message and not rc['logfile']:
946 948 ui.warn("abort: missing commit message\n")
947 949 return 1
948 950
949 951 files = relpath(repo, list(flist))
950 952 if rc['files']:
951 953 files += open(rc['files']).read().splitlines()
952 954
953 955 rc['parent'] = map(repo.lookup, rc['parent'])
954 956
955 957 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
956 958
957 959 def recover(ui, repo):
958 960 """roll back an interrupted transaction"""
959 961 repo.recover()
960 962
961 963 def remove(ui, repo, file1, *files):
962 964 """remove the specified files on the next commit"""
963 965 repo.remove(relpath(repo, (file1,) + files))
964 966
965 967 def revert(ui, repo, *names, **opts):
966 968 """revert modified files or dirs back to their unmodified states"""
967 969 node = opts['rev'] and repo.lookup(opts['rev']) or \
968 970 repo.dirstate.parents()[0]
969 971 root = os.path.realpath(repo.root)
970 972
971 973 def trimpath(p):
972 974 p = os.path.realpath(p)
973 975 if p.startswith(root):
974 976 rest = p[len(root):]
975 977 if not rest:
976 978 return rest
977 979 if p.startswith(os.sep):
978 980 return rest[1:]
979 981 return p
980 982
981 983 relnames = map(trimpath, names or [os.getcwd()])
982 984 chosen = {}
983 985
984 986 def choose(name):
985 987 def body(name):
986 988 for r in relnames:
987 989 if not name.startswith(r):
988 990 continue
989 991 rest = name[len(r):]
990 992 if not rest:
991 993 return r, True
992 994 depth = rest.count(os.sep)
993 995 if not r:
994 996 if depth == 0 or not opts['nonrecursive']:
995 997 return r, True
996 998 elif rest[0] == os.sep:
997 999 if depth == 1 or not opts['nonrecursive']:
998 1000 return r, True
999 1001 return None, False
1000 1002 relname, ret = body(name)
1001 1003 if ret:
1002 1004 chosen[relname] = 1
1003 1005 return ret
1004 1006
1005 1007 r = repo.update(node, False, True, choose, False)
1006 1008 for n in relnames:
1007 1009 if n not in chosen:
1008 1010 ui.warn('error: no matches for %s\n' % n)
1009 1011 r = 1
1010 1012 sys.stdout.flush()
1011 1013 return r
1012 1014
1013 1015 def root(ui, repo):
1014 1016 """print the root (top) of the current working dir"""
1015 1017 ui.write(repo.root + "\n")
1016 1018
1017 1019 def serve(ui, repo, **opts):
1018 1020 """export the repository via HTTP"""
1019 1021
1020 1022 if opts["stdio"]:
1021 1023 fin, fout = sys.stdin, sys.stdout
1022 1024 sys.stdout = sys.stderr
1023 1025
1024 1026 def getarg():
1025 1027 argline = fin.readline()[:-1]
1026 1028 arg, l = argline.split()
1027 1029 val = fin.read(int(l))
1028 1030 return arg, val
1029 1031 def respond(v):
1030 1032 fout.write("%d\n" % len(v))
1031 1033 fout.write(v)
1032 1034 fout.flush()
1033 1035
1034 1036 lock = None
1035 1037
1036 1038 while 1:
1037 1039 cmd = fin.readline()[:-1]
1038 1040 if cmd == '':
1039 1041 return
1040 1042 if cmd == "heads":
1041 1043 h = repo.heads()
1042 1044 respond(" ".join(map(hg.hex, h)) + "\n")
1043 1045 if cmd == "lock":
1044 1046 lock = repo.lock()
1045 1047 respond("")
1046 1048 if cmd == "unlock":
1047 1049 if lock:
1048 1050 lock.release()
1049 1051 lock = None
1050 1052 respond("")
1051 1053 elif cmd == "branches":
1052 1054 arg, nodes = getarg()
1053 1055 nodes = map(hg.bin, nodes.split(" "))
1054 1056 r = []
1055 1057 for b in repo.branches(nodes):
1056 1058 r.append(" ".join(map(hg.hex, b)) + "\n")
1057 1059 respond("".join(r))
1058 1060 elif cmd == "between":
1059 1061 arg, pairs = getarg()
1060 1062 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
1061 1063 r = []
1062 1064 for b in repo.between(pairs):
1063 1065 r.append(" ".join(map(hg.hex, b)) + "\n")
1064 1066 respond("".join(r))
1065 1067 elif cmd == "changegroup":
1066 1068 nodes = []
1067 1069 arg, roots = getarg()
1068 1070 nodes = map(hg.bin, roots.split(" "))
1069 1071
1070 1072 cg = repo.changegroup(nodes)
1071 1073 while 1:
1072 1074 d = cg.read(4096)
1073 1075 if not d:
1074 1076 break
1075 1077 fout.write(d)
1076 1078
1077 1079 fout.flush()
1078 1080
1079 1081 elif cmd == "addchangegroup":
1080 1082 if not lock:
1081 1083 respond("not locked")
1082 1084 continue
1083 1085 respond("")
1084 1086
1085 1087 r = repo.addchangegroup(fin)
1086 1088 respond("")
1087 1089
1088 1090 def openlog(opt, default):
1089 1091 if opts[opt] and opts[opt] != '-':
1090 1092 return open(opts[opt], 'w')
1091 1093 else:
1092 1094 return default
1093 1095
1094 1096 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
1095 1097 opts["address"], opts["port"], opts["ipv6"],
1096 1098 openlog('accesslog', sys.stdout),
1097 1099 openlog('errorlog', sys.stderr))
1098 1100 if ui.verbose:
1099 1101 addr, port = httpd.socket.getsockname()
1100 1102 if addr == '0.0.0.0':
1101 1103 addr = socket.gethostname()
1102 1104 else:
1103 1105 try:
1104 1106 addr = socket.gethostbyaddr(addr)[0]
1105 1107 except socket.error:
1106 1108 pass
1107 1109 if port != 80:
1108 1110 ui.status('listening at http://%s:%d/\n' % (addr, port))
1109 1111 else:
1110 1112 ui.status('listening at http://%s/\n' % addr)
1111 1113 httpd.serve_forever()
1112 1114
1113 1115 def status(ui, repo, *pats, **opts):
1114 1116 '''show changed files in the working directory
1115 1117
1116 1118 M = modified
1117 1119 A = added
1118 1120 R = removed
1119 1121 ? = not tracked
1120 1122 '''
1121 1123
1122 1124 cwd = repo.getcwd()
1123 1125 files, matchfn = matchpats(repo, cwd, pats, opts)
1124 1126 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1125 1127 for n in repo.changes(files=files, match=matchfn)]
1126 1128
1127 1129 changetypes = [('modified', 'M', c),
1128 1130 ('added', 'A', a),
1129 1131 ('removed', 'R', d),
1130 1132 ('unknown', '?', u)]
1131 1133
1132 1134 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1133 1135 or changetypes):
1134 1136 for f in changes:
1135 1137 ui.write("%s %s\n" % (char, f))
1136 1138
1137 1139 def tag(ui, repo, name, rev=None, **opts):
1138 1140 """add a tag for the current tip or a given revision"""
1139 1141 if opts['text']:
1140 1142 ui.warn("Warning: -t and --text is deprecated,"
1141 1143 " please use -m or --message instead.\n")
1142 1144 if name == "tip":
1143 1145 ui.warn("abort: 'tip' is a reserved name!\n")
1144 1146 return -1
1145 1147 if rev:
1146 1148 r = hg.hex(repo.lookup(rev))
1147 1149 else:
1148 1150 r = hg.hex(repo.changelog.tip())
1149 1151
1150 1152 if name.find(revrangesep) >= 0:
1151 1153 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1152 1154 return -1
1153 1155
1154 1156 if opts['local']:
1155 1157 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1156 1158 return
1157 1159
1158 1160 (c, a, d, u) = repo.changes()
1159 1161 for x in (c, a, d, u):
1160 1162 if ".hgtags" in x:
1161 1163 ui.warn("abort: working copy of .hgtags is changed!\n")
1162 1164 ui.status("(please commit .hgtags manually)\n")
1163 1165 return -1
1164 1166
1165 1167 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1166 1168 if repo.dirstate.state(".hgtags") == '?':
1167 1169 repo.add([".hgtags"])
1168 1170
1169 1171 message = (opts['message'] or opts['text'] or
1170 1172 "Added tag %s for changeset %s" % (name, r))
1171 1173 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1172 1174
1173 1175 def tags(ui, repo):
1174 1176 """list repository tags"""
1175 1177
1176 1178 l = repo.tagslist()
1177 1179 l.reverse()
1178 1180 for t, n in l:
1179 1181 try:
1180 1182 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1181 1183 except KeyError:
1182 1184 r = " ?:?"
1183 1185 ui.write("%-30s %s\n" % (t, r))
1184 1186
1185 1187 def tip(ui, repo):
1186 1188 """show the tip revision"""
1187 1189 n = repo.changelog.tip()
1188 1190 show_changeset(ui, repo, changenode=n)
1189 1191
1190 1192 def undo(ui, repo):
1191 1193 """undo the last commit or pull
1192 1194
1193 1195 Roll back the last pull or commit transaction on the
1194 1196 repository, restoring the project to its earlier state.
1195 1197
1196 1198 This command should be used with care. There is only one level of
1197 1199 undo and there is no redo.
1198 1200
1199 1201 This command is not intended for use on public repositories. Once
1200 1202 a change is visible for pull by other users, undoing it locally is
1201 1203 ineffective.
1202 1204 """
1203 1205 repo.undo()
1204 1206
1205 1207 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1206 1208 '''update or merge working directory
1207 1209
1208 1210 If there are no outstanding changes in the working directory and
1209 1211 there is a linear relationship between the current version and the
1210 1212 requested version, the result is the requested version.
1211 1213
1212 1214 Otherwise the result is a merge between the contents of the
1213 1215 current working directory and the requested version. Files that
1214 1216 changed between either parent are marked as changed for the next
1215 1217 commit and a commit must be performed before any further updates
1216 1218 are allowed.
1217 1219 '''
1218 1220 if branch:
1219 1221 br = repo.branchlookup(branch=branch)
1220 1222 found = []
1221 1223 for x in br:
1222 1224 if branch in br[x]:
1223 1225 found.append(x)
1224 1226 if len(found) > 1:
1225 1227 ui.warn("Found multiple heads for %s\n" % branch)
1226 1228 for x in found:
1227 1229 show_changeset(ui, repo, changenode=x, brinfo=br)
1228 1230 return 1
1229 1231 if len(found) == 1:
1230 1232 node = found[0]
1231 1233 ui.warn("Using head %s for branch %s\n" % (hg.short(node), branch))
1232 1234 else:
1233 1235 ui.warn("branch %s not found\n" % (branch))
1234 1236 return 1
1235 1237 else:
1236 1238 node = node and repo.lookup(node) or repo.changelog.tip()
1237 1239 return repo.update(node, allow=merge, force=clean)
1238 1240
1239 1241 def verify(ui, repo):
1240 1242 """verify the integrity of the repository"""
1241 1243 return repo.verify()
1242 1244
1243 1245 # Command options and aliases are listed here, alphabetically
1244 1246
1245 1247 table = {
1246 1248 "^add":
1247 1249 (add,
1248 1250 [('I', 'include', [], 'include path in search'),
1249 1251 ('X', 'exclude', [], 'exclude path from search')],
1250 1252 "hg add [OPTION]... [FILE]..."),
1251 1253 "addremove":
1252 1254 (addremove,
1253 1255 [('I', 'include', [], 'include path in search'),
1254 1256 ('X', 'exclude', [], 'exclude path from search')],
1255 1257 "hg addremove [OPTION]... [FILE]..."),
1256 1258 "^annotate":
1257 1259 (annotate,
1258 1260 [('r', 'rev', '', 'revision'),
1259 1261 ('u', 'user', None, 'show user'),
1260 1262 ('n', 'number', None, 'show revision number'),
1261 1263 ('c', 'changeset', None, 'show changeset'),
1262 1264 ('I', 'include', [], 'include path in search'),
1263 1265 ('X', 'exclude', [], 'exclude path from search')],
1264 1266 'hg annotate [OPTION]... FILE...'),
1265 1267 "cat":
1266 1268 (cat,
1267 1269 [('o', 'output', "", 'output to file')],
1268 1270 'hg cat [-o OUTFILE] FILE [REV]'),
1269 1271 "^clone":
1270 1272 (clone,
1271 1273 [('U', 'noupdate', None, 'skip update after cloning')],
1272 1274 'hg clone [-U] SOURCE [DEST]'),
1273 1275 "^commit|ci":
1274 1276 (commit,
1275 1277 [('A', 'addremove', None, 'run add/remove during commit'),
1276 1278 ('I', 'include', [], 'include path in search'),
1277 1279 ('X', 'exclude', [], 'exclude path from search'),
1278 1280 ('m', 'message', "", 'commit message'),
1279 1281 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1280 1282 ('l', 'logfile', "", 'commit message file'),
1281 1283 ('d', 'date', "", 'date code'),
1282 1284 ('u', 'user', "", 'user')],
1283 1285 'hg commit [OPTION]... [FILE]...'),
1284 1286 "copy": (copy, [], 'hg copy SOURCE DEST'),
1285 1287 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1286 1288 "debugstate": (debugstate, [], 'debugstate'),
1287 1289 "debugindex": (debugindex, [], 'debugindex FILE'),
1288 1290 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1289 1291 "debugwalk":
1290 1292 (debugwalk,
1291 1293 [('I', 'include', [], 'include path in search'),
1292 1294 ('X', 'exclude', [], 'exclude path from search')],
1293 1295 'debugwalk [OPTION]... [FILE]...'),
1294 1296 "^diff":
1295 1297 (diff,
1296 1298 [('r', 'rev', [], 'revision'),
1297 1299 ('I', 'include', [], 'include path in search'),
1298 1300 ('X', 'exclude', [], 'exclude path from search')],
1299 1301 'hg diff [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1300 1302 "^export":
1301 1303 (export,
1302 1304 [('o', 'output', "", 'output to file')],
1303 1305 "hg export [-o OUTFILE] REV..."),
1304 1306 "forget":
1305 1307 (forget,
1306 1308 [('I', 'include', [], 'include path in search'),
1307 1309 ('X', 'exclude', [], 'exclude path from search')],
1308 1310 "hg forget [OPTION]... FILE..."),
1309 1311 "heads":
1310 1312 (heads,
1311 1313 [('b', 'branches', None, 'find branch info')],
1312 1314 'hg [-b] heads'),
1313 1315 "help": (help_, [], 'hg help [COMMAND]'),
1314 1316 "identify|id": (identify, [], 'hg identify'),
1315 1317 "import|patch":
1316 1318 (import_,
1317 1319 [('p', 'strip', 1, 'path strip'),
1318 1320 ('b', 'base', "", 'base path')],
1319 1321 "hg import [-p NUM] [-b BASE] PATCH..."),
1320 1322 "incoming": (incoming, [], 'hg incoming [SOURCE]'),
1321 1323 "^init": (init, [], 'hg init [DEST]'),
1322 1324 "locate":
1323 1325 (locate,
1324 1326 [('r', 'rev', '', 'revision'),
1325 1327 ('0', 'print0', None, 'end records with NUL'),
1326 1328 ('f', 'fullpath', None, 'print complete paths'),
1327 1329 ('I', 'include', [], 'include path in search'),
1328 1330 ('X', 'exclude', [], 'exclude path from search')],
1329 1331 'hg locate [OPTION]... [PATTERN]...'),
1330 1332 "^log|history":
1331 1333 (log,
1332 1334 [('r', 'rev', [], 'revision'),
1333 1335 ('p', 'patch', None, 'show patch')],
1334 1336 'hg log [-r REV1 [-r REV2]] [-p] [FILE]'),
1335 1337 "manifest": (manifest, [], 'hg manifest [REV]'),
1336 1338 "outgoing": (outgoing, [], 'hg outgoing [DEST]'),
1337 1339 "parents": (parents, [], 'hg parents [REV]'),
1338 1340 "paths": (paths, [], 'hg paths [NAME]'),
1339 1341 "^pull":
1340 1342 (pull,
1341 1343 [('u', 'update', None, 'update working directory')],
1342 1344 'hg pull [-u] [SOURCE]'),
1343 1345 "^push":
1344 1346 (push,
1345 1347 [('f', 'force', None, 'force push')],
1346 1348 'hg push [-f] [DEST]'),
1347 1349 "rawcommit":
1348 1350 (rawcommit,
1349 1351 [('p', 'parent', [], 'parent'),
1350 1352 ('d', 'date', "", 'date code'),
1351 1353 ('u', 'user', "", 'user'),
1352 1354 ('F', 'files', "", 'file list'),
1353 1355 ('m', 'message', "", 'commit message'),
1354 1356 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1355 1357 ('l', 'logfile', "", 'commit message file')],
1356 1358 'hg rawcommit [OPTION]... [FILE]...'),
1357 1359 "recover": (recover, [], "hg recover"),
1358 1360 "^remove|rm": (remove, [], "hg remove FILE..."),
1359 1361 "^revert":
1360 1362 (revert,
1361 1363 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1362 1364 ("r", "rev", "", "revision")],
1363 1365 "hg revert [-n] [-r REV] [NAME]..."),
1364 1366 "root": (root, [], "hg root"),
1365 1367 "^serve":
1366 1368 (serve,
1367 1369 [('A', 'accesslog', '', 'access log file'),
1368 1370 ('E', 'errorlog', '', 'error log file'),
1369 1371 ('p', 'port', 8000, 'listen port'),
1370 1372 ('a', 'address', '', 'interface address'),
1371 1373 ('n', 'name', os.getcwd(), 'repository name'),
1372 1374 ('', 'stdio', None, 'for remote clients'),
1373 1375 ('t', 'templates', "", 'template map'),
1374 1376 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1375 1377 "hg serve [OPTION]..."),
1376 1378 "^status":
1377 1379 (status,
1378 1380 [('m', 'modified', None, 'show only modified files'),
1379 1381 ('a', 'added', None, 'show only added files'),
1380 1382 ('r', 'removed', None, 'show only removed files'),
1381 1383 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1382 1384 ('I', 'include', [], 'include path in search'),
1383 1385 ('X', 'exclude', [], 'exclude path from search')],
1384 1386 "hg status [OPTION]... [FILE]..."),
1385 1387 "tag":
1386 1388 (tag,
1387 1389 [('l', 'local', None, 'make the tag local'),
1388 1390 ('m', 'message', "", 'commit message'),
1389 1391 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1390 1392 ('d', 'date', "", 'date code'),
1391 1393 ('u', 'user', "", 'user')],
1392 1394 'hg tag [OPTION]... NAME [REV]'),
1393 1395 "tags": (tags, [], 'hg tags'),
1394 1396 "tip": (tip, [], 'hg tip'),
1395 1397 "undo": (undo, [], 'hg undo'),
1396 1398 "^update|up|checkout|co":
1397 1399 (update,
1398 1400 [('b', 'branch', "", 'checkout the head of a specific branch'),
1399 1401 ('m', 'merge', None, 'allow merging of conflicts'),
1400 1402 ('C', 'clean', None, 'overwrite locally modified files')],
1401 1403 'hg update [-b TAG] [-m] [-C] [REV]'),
1402 1404 "verify": (verify, [], 'hg verify'),
1403 1405 "version": (show_version, [], 'hg version'),
1404 1406 }
1405 1407
1406 1408 globalopts = [('v', 'verbose', None, 'verbose mode'),
1407 1409 ('', 'debug', None, 'debug mode'),
1408 1410 ('q', 'quiet', None, 'quiet mode'),
1409 1411 ('', 'profile', None, 'profile'),
1410 1412 ('R', 'repository', "", 'repository root directory'),
1411 1413 ('', 'traceback', None, 'print traceback on exception'),
1412 1414 ('y', 'noninteractive', None, 'run non-interactively'),
1413 1415 ('', 'version', None, 'output version information and exit'),
1414 1416 ('', 'time', None, 'time how long the command takes'),
1415 1417 ]
1416 1418
1417 1419 norepo = "clone init version help debugindex debugindexdot paths"
1418 1420
1419 1421 def find(cmd):
1420 1422 for e in table.keys():
1421 1423 if re.match("(%s)$" % e, cmd):
1422 1424 return e, table[e]
1423 1425
1424 1426 raise UnknownCommand(cmd)
1425 1427
1426 1428 class SignalInterrupt(Exception):
1427 1429 """Exception raised on SIGTERM and SIGHUP."""
1428 1430
1429 1431 def catchterm(*args):
1430 1432 raise SignalInterrupt
1431 1433
1432 1434 def run():
1433 1435 sys.exit(dispatch(sys.argv[1:]))
1434 1436
1435 1437 class ParseError(Exception):
1436 1438 """Exception raised on errors in parsing the command line."""
1437 1439
1438 1440 def parse(args):
1439 1441 options = {}
1440 1442 cmdoptions = {}
1441 1443
1442 1444 try:
1443 1445 args = fancyopts.fancyopts(args, globalopts, options)
1444 1446 except fancyopts.getopt.GetoptError, inst:
1445 1447 raise ParseError(None, inst)
1446 1448
1447 1449 if options["version"]:
1448 1450 return ("version", show_version, [], options, cmdoptions)
1449 1451 elif not args:
1450 1452 return ("help", help_, ["shortlist"], options, cmdoptions)
1451 1453 else:
1452 1454 cmd, args = args[0], args[1:]
1453 1455
1454 1456 i = find(cmd)[1]
1455 1457
1456 1458 # combine global options into local
1457 1459 c = list(i[1])
1458 1460 for o in globalopts:
1459 1461 c.append((o[0], o[1], options[o[1]], o[3]))
1460 1462
1461 1463 try:
1462 1464 args = fancyopts.fancyopts(args, c, cmdoptions)
1463 1465 except fancyopts.getopt.GetoptError, inst:
1464 1466 raise ParseError(cmd, inst)
1465 1467
1466 1468 # separate global options back out
1467 1469 for o in globalopts:
1468 1470 n = o[1]
1469 1471 options[n] = cmdoptions[n]
1470 1472 del cmdoptions[n]
1471 1473
1472 1474 return (cmd, i[0], args, options, cmdoptions)
1473 1475
1474 1476 def dispatch(args):
1475 1477 signal.signal(signal.SIGTERM, catchterm)
1476 1478 try:
1477 1479 signal.signal(signal.SIGHUP, catchterm)
1478 1480 except AttributeError:
1479 1481 pass
1480 1482
1481 1483 try:
1482 1484 cmd, func, args, options, cmdoptions = parse(args)
1483 1485 except ParseError, inst:
1484 1486 u = ui.ui()
1485 1487 if inst.args[0]:
1486 1488 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1487 1489 help_(u, inst.args[0])
1488 1490 else:
1489 1491 u.warn("hg: %s\n" % inst.args[1])
1490 1492 help_(u, 'shortlist')
1491 1493 sys.exit(-1)
1492 1494 except UnknownCommand, inst:
1493 1495 u = ui.ui()
1494 1496 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1495 1497 help_(u, 'shortlist')
1496 1498 sys.exit(1)
1497 1499
1498 1500 if options["time"]:
1499 1501 def get_times():
1500 1502 t = os.times()
1501 1503 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1502 1504 t = (t[0], t[1], t[2], t[3], time.clock())
1503 1505 return t
1504 1506 s = get_times()
1505 1507 def print_time():
1506 1508 t = get_times()
1507 1509 u = ui.ui()
1508 1510 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1509 1511 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1510 1512 atexit.register(print_time)
1511 1513
1512 1514 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1513 1515 not options["noninteractive"])
1514 1516
1515 1517 try:
1516 1518 try:
1517 1519 if cmd not in norepo.split():
1518 1520 path = options["repository"] or ""
1519 1521 repo = hg.repository(ui=u, path=path)
1520 1522 d = lambda: func(u, repo, *args, **cmdoptions)
1521 1523 else:
1522 1524 d = lambda: func(u, *args, **cmdoptions)
1523 1525
1524 1526 if options['profile']:
1525 1527 import hotshot, hotshot.stats
1526 1528 prof = hotshot.Profile("hg.prof")
1527 1529 r = prof.runcall(d)
1528 1530 prof.close()
1529 1531 stats = hotshot.stats.load("hg.prof")
1530 1532 stats.strip_dirs()
1531 1533 stats.sort_stats('time', 'calls')
1532 1534 stats.print_stats(40)
1533 1535 return r
1534 1536 else:
1535 1537 return d()
1536 1538 except:
1537 1539 if options['traceback']:
1538 1540 traceback.print_exc()
1539 1541 raise
1540 1542 except hg.RepoError, inst:
1541 1543 u.warn("abort: ", inst, "!\n")
1542 1544 except SignalInterrupt:
1543 1545 u.warn("killed!\n")
1544 1546 except KeyboardInterrupt:
1545 1547 try:
1546 1548 u.warn("interrupted!\n")
1547 1549 except IOError, inst:
1548 1550 if inst.errno == errno.EPIPE:
1549 1551 if u.debugflag:
1550 1552 u.warn("\nbroken pipe\n")
1551 1553 else:
1552 1554 raise
1553 1555 except IOError, inst:
1554 1556 if hasattr(inst, "code"):
1555 1557 u.warn("abort: %s\n" % inst)
1556 1558 elif hasattr(inst, "reason"):
1557 1559 u.warn("abort: error: %s\n" % inst.reason[1])
1558 1560 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1559 1561 if u.debugflag: u.warn("broken pipe\n")
1560 1562 else:
1561 1563 raise
1562 1564 except OSError, inst:
1563 1565 if hasattr(inst, "filename"):
1564 1566 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1565 1567 else:
1566 1568 u.warn("abort: %s\n" % inst.strerror)
1567 1569 except util.Abort, inst:
1568 1570 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1569 1571 sys.exit(1)
1570 1572 except TypeError, inst:
1571 1573 # was this an argument error?
1572 1574 tb = traceback.extract_tb(sys.exc_info()[2])
1573 1575 if len(tb) > 2: # no
1574 1576 raise
1575 1577 u.debug(inst, "\n")
1576 1578 u.warn("%s: invalid arguments\n" % cmd)
1577 1579 help_(u, cmd)
1578 1580 except UnknownCommand, inst:
1579 1581 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1580 1582 help_(u, 'shortlist')
1581 1583
1582 1584 sys.exit(-1)
General Comments 0
You need to be logged in to leave comments. Login now