##// END OF EJS Templates
Commands cleanup...
mpm@selenic.com -
r437:5b38a5af default
parent child Browse files
Show More
@@ -1,841 +1,828 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 import os, re, sys, signal
9 9 import fancyopts, ui, hg, util
10 10 from demandload import *
11 11 demandload(globals(), "mdiff time hgweb traceback random signal errno version")
12 12
13 13 class UnknownCommand(Exception): pass
14 14
15 15 def filterfiles(filters, files):
16 16 l = [ x for x in files if x in filters ]
17 17
18 18 for t in filters:
19 19 if t and t[-1] != "/": t += "/"
20 20 l += [ x for x in files if x.startswith(t) ]
21 21 return l
22 22
23 23 def relfilter(repo, files):
24 24 if os.getcwd() != repo.root:
25 25 p = os.getcwd()[len(repo.root) + 1: ]
26 26 return filterfiles([util.pconvert(p)], files)
27 27 return files
28 28
29 29 def relpath(repo, args):
30 30 if os.getcwd() != repo.root:
31 31 p = os.getcwd()[len(repo.root) + 1: ]
32 32 return [ util.pconvert(os.path.normpath(os.path.join(p, x))) for x in args ]
33 33 return args
34 34
35 35 def dodiff(ui, repo, path, files = None, node1 = None, node2 = None):
36 36 def date(c):
37 37 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
38 38
39 39 if node2:
40 40 change = repo.changelog.read(node2)
41 41 mmap2 = repo.manifest.read(change[0])
42 42 (c, a, d) = repo.diffrevs(node1, node2)
43 43 def read(f): return repo.file(f).read(mmap2[f])
44 44 date2 = date(change)
45 45 else:
46 46 date2 = time.asctime()
47 47 (c, a, d, u) = repo.diffdir(path, node1)
48 48 if not node1:
49 49 node1 = repo.dirstate.parents()[0]
50 50 def read(f): return repo.wfile(f).read()
51 51
52 52 if ui.quiet:
53 53 r = None
54 54 else:
55 55 hexfunc = ui.verbose and hg.hex or hg.short
56 56 r = [hexfunc(node) for node in [node1, node2] if node]
57 57
58 58 change = repo.changelog.read(node1)
59 59 mmap = repo.manifest.read(change[0])
60 60 date1 = date(change)
61 61
62 62 if files:
63 63 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
64 64
65 65 for f in c:
66 66 to = None
67 67 if f in mmap:
68 68 to = repo.file(f).read(mmap[f])
69 69 tn = read(f)
70 70 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
71 71 for f in a:
72 72 to = None
73 73 tn = read(f)
74 74 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
75 75 for f in d:
76 76 to = repo.file(f).read(mmap[f])
77 77 tn = None
78 78 sys.stdout.write(mdiff.unidiff(to, date1, tn, date2, f, r))
79 79
80 80 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
81 81 """show a single changeset or file revision"""
82 82 changelog = repo.changelog
83 83 if filelog:
84 84 log = filelog
85 85 filerev = rev
86 86 node = filenode = filelog.node(filerev)
87 87 changerev = filelog.linkrev(filenode)
88 88 changenode = changenode or changelog.node(changerev)
89 89 else:
90 90 log = changelog
91 91 changerev = rev
92 92 if changenode is None:
93 93 changenode = changelog.node(changerev)
94 94 elif not changerev:
95 95 rev = changerev = changelog.rev(changenode)
96 96 node = changenode
97 97
98 98 if ui.quiet:
99 99 ui.write("%d:%s\n" % (rev, hg.hex(node)))
100 100 return
101 101
102 102 changes = changelog.read(changenode)
103 103
104 104 parents = [(log.rev(parent), hg.hex(parent))
105 105 for parent in log.parents(node)
106 106 if ui.debugflag or parent != hg.nullid]
107 107 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
108 108 parents = []
109 109
110 110 if filelog:
111 111 ui.write("revision: %d:%s\n" % (filerev, hg.hex(filenode)))
112 112 for parent in parents:
113 113 ui.write("parent: %d:%s\n" % parent)
114 114 ui.status("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
115 115 else:
116 116 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
117 117 for tag in repo.nodetags(changenode):
118 118 ui.status("tag: %s\n" % tag)
119 119 for parent in parents:
120 120 ui.write("parent: %d:%s\n" % parent)
121 121 ui.note("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
122 122 hg.hex(changes[0])))
123 123 ui.status("user: %s\n" % changes[1])
124 124 ui.status("date: %s\n" % time.asctime(
125 125 time.localtime(float(changes[2].split(' ')[0]))))
126 126 ui.note("files: %s\n" % " ".join(changes[3]))
127 127 description = changes[4].strip()
128 128 if description:
129 129 if ui.verbose:
130 130 ui.status("description:\n")
131 131 ui.status(description)
132 132 ui.status("\n")
133 133 else:
134 134 ui.status("summary: %s\n" % description.splitlines()[0])
135 135 ui.status("\n")
136 136
137 137 def show_version(ui):
138 138 """output version and copyright information"""
139 139 ui.write("Mercurial version %s\n" % version.get_version())
140 140 ui.status(
141 141 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
142 142 "This is free software; see the source for copying conditions. "
143 143 "There is NO\nwarranty; "
144 144 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
145 145 )
146 146
147 147 def help(ui, cmd=None):
148 148 '''show help for a given command or all commands'''
149 149 if cmd:
150 150 try:
151 151 i = find(cmd)
152 152 ui.write("%s\n\n" % i[2])
153 153
154 154 if i[1]:
155 155 for s, l, d, c in i[1]:
156 156 opt=' '
157 157 if s: opt = opt + '-' + s + ' '
158 158 if l: opt = opt + '--' + l + ' '
159 159 if d: opt = opt + '(' + str(d) + ')'
160 160 ui.write(opt, "\n")
161 161 if c: ui.write(' %s\n' % c)
162 162 ui.write("\n")
163 163
164 164 ui.write(i[0].__doc__, "\n")
165 165 except UnknownCommand:
166 166 ui.warn("hg: unknown command %s\n" % cmd)
167 167 sys.exit(0)
168 168 else:
169 169 if not ui.quiet:
170 170 show_version(ui)
171 171 ui.write('\n')
172 172 ui.write('hg commands:\n\n')
173 173
174 174 h = {}
175 175 for e in table.values():
176 176 f = e[0]
177 177 if f.__name__.startswith("debug"): continue
178 178 d = ""
179 179 if f.__doc__:
180 180 d = f.__doc__.splitlines(0)[0].rstrip()
181 h[f.__name__] = d
181 h[f.__name__.rstrip("_")] = d
182 182
183 183 fns = h.keys()
184 184 fns.sort()
185 185 m = max(map(len, fns))
186 186 for f in fns:
187 187 ui.write(' %-*s %s\n' % (m, f, h[f]))
188 188
189 189 # Commands start here, listed alphabetically
190 190
191 191 def add(ui, repo, file, *files):
192 192 '''add the specified files on the next commit'''
193 193 repo.add(relpath(repo, (file,) + files))
194 194
195 195 def addremove(ui, repo, *files):
196 196 """add all new files, delete all missing files"""
197 197 if files:
198 198 files = relpath(repo, files)
199 199 d = []
200 200 u = []
201 201 for f in files:
202 202 p = repo.wjoin(f)
203 203 s = repo.dirstate.state(f)
204 204 isfile = os.path.isfile(p)
205 205 if s != 'r' and not isfile:
206 206 d.append(f)
207 207 elif s not in 'nmai' and isfile:
208 208 u.append(f)
209 209 else:
210 210 (c, a, d, u) = repo.diffdir(repo.root)
211 211 repo.add(u)
212 212 repo.remove(d)
213 213
214 214 def annotate(u, repo, file, *files, **ops):
215 215 """show changeset information per file line"""
216 216 def getnode(rev):
217 217 return hg.short(repo.changelog.node(rev))
218 218
219 219 def getname(rev):
220 220 try:
221 221 return bcache[rev]
222 222 except KeyError:
223 223 cl = repo.changelog.read(repo.changelog.node(rev))
224 224 name = cl[1]
225 225 f = name.find('@')
226 226 if f >= 0:
227 227 name = name[:f]
228 228 bcache[rev] = name
229 229 return name
230 230
231 231 bcache = {}
232 232 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
233 233 if not ops['user'] and not ops['changeset']:
234 234 ops['number'] = 1
235 235
236 236 node = repo.dirstate.parents()[0]
237 237 if ops['revision']:
238 238 node = repo.changelog.lookup(ops['revision'])
239 239 change = repo.changelog.read(node)
240 240 mmap = repo.manifest.read(change[0])
241 241 maxuserlen = 0
242 242 maxchangelen = 0
243 243 for f in relpath(repo, (file,) + files):
244 244 lines = repo.file(f).annotate(mmap[f])
245 245 pieces = []
246 246
247 247 for o, f in opmap:
248 248 if ops[o]:
249 249 l = [ f(n) for n,t in lines ]
250 250 m = max(map(len, l))
251 251 pieces.append([ "%*s" % (m, x) for x in l])
252 252
253 253 for p,l in zip(zip(*pieces), lines):
254 254 u.write(" ".join(p) + ": " + l[1])
255 255
256 256 def cat(ui, repo, file, rev = []):
257 257 """output the latest or given revision of a file"""
258 258 r = repo.file(relpath(repo, [file])[0])
259 259 n = r.tip()
260 260 if rev: n = r.lookup(rev)
261 261 sys.stdout.write(r.read(n))
262 262
263 263 def commit(ui, repo, *files, **opts):
264 264 """commit the specified files or all outstanding changes"""
265 265 text = opts['text']
266 266 if not text and opts['logfile']:
267 267 try: text = open(opts['logfile']).read()
268 268 except IOError: pass
269 269
270 270 if opts['addremove']:
271 271 addremove(ui, repo, *files)
272 272 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
273 273
274 274 def copy(ui, repo, source, dest):
275 275 """mark a file as copied or renamed for the next commit"""
276 276 return repo.copy(*relpath(repo, (source, dest)))
277 277
278 def debugaddchangegroup(ui, repo):
279 data = sys.stdin.read()
280 repo.addchangegroup(data)
281
282 def debugchangegroup(ui, repo, roots):
283 newer = repo.newer(map(repo.lookup, roots))
284 for chunk in repo.changegroup(newer):
285 sys.stdout.write(chunk)
286
287 278 def debugindex(ui, file):
288 279 r = hg.revlog(hg.opener(""), file, "")
289 280 print " rev offset length base linkrev"+\
290 281 " p1 p2 nodeid"
291 282 for i in range(r.count()):
292 283 e = r.index[i]
293 284 print "% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s.." % (
294 285 i, e[0], e[1], e[2], e[3],
295 286 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5]))
296 287
297 288 def debugindexdot(ui, file):
298 289 r = hg.revlog(hg.opener(""), file, "")
299 290 print "digraph G {"
300 291 for i in range(r.count()):
301 292 e = r.index[i]
302 293 print "\t%d -> %d" % (r.rev(e[4]), i)
303 294 if e[5] != hg.nullid:
304 295 print "\t%d -> %d" % (r.rev(e[5]), i)
305 296 print "}"
306 297
307 298 def diff(ui, repo, *files, **opts):
308 299 """diff working directory (or selected files)"""
309 300 revs = []
310 301 if opts['rev']:
311 302 revs = map(lambda x: repo.lookup(x), opts['rev'])
312 303
313 304 if len(revs) > 2:
314 305 self.ui.warn("too many revisions to diff\n")
315 306 sys.exit(1)
316 307
317 308 if files:
318 309 files = relpath(repo, files)
319 310 else:
320 311 files = relpath(repo, [""])
321 312
322 313 dodiff(ui, repo, os.getcwd(), files, *revs)
323 314
324 315 def export(ui, repo, changeset):
325 316 """dump the changeset header and diffs for a revision"""
326 317 node = repo.lookup(changeset)
327 318 prev, other = repo.changelog.parents(node)
328 319 change = repo.changelog.read(node)
329 320 print "# HG changeset patch"
330 321 print "# User %s" % change[1]
331 322 print "# Node ID %s" % hg.hex(node)
332 323 print "# Parent %s" % hg.hex(prev)
333 324 print
334 325 if other != hg.nullid:
335 326 print "# Parent %s" % hg.hex(other)
336 327 print change[4].rstrip()
337 328 print
338 329
339 330 dodiff(ui, repo, "", None, prev, node)
340 331
341 332 def forget(ui, repo, file, *files):
342 333 """don't add the specified files on the next commit"""
343 334 repo.forget(relpath(repo, (file,) + files))
344 335
345 336 def heads(ui, repo):
346 337 """show current repository heads"""
347 338 for n in repo.changelog.heads():
348 339 show_changeset(ui, repo, changenode=n)
349 340
350 341 def history(ui, repo):
351 342 """show the changelog history"""
352 343 for i in range(repo.changelog.count() - 1, -1, -1):
353 344 show_changeset(ui, repo, rev=i)
354 345
355 346 def identify(ui, repo):
356 347 """print information about the working copy"""
357 348 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
358 349 if not parents:
359 350 ui.write("unknown\n")
360 351 return
361 352
362 353 hexfunc = ui.verbose and hg.hex or hg.short
363 354 (c, a, d, u) = repo.diffdir(repo.root)
364 355 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
365 356 (c or a or d) and "+" or "")]
366 357
367 358 if not ui.quiet:
368 359 # multiple tags for a single parent separated by '/'
369 360 parenttags = ['/'.join(tags)
370 361 for tags in map(repo.nodetags, parents) if tags]
371 362 # tags for multiple parents separated by ' + '
372 363 output.append(' + '.join(parenttags))
373 364
374 365 ui.write("%s\n" % ' '.join(output))
375 366
367 def import_(ui, repo, patch1, *patches, **opts):
368 """import an ordered set of patches"""
369 try:
370 import psyco
371 psyco.full()
372 except:
373 pass
374
375 patches = (patch1,) + patches
376
377 d = opts["base"]
378 strip = opts["strip"]
379 quiet = ui.quiet and "> /dev/null" or ""
380
381 for patch in patches:
382 ui.status("applying %s\n" % patch)
383 pf = os.path.join(d, patch)
384
385 text = ""
386 for l in file(pf):
387 if l[:4] == "--- ": break
388 text += l
389
390 # make sure text isn't empty
391 if not text: text = "imported patch %s\n" % patch
392
393 f = os.popen("patch -p%d < %s" % (strip, pf))
394 files = []
395 for l in f.read().splitlines():
396 l.rstrip('\r\n');
397 if not quiet:
398 print l
399 if l[:14] == 'patching file ':
400 files.append(l[14:])
401 f.close()
402
403 if len(files) > 0:
404 addremove(ui, repo, *files)
405 repo.commit(files, text)
406
376 407 def init(ui, source=None, **opts):
377 408 """create a new repository or copy an existing one"""
378 409
379 410 if source:
380 411 paths = {}
381 412 for name, path in ui.configitems("paths"):
382 413 paths[name] = path
383 414
384 415 if source in paths: source = paths[source]
385 416
386 417 link = 0
387 418 if not source.startswith("http://"):
388 419 d1 = os.stat(os.getcwd()).st_dev
389 420 d2 = os.stat(source).st_dev
390 421 if d1 == d2: link = 1
391 422
392 423 if link:
393 424 ui.debug("copying by hardlink\n")
394 425 os.system("cp -al %s/.hg .hg" % source)
395 426 try:
396 427 os.remove(".hg/dirstate")
397 428 except: pass
398 429
399 430 repo = hg.repository(ui, ".")
400 431
401 432 else:
402 433 repo = hg.repository(ui, ".", create=1)
403 434 other = hg.repository(ui, source)
404 435 cg = repo.getchangegroup(other)
405 436 repo.addchangegroup(cg)
406 437
407 438 f = repo.opener("hgrc", "w")
408 439 f.write("[paths]\n")
409 440 f.write("default = %s\n" % source)
410 441
411 442 if opts['update']:
412 443 update(ui, repo)
413 444 else:
414 445 repo = hg.repository(ui, ".", create=1)
415 446
416 447 def log(ui, repo, f):
417 448 """show the revision history of a single file"""
418 449 f = relpath(repo, [f])[0]
419 450
420 451 r = repo.file(f)
421 452 for i in range(r.count() - 1, -1, -1):
422 453 show_changeset(ui, repo, filelog=r, rev=i)
423 454
424 455 def manifest(ui, repo, rev = []):
425 456 """output the latest or given revision of the project manifest"""
426 457 n = repo.manifest.tip()
427 458 if rev:
428 459 n = repo.manifest.lookup(rev)
429 460 m = repo.manifest.read(n)
430 461 mf = repo.manifest.readflags(n)
431 462 files = m.keys()
432 463 files.sort()
433 464
434 465 for f in files:
435 466 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
436 467
437 468 def parents(ui, repo, node = None):
438 469 '''show the parents of the current working dir'''
439 470 if node:
440 471 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
441 472 else:
442 473 p = repo.dirstate.parents()
443 474
444 475 for n in p:
445 476 if n != hg.nullid:
446 477 show_changeset(ui, repo, changenode=n)
447 478
448 def patch(ui, repo, patch1, *patches, **opts):
449 """import an ordered set of patches"""
450 try:
451 import psyco
452 psyco.full()
453 except:
454 pass
455
456 patches = (patch1,) + patches
457
458 d = opts["base"]
459 strip = opts["strip"]
460 quiet = opts["quiet"] and "> /dev/null" or ""
461
462 for patch in patches:
463 ui.status("applying %s\n" % patch)
464 pf = os.path.join(d, patch)
465
466 text = ""
467 for l in file(pf):
468 if l[:4] == "--- ": break
469 text += l
470
471 # make sure text isn't empty
472 if not text: text = "imported patch %s\n" % patch
473
474 f = os.popen("patch -p%d < %s" % (strip, pf))
475 files = []
476 for l in f.read().splitlines():
477 l.rstrip('\r\n');
478 if not quiet:
479 print l
480 if l[:14] == 'patching file ':
481 files.append(l[14:])
482 f.close()
483
484 if len(files) > 0:
485 addremove(ui, repo, *files)
486 repo.commit(files, text)
487
488 479 def pull(ui, repo, source="default", **opts):
489 480 """pull changes from the specified source"""
490 481 paths = {}
491 482 for name, path in ui.configitems("paths"):
492 483 paths[name] = path
493 484
494 485 if source in paths:
495 486 source = paths[source]
496 487
497 488 ui.status('pulling from %s\n' % (source))
498 489
499 490 other = hg.repository(ui, source)
500 491 cg = repo.getchangegroup(other)
501 492 r = repo.addchangegroup(cg)
502 493 if cg and not r:
503 494 if opts['update']:
504 495 return update(ui, repo)
505 496 else:
506 497 ui.status("(run 'hg update' to get a working copy)\n")
507 498
508 499 return r
509 500
510 501 def push(ui, repo, dest="default-push"):
511 502 """push changes to the specified destination"""
512 503 paths = {}
513 504 for name, path in ui.configitems("paths"):
514 505 paths[name] = path
515 506
516 507 if dest in paths: dest = paths[dest]
517 508
518 509 if not dest.startswith("ssh://"):
519 510 ui.warn("abort: can only push to ssh:// destinations currently\n")
520 511 return 1
521 512
522 513 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', dest)
523 514 if not m:
524 515 ui.warn("abort: couldn't parse destination %s\n" % dest)
525 516 return 1
526 517
527 518 user, host, port, path = map(m.group, (2, 3, 5, 7))
528 519 host = user and ("%s@%s" % (user, host)) or host
529 520 port = port and (" -p %s") % port or ""
530 521 path = path or ""
531 522
532 523 sport = random.randrange(30000, 60000)
533 524 cmd = "ssh %s%s -R %d:localhost:%d 'cd %s; hg pull http://localhost:%d/'"
534 525 cmd = cmd % (host, port, sport+1, sport, path, sport+1)
535 526
536 527 child = os.fork()
537 528 if not child:
538 529 sys.stdout = file("/dev/null", "w")
539 530 sys.stderr = sys.stdout
540 531 hgweb.server(repo.root, "pull", "", "localhost", sport)
541 532 else:
542 533 r = os.system(cmd)
543 534 os.kill(child, signal.SIGTERM)
544 535 return r
545 536
546 537 def rawcommit(ui, repo, *flist, **rc):
547 538 "raw commit interface"
548 539
549 540 text = rc['text']
550 541 if not text and rc['logfile']:
551 542 try: text = open(rc['logfile']).read()
552 543 except IOError: pass
553 544 if not text and not rc['logfile']:
554 545 print "missing commit text"
555 546 return 1
556 547
557 548 files = relpath(repo, list(flist))
558 549 if rc['files']:
559 550 files += open(rc['files']).read().splitlines()
560 551
561 552 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
562 553
563 554 def recover(ui, repo):
564 555 """roll back an interrupted transaction"""
565 556 repo.recover()
566 557
567 558 def remove(ui, repo, file, *files):
568 559 """remove the specified files on the next commit"""
569 560 repo.remove(relpath(repo, (file,) + files))
570 561
571 562 def serve(ui, repo, **opts):
572 563 """export the repository via HTTP"""
573 564 hgweb.server(repo.root, opts["name"], opts["templates"],
574 565 opts["address"], opts["port"])
575 566
576 567 def status(ui, repo):
577 568 '''show changed files in the working directory
578 569
579 570 C = changed
580 571 A = added
581 572 R = removed
582 573 ? = not tracked'''
583 574
584 575 (c, a, d, u) = repo.diffdir(os.getcwd())
585 576 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
586 577
587 578 for f in c: print "C", f
588 579 for f in a: print "A", f
589 580 for f in d: print "R", f
590 581 for f in u: print "?", f
591 582
592 583 def tag(ui, repo, name, rev = None, **opts):
593 584 """add a tag for the current tip or a given revision"""
594 585
595 586 if name == "tip":
596 587 ui.warn("abort: 'tip' is a reserved name!\n")
597 588 return -1
598 589
599 590 (c, a, d, u) = repo.diffdir(repo.root)
600 591 for x in (c, a, d, u):
601 592 if ".hgtags" in x:
602 593 ui.warn("abort: working copy of .hgtags is changed!\n")
603 594 ui.status("(please commit .hgtags manually)\n")
604 595 return -1
605 596
606 597 if rev:
607 598 r = hg.hex(repo.lookup(rev))
608 599 else:
609 600 r = hg.hex(repo.changelog.tip())
610 601
611 602 add = 0
612 603 if not os.path.exists(repo.wjoin(".hgtags")): add = 1
613 604 repo.wfile(".hgtags", "a").write("%s %s\n" % (r, name))
614 605 if add: repo.add([".hgtags"])
615 606
616 607 if not opts['text']:
617 608 opts['text'] = "Added tag %s for changeset %s" % (name, r)
618 609
619 610 repo.commit([".hgtags"], opts['text'], opts['user'], opts['date'])
620 611
621 612 def tags(ui, repo):
622 613 """list repository tags"""
623 614
624 615 l = repo.tagslist()
625 616 l.reverse()
626 617 for t,n in l:
627 618 try:
628 619 r = repo.changelog.rev(n)
629 620 except KeyError:
630 621 r = "?"
631 622 print "%-30s %5d:%s" % (t, repo.changelog.rev(n), hg.hex(n))
632 623
633 624 def tip(ui, repo):
634 625 """show the tip revision"""
635 626 n = repo.changelog.tip()
636 627 show_changeset(ui, repo, changenode=n)
637 628
638 629 def undo(ui, repo):
639 630 """undo the last transaction"""
640 631 repo.undo()
641 632
642 633 def update(ui, repo, node=None, merge=False, clean=False):
643 634 '''update or merge working directory
644 635
645 636 If there are no outstanding changes in the working directory and
646 637 there is a linear relationship between the current version and the
647 638 requested version, the result is the requested version.
648 639
649 640 Otherwise the result is a merge between the contents of the
650 641 current working directory and the requested version. Files that
651 642 changed between either parent are marked as changed for the next
652 643 commit and a commit must be performed before any further updates
653 644 are allowed.
654 645 '''
655 646 node = node and repo.lookup(node) or repo.changelog.tip()
656 647 return repo.update(node, allow=merge, force=clean)
657 648
658 649 def verify(ui, repo):
659 650 """verify the integrity of the repository"""
660 651 return repo.verify()
661 652
662 653 # Command options and aliases are listed here, alphabetically
663 654
664 655 table = {
665 656 "add": (add, [], "hg add [files]"),
666 657 "addremove": (addremove, [], "hg addremove [files]"),
667 "ann|annotate": (annotate,
658 "annotate": (annotate,
668 659 [('r', 'revision', '', 'revision'),
669 660 ('u', 'user', None, 'show user'),
670 661 ('n', 'number', None, 'show revision number'),
671 662 ('c', 'changeset', None, 'show changeset')],
672 663 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
673 "cat|dump": (cat, [], 'hg cat <file> [rev]'),
664 "cat": (cat, [], 'hg cat <file> [rev]'),
674 665 "commit|ci": (commit,
675 666 [('t', 'text', "", 'commit text'),
676 667 ('A', 'addremove', None, 'run add/remove during commit'),
677 668 ('l', 'logfile', "", 'commit text file'),
678 669 ('d', 'date', "", 'data'),
679 670 ('u', 'user', "", 'user')],
680 671 'hg commit [files]'),
681 672 "copy": (copy, [], 'hg copy <source> <dest>'),
682 "debugaddchangegroup": (debugaddchangegroup, [], 'debugaddchangegroup'),
683 "debugchangegroup": (debugchangegroup, [], 'debugchangegroup [roots]'),
684 673 "debugindex": (debugindex, [], 'debugindex <file>'),
685 674 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
686 675 "diff": (diff, [('r', 'rev', [], 'revision')],
687 676 'hg diff [-r A] [-r B] [files]'),
688 677 "export": (export, [], "hg export <changeset>"),
689 678 "forget": (forget, [], "hg forget [files]"),
690 679 "heads": (heads, [], 'hg heads'),
691 680 "history": (history, [], 'hg history'),
692 681 "help": (help, [], 'hg help [command]'),
693 682 "identify|id": (identify, [], 'hg identify'),
683 "import|patch": (import_,
684 [('p', 'strip', 1, 'path strip'),
685 ('b', 'base', "", 'base path')],
686 "hg import [options] <patches>"),
694 687 "init": (init, [('u', 'update', None, 'update after init')],
695 688 'hg init [options] [url]'),
696 689 "log": (log, [], 'hg log <file>'),
697 "manifest|dumpmanifest": (manifest, [], 'hg manifest [rev]'),
690 "manifest": (manifest, [], 'hg manifest [rev]'),
698 691 "parents": (parents, [], 'hg parents [node]'),
699 "patch|import": (patch,
700 [('p', 'strip', 1, 'path strip'),
701 ('b', 'base', "", 'base path'),
702 ('q', 'quiet', "", 'silence diff')],
703 "hg import [options] patches"),
704 "pull|merge": (pull,
692 "pull": (pull,
705 693 [('u', 'update', None, 'update working directory')],
706 694 'hg pull [options] [source]'),
707 695 "push": (push, [], 'hg push <destination>'),
708 696 "rawcommit": (rawcommit,
709 697 [('p', 'parent', [], 'parent'),
710 698 ('d', 'date', "", 'data'),
711 699 ('u', 'user', "", 'user'),
712 700 ('F', 'files', "", 'file list'),
713 701 ('t', 'text', "", 'commit text'),
714 702 ('l', 'logfile', "", 'commit text file')],
715 703 'hg rawcommit [options] [files]'),
716 704 "recover": (recover, [], "hg recover"),
717 "remove": (remove, [], "hg remove [files]"),
705 "remove|rm": (remove, [], "hg remove [files]"),
718 706 "serve": (serve, [('p', 'port', 8000, 'listen port'),
719 707 ('a', 'address', '', 'interface address'),
720 708 ('n', 'name', os.getcwd(), 'repository name'),
721 709 ('t', 'templates', "", 'template map')],
722 710 "hg serve [options]"),
723 711 "status": (status, [], 'hg status'),
724 712 "tag": (tag, [('t', 'text', "", 'commit text'),
725 713 ('d', 'date', "", 'date'),
726 714 ('u', 'user', "", 'user')],
727 715 'hg tag [options] <name> [rev]'),
728 716 "tags": (tags, [], 'hg tags'),
729 717 "tip": (tip, [], 'hg tip'),
730 718 "undo": (undo, [], 'hg undo'),
731 "update|up|checkout|co|resolve": (update,
732 [('m', 'merge', None,
733 'allow merging of conflicts'),
734 ('C', 'clean', None,
735 'overwrite locally modified files')],
736 'hg update [options] [node]'),
719 "update|up|checkout|co":
720 (update,
721 [('m', 'merge', None, 'allow merging of conflicts'),
722 ('C', 'clean', None, 'overwrite locally modified files')],
723 'hg update [options] [node]'),
737 724 "verify": (verify, [], 'hg verify'),
738 725 }
739 726
740 727 norepo = "init version help debugindex debugindexdot"
741 728
742 729 def find(cmd):
743 730 i = None
744 731 for e in table.keys():
745 732 if re.match("(%s)$" % e, cmd):
746 733 return table[e]
747 734
748 735 raise UnknownCommand(cmd)
749 736
750 737 class SignalInterrupt(Exception): pass
751 738
752 739 def catchterm(*args):
753 740 raise SignalInterrupt
754 741
755 742 def run():
756 743 sys.exit(dispatch(sys.argv[1:]))
757 744
758 745 def dispatch(args):
759 746 options = {}
760 747 opts = [('v', 'verbose', None, 'verbose'),
761 748 ('d', 'debug', None, 'debug'),
762 749 ('q', 'quiet', None, 'quiet'),
763 750 ('p', 'profile', None, 'profile'),
764 751 ('y', 'noninteractive', None, 'run non-interactively'),
765 752 ('', 'version', None, 'output version information and exit'),
766 753 ]
767 754
768 755 args = fancyopts.fancyopts(args, opts, options,
769 756 'hg [options] <command> [options] [files]')
770 757
771 758 if not args:
772 759 cmd = "help"
773 760 else:
774 761 cmd, args = args[0], args[1:]
775 762
776 763 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
777 764 not options["noninteractive"])
778 765
779 766 if options["version"]:
780 767 show_version(u)
781 768 sys.exit(0)
782 769
783 770 try:
784 771 i = find(cmd)
785 772 except UnknownCommand:
786 773 u.warn("hg: unknown command '%s'\n" % cmd)
787 774 help(u)
788 775 sys.exit(1)
789 776
790 777 signal.signal(signal.SIGTERM, catchterm)
791 778
792 779 cmdoptions = {}
793 780 try:
794 781 args = fancyopts.fancyopts(args, i[1], cmdoptions, i[2])
795 782 except fancyopts.getopt.GetoptError, inst:
796 783 u.warn("hg %s: %s\n" % (cmd, inst))
797 784 help(u, cmd)
798 785 sys.exit(-1)
799 786
800 787 if cmd not in norepo.split():
801 788 repo = hg.repository(ui = u)
802 789 d = lambda: i[0](u, repo, *args, **cmdoptions)
803 790 else:
804 791 d = lambda: i[0](u, *args, **cmdoptions)
805 792
806 793 try:
807 794 if options['profile']:
808 795 import hotshot, hotshot.stats
809 796 prof = hotshot.Profile("hg.prof")
810 797 r = prof.runcall(d)
811 798 prof.close()
812 799 stats = hotshot.stats.load("hg.prof")
813 800 stats.strip_dirs()
814 801 stats.sort_stats('time', 'calls')
815 802 stats.print_stats(40)
816 803 return r
817 804 else:
818 805 return d()
819 806 except SignalInterrupt:
820 807 u.warn("killed!\n")
821 808 except KeyboardInterrupt:
822 809 u.warn("interrupted!\n")
823 810 except IOError, inst:
824 811 if hasattr(inst, "code"):
825 812 u.warn("abort: %s\n" % inst)
826 813 elif hasattr(inst, "reason"):
827 814 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
828 815 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
829 816 u.warn("broken pipe\n")
830 817 else:
831 818 raise
832 819 except TypeError, inst:
833 820 # was this an argument error?
834 821 tb = traceback.extract_tb(sys.exc_info()[2])
835 822 if len(tb) > 2: # no
836 823 raise
837 824 u.debug(inst, "\n")
838 825 u.warn("%s: invalid arguments\n" % i[0].__name__)
839 826 help(u, cmd)
840 827 sys.exit(-1)
841 828
General Comments 0
You need to be logged in to leave comments. Login now