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