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