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