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