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