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