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