##// END OF EJS Templates
Output file of 'export' is opened as binary (other OS)...
thananck@yahoo.com -
r615:ad2999fa default
parent child Browse files
Show More
@@ -1,1185 +1,1185 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 from demandload import *
9 9 demandload(globals(), "os re sys signal")
10 10 demandload(globals(), "fancyopts ui hg util")
11 11 demandload(globals(), "hgweb mdiff random signal time traceback")
12 12 demandload(globals(), "errno socket version")
13 13
14 14 class UnknownCommand(Exception): pass
15 15
16 16 def filterfiles(filters, files):
17 17 l = [ x for x in files if x in filters ]
18 18
19 19 for t in filters:
20 20 if t and t[-1] != "/": t += "/"
21 21 l += [ x for x in files if x.startswith(t) ]
22 22 return l
23 23
24 24 def relfilter(repo, files):
25 25 if os.getcwd() != repo.root:
26 26 p = os.getcwd()[len(repo.root) + 1: ]
27 27 return filterfiles([util.pconvert(p)], files)
28 28 return files
29 29
30 30 def relpath(repo, args):
31 31 if os.getcwd() != repo.root:
32 32 p = os.getcwd()[len(repo.root) + 1: ]
33 33 return [ util.pconvert(os.path.normpath(os.path.join(p, x)))
34 34 for x in args ]
35 35 return args
36 36
37 37 revrangesep = ':'
38 38
39 39 def revrange(ui, repo, revs = [], revlog = None):
40 40 if revlog is None:
41 41 revlog = repo.changelog
42 42 revcount = revlog.count()
43 43 def fix(val, defval):
44 44 if not val: return defval
45 45 try:
46 46 num = int(val)
47 47 if str(num) != val: raise ValueError
48 48 if num < 0: num += revcount
49 49 if not (0 <= num < revcount):
50 50 raise ValueError
51 51 except ValueError:
52 52 try:
53 53 num = repo.changelog.rev(repo.lookup(val))
54 54 except KeyError:
55 55 try:
56 56 num = revlog.rev(revlog.lookup(val))
57 57 except KeyError:
58 58 ui.warn('abort: invalid revision identifier %s\n' % val)
59 59 sys.exit(1)
60 60 return num
61 61 for spec in revs:
62 62 if spec.find(revrangesep) >= 0:
63 63 start, end = spec.split(revrangesep, 1)
64 64 start = fix(start, 0)
65 65 end = fix(end, revcount - 1)
66 66 if end > start:
67 67 end += 1
68 68 step = 1
69 69 else:
70 70 end -= 1
71 71 step = -1
72 72 for rev in xrange(start, end, step):
73 73 yield str(rev)
74 74 else:
75 75 yield spec
76 76
77 77 def dodiff(fp, ui, repo, files = None, node1 = None, node2 = None):
78 78 def date(c):
79 79 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
80 80
81 81 (c, a, d, u) = repo.changes(node1, node2, files)
82 82 if files:
83 83 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
84 84
85 85 if not c and not a and not d:
86 86 return
87 87
88 88 if node2:
89 89 change = repo.changelog.read(node2)
90 90 mmap2 = repo.manifest.read(change[0])
91 91 def read(f): return repo.file(f).read(mmap2[f])
92 92 date2 = date(change)
93 93 else:
94 94 date2 = time.asctime()
95 95 if not node1:
96 96 node1 = repo.dirstate.parents()[0]
97 97 def read(f): return repo.wfile(f).read()
98 98
99 99 if ui.quiet:
100 100 r = None
101 101 else:
102 102 hexfunc = ui.verbose and hg.hex or hg.short
103 103 r = [hexfunc(node) for node in [node1, node2] if node]
104 104
105 105 change = repo.changelog.read(node1)
106 106 mmap = repo.manifest.read(change[0])
107 107 date1 = date(change)
108 108
109 109 for f in c:
110 110 to = None
111 111 if f in mmap:
112 112 to = repo.file(f).read(mmap[f])
113 113 tn = read(f)
114 114 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
115 115 for f in a:
116 116 to = None
117 117 tn = read(f)
118 118 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
119 119 for f in d:
120 120 to = repo.file(f).read(mmap[f])
121 121 tn = None
122 122 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
123 123
124 124 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None):
125 125 """show a single changeset or file revision"""
126 126 changelog = repo.changelog
127 127 if filelog:
128 128 log = filelog
129 129 filerev = rev
130 130 node = filenode = filelog.node(filerev)
131 131 changerev = filelog.linkrev(filenode)
132 132 changenode = changenode or changelog.node(changerev)
133 133 else:
134 134 log = changelog
135 135 changerev = rev
136 136 if changenode is None:
137 137 changenode = changelog.node(changerev)
138 138 elif not changerev:
139 139 rev = changerev = changelog.rev(changenode)
140 140 node = changenode
141 141
142 142 if ui.quiet:
143 143 ui.write("%d:%s\n" % (rev, hg.hex(node)))
144 144 return
145 145
146 146 changes = changelog.read(changenode)
147 147
148 148 parents = [(log.rev(parent), hg.hex(parent))
149 149 for parent in log.parents(node)
150 150 if ui.debugflag or parent != hg.nullid]
151 151 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
152 152 parents = []
153 153
154 154 if filelog:
155 155 ui.write("revision: %d:%s\n" % (filerev, hg.hex(filenode)))
156 156 for parent in parents:
157 157 ui.write("parent: %d:%s\n" % parent)
158 158 ui.status("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
159 159 else:
160 160 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
161 161 for tag in repo.nodetags(changenode):
162 162 ui.status("tag: %s\n" % tag)
163 163 for parent in parents:
164 164 ui.write("parent: %d:%s\n" % parent)
165 165 ui.note("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
166 166 hg.hex(changes[0])))
167 167 ui.status("user: %s\n" % changes[1])
168 168 ui.status("date: %s\n" % time.asctime(
169 169 time.localtime(float(changes[2].split(' ')[0]))))
170 170 if ui.debugflag:
171 171 files = repo.changes(changelog.parents(changenode)[0], changenode)
172 172 for key, value in zip(["files:", "files+:", "files-:"], files):
173 173 if value:
174 174 ui.note("%-12s %s\n" % (key, " ".join(value)))
175 175 else:
176 176 ui.note("files: %s\n" % " ".join(changes[3]))
177 177 description = changes[4].strip()
178 178 if description:
179 179 if ui.verbose:
180 180 ui.status("description:\n")
181 181 ui.status(description)
182 182 ui.status("\n\n")
183 183 else:
184 184 ui.status("summary: %s\n" % description.splitlines()[0])
185 185 ui.status("\n")
186 186
187 187 def show_version(ui):
188 188 """output version and copyright information"""
189 189 ui.write("Mercurial version %s\n" % version.get_version())
190 190 ui.status(
191 191 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
192 192 "This is free software; see the source for copying conditions. "
193 193 "There is NO\nwarranty; "
194 194 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
195 195 )
196 196
197 197 def help(ui, cmd=None):
198 198 '''show help for a given command or all commands'''
199 199 if cmd:
200 200 try:
201 201 i = find(cmd)
202 202 ui.write("%s\n\n" % i[2])
203 203
204 204 if i[1]:
205 205 for s, l, d, c in i[1]:
206 206 opt=' '
207 207 if s: opt = opt + '-' + s + ' '
208 208 if l: opt = opt + '--' + l + ' '
209 209 if d: opt = opt + '(' + str(d) + ')'
210 210 ui.write(opt, "\n")
211 211 if c: ui.write(' %s\n' % c)
212 212 ui.write("\n")
213 213
214 214 ui.write(i[0].__doc__, "\n")
215 215 except UnknownCommand:
216 216 ui.warn("hg: unknown command %s\n" % cmd)
217 217 sys.exit(0)
218 218 else:
219 219 if ui.verbose:
220 220 show_version(ui)
221 221 ui.write('\n')
222 222 if ui.verbose:
223 223 ui.write('hg commands:\n\n')
224 224 else:
225 225 ui.write('basic hg commands (use "hg help -v" for more):\n\n')
226 226
227 227 h = {}
228 228 for c, e in table.items():
229 229 f = c.split("|")[0]
230 230 if not ui.verbose and not f.startswith("^"):
231 231 continue
232 232 if not ui.debugflag and f.startswith("debug"):
233 233 continue
234 234 f = f.lstrip("^")
235 235 d = ""
236 236 if e[0].__doc__:
237 237 d = e[0].__doc__.splitlines(0)[0].rstrip()
238 238 h[f] = d
239 239
240 240 fns = h.keys()
241 241 fns.sort()
242 242 m = max(map(len, fns))
243 243 for f in fns:
244 244 ui.write(' %-*s %s\n' % (m, f, h[f]))
245 245
246 246 # Commands start here, listed alphabetically
247 247
248 248 def add(ui, repo, file, *files):
249 249 '''add the specified files on the next commit'''
250 250 repo.add(relpath(repo, (file,) + files))
251 251
252 252 def addremove(ui, repo, *files):
253 253 """add all new files, delete all missing files"""
254 254 if files:
255 255 files = relpath(repo, files)
256 256 d = []
257 257 u = []
258 258 for f in files:
259 259 p = repo.wjoin(f)
260 260 s = repo.dirstate.state(f)
261 261 isfile = os.path.isfile(p)
262 262 if s != 'r' and not isfile:
263 263 d.append(f)
264 264 elif s not in 'nmai' and isfile:
265 265 u.append(f)
266 266 else:
267 267 (c, a, d, u) = repo.changes(None, None)
268 268 repo.add(u)
269 269 repo.remove(d)
270 270
271 271 def annotate(u, repo, file, *files, **ops):
272 272 """show changeset information per file line"""
273 273 def getnode(rev):
274 274 return hg.short(repo.changelog.node(rev))
275 275
276 276 def getname(rev):
277 277 try:
278 278 return bcache[rev]
279 279 except KeyError:
280 280 cl = repo.changelog.read(repo.changelog.node(rev))
281 281 name = cl[1]
282 282 f = name.find('@')
283 283 if f >= 0:
284 284 name = name[:f]
285 285 f = name.find('<')
286 286 if f >= 0:
287 287 name = name[f+1:]
288 288 bcache[rev] = name
289 289 return name
290 290
291 291 bcache = {}
292 292 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
293 293 if not ops['user'] and not ops['changeset']:
294 294 ops['number'] = 1
295 295
296 296 node = repo.dirstate.parents()[0]
297 297 if ops['revision']:
298 298 node = repo.changelog.lookup(ops['revision'])
299 299 change = repo.changelog.read(node)
300 300 mmap = repo.manifest.read(change[0])
301 301 for f in relpath(repo, (file,) + files):
302 302 lines = repo.file(f).annotate(mmap[f])
303 303 pieces = []
304 304
305 305 for o, f in opmap:
306 306 if ops[o]:
307 307 l = [ f(n) for n,t in lines ]
308 308 m = max(map(len, l))
309 309 pieces.append([ "%*s" % (m, x) for x in l])
310 310
311 311 for p,l in zip(zip(*pieces), lines):
312 312 u.write(" ".join(p) + ": " + l[1])
313 313
314 314 def cat(ui, repo, file, rev = []):
315 315 """output the latest or given revision of a file"""
316 316 r = repo.file(relpath(repo, [file])[0])
317 317 n = r.tip()
318 318 if rev: n = r.lookup(rev)
319 319 sys.stdout.write(r.read(n))
320 320
321 321 def clone(ui, source, dest = None, **opts):
322 322 """make a copy of an existing repository"""
323 323 source = ui.expandpath(source)
324 324
325 325 if dest is None:
326 326 dest = os.path.basename(os.path.normpath(source))
327 327
328 328 if os.path.exists(dest):
329 329 ui.warn("abort: destination '%s' already exists\n" % dest)
330 330 return 1
331 331
332 332 class dircleanup:
333 333 def __init__(self, dir):
334 334 self.dir = dir
335 335 os.mkdir(dir)
336 336 def close(self):
337 337 self.dir = None
338 338 def __del__(self):
339 339 if self.dir:
340 340 import shutil
341 341 shutil.rmtree(self.dir, True)
342 342
343 343 d = dircleanup(dest)
344 344
345 345 link = 0
346 346 abspath = source
347 347 if not (source.startswith("http://") or
348 348 source.startswith("hg://") or
349 349 source.startswith("old-http://")):
350 350 abspath = os.path.abspath(source)
351 351 d1 = os.stat(dest).st_dev
352 352 d2 = os.stat(source).st_dev
353 353 if d1 == d2: link = 1
354 354
355 355 if link:
356 356 ui.note("copying by hardlink\n")
357 357 util.system("cp -al '%s'/.hg '%s'/.hg" % (source, dest))
358 358 try:
359 359 os.remove(os.path.join(dest, ".hg", "dirstate"))
360 360 except: pass
361 361
362 362 repo = hg.repository(ui, dest)
363 363
364 364 else:
365 365 repo = hg.repository(ui, dest, create=1)
366 366 other = hg.repository(ui, source)
367 367 fetch = repo.findincoming(other)
368 368 if fetch:
369 369 cg = other.changegroup(fetch)
370 370 repo.addchangegroup(cg)
371 371
372 372 f = repo.opener("hgrc", "w")
373 373 f.write("[paths]\n")
374 374 f.write("default = %s\n" % abspath)
375 375
376 376 if not opts['noupdate']:
377 377 update(ui, repo)
378 378
379 379 d.close()
380 380
381 381 def commit(ui, repo, *files, **opts):
382 382 """commit the specified files or all outstanding changes"""
383 383 text = opts['text']
384 384 if not text and opts['logfile']:
385 385 try: text = open(opts['logfile']).read()
386 386 except IOError: pass
387 387
388 388 if opts['addremove']:
389 389 addremove(ui, repo, *files)
390 390 repo.commit(relpath(repo, files), text, opts['user'], opts['date'])
391 391
392 392 def copy(ui, repo, source, dest):
393 393 """mark a file as copied or renamed for the next commit"""
394 394 return repo.copy(*relpath(repo, (source, dest)))
395 395
396 396 def debugcheckstate(ui, repo):
397 397 """validate the correctness of the current dirstate"""
398 398 parent1, parent2 = repo.dirstate.parents()
399 399 repo.dirstate.read()
400 400 dc = repo.dirstate.map
401 401 keys = dc.keys()
402 402 keys.sort()
403 403 m1n = repo.changelog.read(parent1)[0]
404 404 m2n = repo.changelog.read(parent2)[0]
405 405 m1 = repo.manifest.read(m1n)
406 406 m2 = repo.manifest.read(m2n)
407 407 errors = 0
408 408 for f in dc:
409 409 state = repo.dirstate.state(f)
410 410 if state in "nr" and f not in m1:
411 411 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
412 412 errors += 1
413 413 if state in "a" and f in m1:
414 414 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
415 415 errors += 1
416 416 if state in "m" and f not in m1 and f not in m2:
417 417 ui.warn("%s in state %s, but not in either manifest\n" %
418 418 (f, state))
419 419 errors += 1
420 420 for f in m1:
421 421 state = repo.dirstate.state(f)
422 422 if state not in "nrm":
423 423 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
424 424 errors += 1
425 425 if errors:
426 426 ui.warn(".hg/dirstate inconsistent with current parent's manifest\n")
427 427 sys.exit(1)
428 428
429 429 def debugstate(ui, repo):
430 430 """show the contents of the current dirstate"""
431 431 repo.dirstate.read()
432 432 dc = repo.dirstate.map
433 433 keys = dc.keys()
434 434 keys.sort()
435 435 for file in keys:
436 436 ui.write("%c %s\n" % (dc[file][0], file))
437 437
438 438 def debugindex(ui, file):
439 439 """dump the contents of an index file"""
440 440 r = hg.revlog(hg.opener(""), file, "")
441 441 ui.write(" rev offset length base linkrev" +
442 442 " p1 p2 nodeid\n")
443 443 for i in range(r.count()):
444 444 e = r.index[i]
445 445 ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
446 446 i, e[0], e[1], e[2], e[3],
447 447 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
448 448
449 449 def debugindexdot(ui, file):
450 450 """dump an index DAG as a .dot file"""
451 451 r = hg.revlog(hg.opener(""), file, "")
452 452 ui.write("digraph G {\n")
453 453 for i in range(r.count()):
454 454 e = r.index[i]
455 455 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
456 456 if e[5] != hg.nullid:
457 457 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
458 458 ui.write("}\n")
459 459
460 460 def diff(ui, repo, *files, **opts):
461 461 """diff working directory (or selected files)"""
462 462 revs = []
463 463 if opts['rev']:
464 464 revs = map(lambda x: repo.lookup(x), opts['rev'])
465 465
466 466 if len(revs) > 2:
467 467 ui.warn("too many revisions to diff\n")
468 468 sys.exit(1)
469 469
470 470 if files:
471 471 files = relpath(repo, files)
472 472 else:
473 473 files = relpath(repo, [""])
474 474
475 475 dodiff(sys.stdout, ui, repo, files, *revs)
476 476
477 477 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
478 478 node = repo.lookup(changeset)
479 479 prev, other = repo.changelog.parents(node)
480 480 change = repo.changelog.read(node)
481 481
482 482 def expand(name):
483 483 expansions = {
484 484 '%': lambda: '%',
485 485 'H': lambda: hg.hex(node),
486 486 'N': lambda: str(total),
487 487 'R': lambda: str(repo.changelog.rev(node)),
488 488 'b': lambda: os.path.basename(repo.root),
489 489 'h': lambda: hg.short(node),
490 490 'n': lambda: str(seqno).zfill(len(str(total))),
491 491 'r': lambda: str(repo.changelog.rev(node)).zfill(revwidth),
492 492 }
493 493 newname = []
494 494 namelen = len(name)
495 495 i = 0
496 496 while i < namelen:
497 497 c = name[i]
498 498 if c == '%':
499 499 i += 1
500 500 c = name[i]
501 501 c = expansions[c]()
502 502 newname.append(c)
503 503 i += 1
504 504 return ''.join(newname)
505 505
506 506 if opts['output'] and opts['output'] != '-':
507 507 try:
508 fp = open(expand(opts['output']), 'w')
508 fp = open(expand(opts['output']), 'wb')
509 509 except KeyError, inst:
510 510 ui.warn("error: invalid format spec '%%%s' in output file name\n" %
511 511 inst.args[0])
512 512 sys.exit(1)
513 513 else:
514 514 fp = sys.stdout
515 515
516 516 fp.write("# HG changeset patch\n")
517 517 fp.write("# User %s\n" % change[1])
518 518 fp.write("# Node ID %s\n" % hg.hex(node))
519 519 fp.write("# Parent %s\n" % hg.hex(prev))
520 520 if other != hg.nullid:
521 521 fp.write("# Parent %s\n" % hg.hex(other))
522 522 fp.write(change[4].rstrip())
523 523 fp.write("\n\n")
524 524
525 525 dodiff(fp, ui, repo, None, prev, node)
526 526
527 527 def export(ui, repo, *changesets, **opts):
528 528 """dump the header and diffs for one or more changesets"""
529 529 if not changesets:
530 530 ui.warn("error: export requires at least one changeset\n")
531 531 sys.exit(1)
532 532 seqno = 0
533 533 revs = list(revrange(ui, repo, changesets))
534 534 total = len(revs)
535 535 revwidth = max(len(revs[0]), len(revs[-1]))
536 536 for cset in revs:
537 537 seqno += 1
538 538 doexport(ui, repo, cset, seqno, total, revwidth, opts)
539 539
540 540 def forget(ui, repo, file, *files):
541 541 """don't add the specified files on the next commit"""
542 542 repo.forget(relpath(repo, (file,) + files))
543 543
544 544 def heads(ui, repo):
545 545 """show current repository heads"""
546 546 for n in repo.changelog.heads():
547 547 show_changeset(ui, repo, changenode=n)
548 548
549 549 def identify(ui, repo):
550 550 """print information about the working copy"""
551 551 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
552 552 if not parents:
553 553 ui.write("unknown\n")
554 554 return
555 555
556 556 hexfunc = ui.verbose and hg.hex or hg.short
557 557 (c, a, d, u) = repo.changes(None, None)
558 558 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
559 559 (c or a or d) and "+" or "")]
560 560
561 561 if not ui.quiet:
562 562 # multiple tags for a single parent separated by '/'
563 563 parenttags = ['/'.join(tags)
564 564 for tags in map(repo.nodetags, parents) if tags]
565 565 # tags for multiple parents separated by ' + '
566 566 output.append(' + '.join(parenttags))
567 567
568 568 ui.write("%s\n" % ' '.join(output))
569 569
570 570 def import_(ui, repo, patch1, *patches, **opts):
571 571 """import an ordered set of patches"""
572 572 try:
573 573 import psyco
574 574 psyco.full()
575 575 except:
576 576 pass
577 577
578 578 patches = (patch1,) + patches
579 579
580 580 d = opts["base"]
581 581 strip = opts["strip"]
582 582
583 583 for patch in patches:
584 584 ui.status("applying %s\n" % patch)
585 585 pf = os.path.join(d, patch)
586 586
587 587 text = ""
588 588 for l in file(pf):
589 589 if l.startswith("--- ") or l.startswith("diff -r"): break
590 590 text += l
591 591
592 592 # parse values that exist when importing the result of an hg export
593 593 hgpatch = user = snippet = None
594 594 ui.debug('text:\n')
595 595 for t in text.splitlines():
596 596 ui.debug(t,'\n')
597 597 if t == '# HG changeset patch' or hgpatch == True:
598 598 hgpatch = True
599 599 if t[:7] == "# User ":
600 600 user = t[7:]
601 601 ui.debug('User: %s\n' % user)
602 602 if t[:2] <> "# " and t.strip() and not snippet: snippet = t
603 603 if snippet: text = snippet + '\n' + text
604 604 ui.debug('text:\n%s\n' % text)
605 605
606 606 # make sure text isn't empty
607 607 if not text: text = "imported patch %s\n" % patch
608 608
609 609 f = os.popen("patch -p%d < %s" % (strip, pf))
610 610 files = []
611 611 for l in f.read().splitlines():
612 612 l.rstrip('\r\n');
613 613 ui.status("%s\n" % l)
614 614 if l[:14] == 'patching file ':
615 615 pf = l[14:]
616 616 if pf not in files:
617 617 files.append(pf)
618 618 patcherr = f.close()
619 619 if patcherr:
620 620 sys.stderr.write("patch failed")
621 621 sys.exit(1)
622 622
623 623 if len(files) > 0:
624 624 addremove(ui, repo, *files)
625 625 repo.commit(files, text, user)
626 626
627 627 def init(ui, source=None):
628 628 """create a new repository in the current directory"""
629 629
630 630 if source:
631 631 ui.warn("no longer supported: use \"hg clone\" instead\n")
632 632 sys.exit(1)
633 633 repo = hg.repository(ui, ".", create=1)
634 634
635 635 def log(ui, repo, f=None, **opts):
636 636 """show the revision history of the repository or a single file"""
637 637 if f:
638 638 files = relpath(repo, [f])
639 639 filelog = repo.file(files[0])
640 640 log = filelog
641 641 lookup = filelog.lookup
642 642 else:
643 643 files = None
644 644 filelog = None
645 645 log = repo.changelog
646 646 lookup = repo.lookup
647 647 revlist = []
648 648 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
649 649 while revs:
650 650 if len(revs) == 1:
651 651 revlist.append(revs.pop(0))
652 652 else:
653 653 a = revs.pop(0)
654 654 b = revs.pop(0)
655 655 off = a > b and -1 or 1
656 656 revlist.extend(range(a, b + off, off))
657 657
658 658 for i in revlist or range(log.count() - 1, -1, -1):
659 659 show_changeset(ui, repo, filelog=filelog, rev=i)
660 660 if opts['patch']:
661 661 if filelog:
662 662 filenode = filelog.node(i)
663 663 i = filelog.linkrev(filenode)
664 664 changenode = repo.changelog.node(i)
665 665 prev, other = repo.changelog.parents(changenode)
666 666 dodiff(sys.stdout, ui, repo, files, prev, changenode)
667 667 ui.write("\n")
668 668 ui.write("\n")
669 669
670 670 def manifest(ui, repo, rev = []):
671 671 """output the latest or given revision of the project manifest"""
672 672 n = repo.manifest.tip()
673 673 if rev:
674 674 n = repo.manifest.lookup(rev)
675 675 m = repo.manifest.read(n)
676 676 mf = repo.manifest.readflags(n)
677 677 files = m.keys()
678 678 files.sort()
679 679
680 680 for f in files:
681 681 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
682 682
683 683 def parents(ui, repo, node = None):
684 684 '''show the parents of the current working dir'''
685 685 if node:
686 686 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
687 687 else:
688 688 p = repo.dirstate.parents()
689 689
690 690 for n in p:
691 691 if n != hg.nullid:
692 692 show_changeset(ui, repo, changenode=n)
693 693
694 694 def pull(ui, repo, source="default", **opts):
695 695 """pull changes from the specified source"""
696 696 source = ui.expandpath(source)
697 697
698 698 ui.status('pulling from %s\n' % (source))
699 699
700 700 other = hg.repository(ui, source)
701 701 fetch = repo.findincoming(other)
702 702 if not fetch:
703 703 ui.status("no changes found\n")
704 704 return
705 705
706 706 cg = other.changegroup(fetch)
707 707 r = repo.addchangegroup(cg)
708 708 if cg and not r:
709 709 if opts['update']:
710 710 return update(ui, repo)
711 711 else:
712 712 ui.status("(run 'hg update' to get a working copy)\n")
713 713
714 714 return r
715 715
716 716 def push(ui, repo, dest="default-push"):
717 717 """push changes to the specified destination"""
718 718 dest = ui.expandpath(dest)
719 719
720 720 if not dest.startswith("ssh://"):
721 721 ui.warn("abort: can only push to ssh:// destinations currently\n")
722 722 return 1
723 723
724 724 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', dest)
725 725 if not m:
726 726 ui.warn("abort: couldn't parse destination %s\n" % dest)
727 727 return 1
728 728
729 729 user, host, port, path = map(m.group, (2, 3, 5, 7))
730 730 uhost = user and ("%s@%s" % (user, host)) or host
731 731 port = port and (" -p %s") % port or ""
732 732 path = path or ""
733 733
734 734 sport = random.randrange(30000, 60000)
735 735 cmd = "ssh %s%s -R %d:localhost:%d 'cd %s; hg pull http://localhost:%d/'"
736 736 cmd = cmd % (uhost, port, sport+1, sport, path, sport+1)
737 737
738 738 child = os.fork()
739 739 if not child:
740 740 sys.stdout = file("/dev/null", "w")
741 741 sys.stderr = sys.stdout
742 742 hgweb.server(repo.root, "pull", "", "localhost", sport)
743 743 else:
744 744 ui.status("connecting to %s\n" % host)
745 745 r = os.system(cmd)
746 746 os.kill(child, signal.SIGTERM)
747 747 return r
748 748
749 749 def rawcommit(ui, repo, *flist, **rc):
750 750 "raw commit interface"
751 751
752 752 text = rc['text']
753 753 if not text and rc['logfile']:
754 754 try: text = open(rc['logfile']).read()
755 755 except IOError: pass
756 756 if not text and not rc['logfile']:
757 757 ui.warn("abort: missing commit text\n")
758 758 return 1
759 759
760 760 files = relpath(repo, list(flist))
761 761 if rc['files']:
762 762 files += open(rc['files']).read().splitlines()
763 763
764 764 rc['parent'] = map(repo.lookup, rc['parent'])
765 765
766 766 repo.rawcommit(files, text, rc['user'], rc['date'], *rc['parent'])
767 767
768 768 def recover(ui, repo):
769 769 """roll back an interrupted transaction"""
770 770 repo.recover()
771 771
772 772 def remove(ui, repo, file, *files):
773 773 """remove the specified files on the next commit"""
774 774 repo.remove(relpath(repo, (file,) + files))
775 775
776 776 def revert(ui, repo, *names, **opts):
777 777 """revert modified files or dirs back to their unmodified states"""
778 778 node = opts['rev'] and repo.lookup(opts['rev']) or \
779 779 repo.dirstate.parents()[0]
780 780 root = os.path.realpath(repo.root)
781 781
782 782 def trimpath(p):
783 783 p = os.path.realpath(p)
784 784 if p.startswith(root):
785 785 rest = p[len(root):]
786 786 if not rest:
787 787 return rest
788 788 if p.startswith(os.sep):
789 789 return rest[1:]
790 790 return p
791 791
792 792 relnames = map(trimpath, names or [os.getcwd()])
793 793 chosen = {}
794 794
795 795 def choose(name):
796 796 def body(name):
797 797 for r in relnames:
798 798 if not name.startswith(r): continue
799 799 rest = name[len(r):]
800 800 if not rest: return r, True
801 801 depth = rest.count(os.sep)
802 802 if not r:
803 803 if depth == 0 or not opts['nonrecursive']: return r, True
804 804 elif rest[0] == os.sep:
805 805 if depth == 1 or not opts['nonrecursive']: return r, True
806 806 return None, False
807 807 relname, ret = body(name)
808 808 if ret:
809 809 chosen[relname] = 1
810 810 return ret
811 811
812 812 r = repo.update(node, False, True, choose, False)
813 813 for n in relnames:
814 814 if n not in chosen:
815 815 ui.warn('error: no matches for %s\n' % n)
816 816 r = 1
817 817 sys.stdout.flush()
818 818 return r
819 819
820 820 def root(ui, repo):
821 821 """print the root (top) of the current working dir"""
822 822 ui.write(repo.root + "\n")
823 823
824 824 def serve(ui, repo, **opts):
825 825 """export the repository via HTTP"""
826 826 def openlog(opt, default):
827 827 if opts[opt] and opts[opt] != '-': return open(opts[opt], 'w')
828 828 else: return default
829 829 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
830 830 opts["address"], opts["port"],
831 831 openlog('accesslog', sys.stdout),
832 832 openlog('errorlog', sys.stderr))
833 833 if ui.verbose:
834 834 addr, port = httpd.socket.getsockname()
835 835 if addr == '0.0.0.0':
836 836 addr = socket.gethostname()
837 837 else:
838 838 try:
839 839 addr = socket.gethostbyaddr(addr)[0]
840 840 except: pass
841 841 if port != 80:
842 842 ui.status('listening at http://%s:%d/\n' % (addr, port))
843 843 else:
844 844 ui.status('listening at http://%s/\n' % addr)
845 845 httpd.serve_forever()
846 846
847 847 def status(ui, repo):
848 848 '''show changed files in the working directory
849 849
850 850 C = changed
851 851 A = added
852 852 R = removed
853 853 ? = not tracked'''
854 854
855 855 (c, a, d, u) = repo.changes(None, None)
856 856 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
857 857
858 858 for f in c: ui.write("C ", f, "\n")
859 859 for f in a: ui.write("A ", f, "\n")
860 860 for f in d: ui.write("R ", f, "\n")
861 861 for f in u: ui.write("? ", f, "\n")
862 862
863 863 def tag(ui, repo, name, rev = None, **opts):
864 864 """add a tag for the current tip or a given revision"""
865 865
866 866 if name == "tip":
867 867 ui.warn("abort: 'tip' is a reserved name!\n")
868 868 return -1
869 869 if rev:
870 870 r = hg.hex(repo.lookup(rev))
871 871 else:
872 872 r = hg.hex(repo.changelog.tip())
873 873
874 874 if name.find(revrangesep) >= 0:
875 875 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
876 876 return -1
877 877
878 878 if opts['local']:
879 879 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
880 880 return
881 881
882 882 (c, a, d, u) = repo.changes(None, None)
883 883 for x in (c, a, d, u):
884 884 if ".hgtags" in x:
885 885 ui.warn("abort: working copy of .hgtags is changed!\n")
886 886 ui.status("(please commit .hgtags manually)\n")
887 887 return -1
888 888
889 889 add = 0
890 890 if not os.path.exists(repo.wjoin(".hgtags")): add = 1
891 891 repo.wfile(".hgtags", "a").write("%s %s\n" % (r, name))
892 892 if add: repo.add([".hgtags"])
893 893
894 894 if not opts['text']:
895 895 opts['text'] = "Added tag %s for changeset %s" % (name, r)
896 896
897 897 repo.commit([".hgtags"], opts['text'], opts['user'], opts['date'])
898 898
899 899 def tags(ui, repo):
900 900 """list repository tags"""
901 901
902 902 l = repo.tagslist()
903 903 l.reverse()
904 904 for t, n in l:
905 905 try:
906 906 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
907 907 except KeyError:
908 908 r = " ?:?"
909 909 ui.write("%-30s %s\n" % (t, r))
910 910
911 911 def tip(ui, repo):
912 912 """show the tip revision"""
913 913 n = repo.changelog.tip()
914 914 show_changeset(ui, repo, changenode=n)
915 915
916 916 def undo(ui, repo):
917 917 """undo the last commit or pull
918 918
919 919 Roll back the last pull or commit transaction on the
920 920 repository, restoring the project to its earlier state.
921 921
922 922 This command should be used with care. There is only one level of
923 923 undo and there is no redo.
924 924
925 925 This command is not intended for use on public repositories. Once
926 926 a change is visible for pull by other users, undoing it locally is
927 927 ineffective.
928 928 """
929 929 repo.undo()
930 930
931 931 def update(ui, repo, node=None, merge=False, clean=False):
932 932 '''update or merge working directory
933 933
934 934 If there are no outstanding changes in the working directory and
935 935 there is a linear relationship between the current version and the
936 936 requested version, the result is the requested version.
937 937
938 938 Otherwise the result is a merge between the contents of the
939 939 current working directory and the requested version. Files that
940 940 changed between either parent are marked as changed for the next
941 941 commit and a commit must be performed before any further updates
942 942 are allowed.
943 943 '''
944 944 node = node and repo.lookup(node) or repo.changelog.tip()
945 945 return repo.update(node, allow=merge, force=clean)
946 946
947 947 def verify(ui, repo):
948 948 """verify the integrity of the repository"""
949 949 return repo.verify()
950 950
951 951 # Command options and aliases are listed here, alphabetically
952 952
953 953 table = {
954 954 "^add": (add, [], "hg add [files]"),
955 955 "addremove": (addremove, [], "hg addremove [files]"),
956 956 "^annotate": (annotate,
957 957 [('r', 'revision', '', 'revision'),
958 958 ('u', 'user', None, 'show user'),
959 959 ('n', 'number', None, 'show revision number'),
960 960 ('c', 'changeset', None, 'show changeset')],
961 961 'hg annotate [-u] [-c] [-n] [-r id] [files]'),
962 962 "cat": (cat, [], 'hg cat <file> [rev]'),
963 963 "^clone": (clone, [('U', 'noupdate', None, 'skip update after cloning')],
964 964 'hg clone [options] <source> [dest]'),
965 965 "^commit|ci": (commit,
966 966 [('t', 'text', "", 'commit text'),
967 967 ('A', 'addremove', None, 'run add/remove during commit'),
968 968 ('l', 'logfile', "", 'commit text file'),
969 969 ('d', 'date', "", 'date code'),
970 970 ('u', 'user', "", 'user')],
971 971 'hg commit [files]'),
972 972 "copy": (copy, [], 'hg copy <source> <dest>'),
973 973 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
974 974 "debugstate": (debugstate, [], 'debugstate'),
975 975 "debugindex": (debugindex, [], 'debugindex <file>'),
976 976 "debugindexdot": (debugindexdot, [], 'debugindexdot <file>'),
977 977 "^diff": (diff, [('r', 'rev', [], 'revision')],
978 978 'hg diff [-r A] [-r B] [files]'),
979 979 "^export": (export, [('o', 'output', "", 'output to file')],
980 980 "hg export [-o file] <changeset> ..."),
981 981 "forget": (forget, [], "hg forget [files]"),
982 982 "heads": (heads, [], 'hg heads'),
983 983 "help": (help, [], 'hg help [command]'),
984 984 "identify|id": (identify, [], 'hg identify'),
985 985 "import|patch": (import_,
986 986 [('p', 'strip', 1, 'path strip'),
987 987 ('b', 'base', "", 'base path')],
988 988 "hg import [options] <patches>"),
989 989 "^init": (init, [], 'hg init'),
990 990 "^log|history": (log,
991 991 [('r', 'rev', [], 'revision'),
992 992 ('p', 'patch', None, 'show patch')],
993 993 'hg log [-r A] [-r B] [-p] [file]'),
994 994 "manifest": (manifest, [], 'hg manifest [rev]'),
995 995 "parents": (parents, [], 'hg parents [node]'),
996 996 "^pull": (pull,
997 997 [('u', 'update', None, 'update working directory')],
998 998 'hg pull [options] [source]'),
999 999 "^push": (push, [], 'hg push <destination>'),
1000 1000 "rawcommit": (rawcommit,
1001 1001 [('p', 'parent', [], 'parent'),
1002 1002 ('d', 'date', "", 'date code'),
1003 1003 ('u', 'user', "", 'user'),
1004 1004 ('F', 'files', "", 'file list'),
1005 1005 ('t', 'text', "", 'commit text'),
1006 1006 ('l', 'logfile', "", 'commit text file')],
1007 1007 'hg rawcommit [options] [files]'),
1008 1008 "recover": (recover, [], "hg recover"),
1009 1009 "^remove|rm": (remove, [], "hg remove [files]"),
1010 1010 "^revert": (revert,
1011 1011 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1012 1012 ("r", "rev", "", "revision")],
1013 1013 "hg revert [files|dirs]"),
1014 1014 "root": (root, [], "hg root"),
1015 1015 "^serve": (serve, [('A', 'accesslog', '', 'access log file'),
1016 1016 ('E', 'errorlog', '', 'error log file'),
1017 1017 ('p', 'port', 8000, 'listen port'),
1018 1018 ('a', 'address', '', 'interface address'),
1019 1019 ('n', 'name', os.getcwd(), 'repository name'),
1020 1020 ('t', 'templates', "", 'template map')],
1021 1021 "hg serve [options]"),
1022 1022 "^status": (status, [], 'hg status'),
1023 1023 "tag": (tag, [('l', 'local', None, 'make the tag local'),
1024 1024 ('t', 'text', "", 'commit text'),
1025 1025 ('d', 'date', "", 'date code'),
1026 1026 ('u', 'user', "", 'user')],
1027 1027 'hg tag [options] <name> [rev]'),
1028 1028 "tags": (tags, [], 'hg tags'),
1029 1029 "tip": (tip, [], 'hg tip'),
1030 1030 "undo": (undo, [], 'hg undo'),
1031 1031 "^update|up|checkout|co":
1032 1032 (update,
1033 1033 [('m', 'merge', None, 'allow merging of conflicts'),
1034 1034 ('C', 'clean', None, 'overwrite locally modified files')],
1035 1035 'hg update [options] [node]'),
1036 1036 "verify": (verify, [], 'hg verify'),
1037 1037 "version": (show_version, [], 'hg version'),
1038 1038 }
1039 1039
1040 1040 globalopts = [('v', 'verbose', None, 'verbose'),
1041 1041 ('', 'debug', None, 'debug'),
1042 1042 ('q', 'quiet', None, 'quiet'),
1043 1043 ('', 'profile', None, 'profile'),
1044 1044 ('R', 'repository', "", 'repository root directory'),
1045 1045 ('', 'traceback', None, 'print traceback on exception'),
1046 1046 ('y', 'noninteractive', None, 'run non-interactively'),
1047 1047 ('', 'version', None, 'output version information and exit'),
1048 1048 ]
1049 1049
1050 1050 norepo = "clone init version help debugindex debugindexdot"
1051 1051
1052 1052 def find(cmd):
1053 1053 for e in table.keys():
1054 1054 if re.match("(%s)$" % e, cmd):
1055 1055 return table[e]
1056 1056
1057 1057 raise UnknownCommand(cmd)
1058 1058
1059 1059 class SignalInterrupt(Exception): pass
1060 1060
1061 1061 def catchterm(*args):
1062 1062 raise SignalInterrupt
1063 1063
1064 1064 def run():
1065 1065 sys.exit(dispatch(sys.argv[1:]))
1066 1066
1067 1067 class ParseError(Exception): pass
1068 1068
1069 1069 def parse(args):
1070 1070 options = {}
1071 1071 cmdoptions = {}
1072 1072
1073 1073 try:
1074 1074 args = fancyopts.fancyopts(args, globalopts, options)
1075 1075 except fancyopts.getopt.GetoptError, inst:
1076 1076 raise ParseError(cmd, inst)
1077 1077
1078 1078 if options["version"]:
1079 1079 return ("version", show_version, [], options, cmdoptions)
1080 1080 elif not args:
1081 1081 return ("help", help, [], options, cmdoptions)
1082 1082 else:
1083 1083 cmd, args = args[0], args[1:]
1084 1084
1085 1085 i = find(cmd)
1086 1086
1087 1087 # combine global options into local
1088 1088 c = list(i[1])
1089 1089 l = len(c)
1090 1090 for o in globalopts:
1091 1091 c.append((o[0], o[1], options[o[1]], o[3]))
1092 1092
1093 1093 try:
1094 1094 args = fancyopts.fancyopts(args, c, cmdoptions)
1095 1095 except fancyopts.getopt.GetoptError, inst:
1096 1096 raise ParseError(cmd, inst)
1097 1097
1098 1098 # separate global options back out
1099 1099 for o in globalopts:
1100 1100 n = o[1]
1101 1101 options[n] = cmdoptions[n]
1102 1102 del cmdoptions[n]
1103 1103
1104 1104 return (cmd, i[0], args, options, cmdoptions)
1105 1105
1106 1106 def dispatch(args):
1107 1107 signal.signal(signal.SIGTERM, catchterm)
1108 1108
1109 1109 try:
1110 1110 cmd, func, args, options, cmdoptions = parse(args)
1111 1111 except ParseError, inst:
1112 1112 u = ui.ui()
1113 1113 if inst.args[0]:
1114 1114 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1115 1115 help(u, inst.args[0])
1116 1116 else:
1117 1117 u.warn("hg: %s\n" % inst.args[1])
1118 1118 help(u)
1119 1119 sys.exit(-1)
1120 1120 except UnknownCommand, inst:
1121 1121 u = ui.ui()
1122 1122 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1123 1123 help(u)
1124 1124 sys.exit(1)
1125 1125
1126 1126 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1127 1127 not options["noninteractive"])
1128 1128
1129 1129 try:
1130 1130 try:
1131 1131 if cmd not in norepo.split():
1132 1132 path = options["repository"] or ""
1133 1133 repo = hg.repository(ui=u, path=path)
1134 1134 d = lambda: func(u, repo, *args, **cmdoptions)
1135 1135 else:
1136 1136 d = lambda: func(u, *args, **cmdoptions)
1137 1137
1138 1138 if options['profile']:
1139 1139 import hotshot, hotshot.stats
1140 1140 prof = hotshot.Profile("hg.prof")
1141 1141 r = prof.runcall(d)
1142 1142 prof.close()
1143 1143 stats = hotshot.stats.load("hg.prof")
1144 1144 stats.strip_dirs()
1145 1145 stats.sort_stats('time', 'calls')
1146 1146 stats.print_stats(40)
1147 1147 return r
1148 1148 else:
1149 1149 return d()
1150 1150 except:
1151 1151 if options['traceback']:
1152 1152 traceback.print_exc()
1153 1153 raise
1154 1154 except util.CommandError, inst:
1155 1155 u.warn("abort: %s\n" % inst.args)
1156 1156 except hg.RepoError, inst:
1157 1157 u.warn("abort: ", inst, "!\n")
1158 1158 except SignalInterrupt:
1159 1159 u.warn("killed!\n")
1160 1160 except KeyboardInterrupt:
1161 1161 u.warn("interrupted!\n")
1162 1162 except IOError, inst:
1163 1163 if hasattr(inst, "code"):
1164 1164 u.warn("abort: %s\n" % inst)
1165 1165 elif hasattr(inst, "reason"):
1166 1166 u.warn("abort: error %d: %s\n" % (inst.reason[0], inst.reason[1]))
1167 1167 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1168 1168 u.warn("broken pipe\n")
1169 1169 else:
1170 1170 raise
1171 1171 except OSError, inst:
1172 1172 if hasattr(inst, "filename"):
1173 1173 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1174 1174 else:
1175 1175 u.warn("abort: %s\n" % inst.strerror)
1176 1176 except TypeError, inst:
1177 1177 # was this an argument error?
1178 1178 tb = traceback.extract_tb(sys.exc_info()[2])
1179 1179 if len(tb) > 2: # no
1180 1180 raise
1181 1181 u.debug(inst, "\n")
1182 1182 u.warn("%s: invalid arguments\n" % cmd)
1183 1183 help(u, cmd)
1184 1184
1185 1185 sys.exit(-1)
General Comments 0
You need to be logged in to leave comments. Login now