8 from demandload import demandload |
8 from demandload import demandload |
9 from node import * |
9 from node import * |
10 from i18n import gettext as _ |
10 from i18n import gettext as _ |
11 demandload(globals(), "os re sys signal shutil imp urllib pdb") |
11 demandload(globals(), "os re sys signal shutil imp urllib pdb") |
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo") |
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo") |
13 demandload(globals(), "fnmatch mdiff difflib patch random signal tempfile time") |
13 demandload(globals(), "fnmatch difflib patch random signal tempfile time") |
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2") |
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2") |
15 demandload(globals(), "archival cStringIO changegroup") |
15 demandload(globals(), "archival cStringIO changegroup") |
16 demandload(globals(), "hgweb.server sshserver") |
16 demandload(globals(), "cmdutil hgweb.server sshserver") |
17 |
17 |
18 class UnknownCommand(Exception): |
18 class UnknownCommand(Exception): |
19 """Exception raised if command is not in the command table.""" |
19 """Exception raised if command is not in the command table.""" |
20 class AmbiguousCommand(Exception): |
20 class AmbiguousCommand(Exception): |
21 """Exception raised if command shortcut matches more than one command.""" |
21 """Exception raised if command shortcut matches more than one command.""" |
22 |
22 |
23 def bail_if_changed(repo): |
23 def bail_if_changed(repo): |
24 modified, added, removed, deleted, unknown = repo.changes() |
24 modified, added, removed, deleted, unknown = repo.changes() |
25 if modified or added or removed or deleted: |
25 if modified or added or removed or deleted: |
26 raise util.Abort(_("outstanding uncommitted changes")) |
26 raise util.Abort(_("outstanding uncommitted changes")) |
27 |
|
28 def filterfiles(filters, files): |
|
29 l = [x for x in files if x in filters] |
|
30 |
|
31 for t in filters: |
|
32 if t and t[-1] != "/": |
|
33 t += "/" |
|
34 l += [x for x in files if x.startswith(t)] |
|
35 return l |
|
36 |
27 |
37 def relpath(repo, args): |
28 def relpath(repo, args): |
38 cwd = repo.getcwd() |
29 cwd = repo.getcwd() |
39 if cwd: |
30 if cwd: |
40 return [util.normpath(os.path.join(cwd, x)) for x in args] |
31 return [util.normpath(os.path.join(cwd, x)) for x in args] |
342 if rev in seen: |
333 if rev in seen: |
343 continue |
334 continue |
344 seen[rev] = 1 |
335 seen[rev] = 1 |
345 yield str(rev) |
336 yield str(rev) |
346 |
337 |
347 def make_filename(repo, pat, node, |
|
348 total=None, seqno=None, revwidth=None, pathname=None): |
|
349 node_expander = { |
|
350 'H': lambda: hex(node), |
|
351 'R': lambda: str(repo.changelog.rev(node)), |
|
352 'h': lambda: short(node), |
|
353 } |
|
354 expander = { |
|
355 '%': lambda: '%', |
|
356 'b': lambda: os.path.basename(repo.root), |
|
357 } |
|
358 |
|
359 try: |
|
360 if node: |
|
361 expander.update(node_expander) |
|
362 if node and revwidth is not None: |
|
363 expander['r'] = (lambda: |
|
364 str(repo.changelog.rev(node)).zfill(revwidth)) |
|
365 if total is not None: |
|
366 expander['N'] = lambda: str(total) |
|
367 if seqno is not None: |
|
368 expander['n'] = lambda: str(seqno) |
|
369 if total is not None and seqno is not None: |
|
370 expander['n'] = lambda:str(seqno).zfill(len(str(total))) |
|
371 if pathname is not None: |
|
372 expander['s'] = lambda: os.path.basename(pathname) |
|
373 expander['d'] = lambda: os.path.dirname(pathname) or '.' |
|
374 expander['p'] = lambda: pathname |
|
375 |
|
376 newname = [] |
|
377 patlen = len(pat) |
|
378 i = 0 |
|
379 while i < patlen: |
|
380 c = pat[i] |
|
381 if c == '%': |
|
382 i += 1 |
|
383 c = pat[i] |
|
384 c = expander[c]() |
|
385 newname.append(c) |
|
386 i += 1 |
|
387 return ''.join(newname) |
|
388 except KeyError, inst: |
|
389 raise util.Abort(_("invalid format spec '%%%s' in output file name"), |
|
390 inst.args[0]) |
|
391 |
|
392 def make_file(repo, pat, node=None, |
|
393 total=None, seqno=None, revwidth=None, mode='wb', pathname=None): |
|
394 if not pat or pat == '-': |
|
395 return 'w' in mode and sys.stdout or sys.stdin |
|
396 if hasattr(pat, 'write') and 'w' in mode: |
|
397 return pat |
|
398 if hasattr(pat, 'read') and 'r' in mode: |
|
399 return pat |
|
400 return open(make_filename(repo, pat, node, total, seqno, revwidth, |
|
401 pathname), |
|
402 mode) |
|
403 |
|
404 def write_bundle(cg, filename=None, compress=True): |
338 def write_bundle(cg, filename=None, compress=True): |
405 """Write a bundle file and return its filename. |
339 """Write a bundle file and return its filename. |
406 |
340 |
407 Existing files will not be overwritten. |
341 Existing files will not be overwritten. |
408 If no filename is specified, a temporary file is created. |
342 If no filename is specified, a temporary file is created. |
450 finally: |
384 finally: |
451 if fh is not None: |
385 if fh is not None: |
452 fh.close() |
386 fh.close() |
453 if cleanup is not None: |
387 if cleanup is not None: |
454 os.unlink(cleanup) |
388 os.unlink(cleanup) |
455 |
|
456 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always, |
|
457 changes=None, text=False, opts={}): |
|
458 if not node1: |
|
459 node1 = repo.dirstate.parents()[0] |
|
460 # reading the data for node1 early allows it to play nicely |
|
461 # with repo.changes and the revlog cache. |
|
462 change = repo.changelog.read(node1) |
|
463 mmap = repo.manifest.read(change[0]) |
|
464 date1 = util.datestr(change[2]) |
|
465 |
|
466 if not changes: |
|
467 changes = repo.changes(node1, node2, files, match=match) |
|
468 modified, added, removed, deleted, unknown = changes |
|
469 if files: |
|
470 modified, added, removed = map(lambda x: filterfiles(files, x), |
|
471 (modified, added, removed)) |
|
472 |
|
473 if not modified and not added and not removed: |
|
474 return |
|
475 |
|
476 if node2: |
|
477 change = repo.changelog.read(node2) |
|
478 mmap2 = repo.manifest.read(change[0]) |
|
479 _date2 = util.datestr(change[2]) |
|
480 def date2(f): |
|
481 return _date2 |
|
482 def read(f): |
|
483 return repo.file(f).read(mmap2[f]) |
|
484 else: |
|
485 tz = util.makedate()[1] |
|
486 _date2 = util.datestr() |
|
487 def date2(f): |
|
488 try: |
|
489 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz)) |
|
490 except OSError, err: |
|
491 if err.errno != errno.ENOENT: raise |
|
492 return _date2 |
|
493 def read(f): |
|
494 return repo.wread(f) |
|
495 |
|
496 if ui.quiet: |
|
497 r = None |
|
498 else: |
|
499 hexfunc = ui.verbose and hex or short |
|
500 r = [hexfunc(node) for node in [node1, node2] if node] |
|
501 |
|
502 diffopts = ui.diffopts() |
|
503 showfunc = opts.get('show_function') or diffopts['showfunc'] |
|
504 ignorews = opts.get('ignore_all_space') or diffopts['ignorews'] |
|
505 ignorewsamount = opts.get('ignore_space_change') or \ |
|
506 diffopts['ignorewsamount'] |
|
507 ignoreblanklines = opts.get('ignore_blank_lines') or \ |
|
508 diffopts['ignoreblanklines'] |
|
509 |
|
510 all = modified + added + removed |
|
511 all.sort() |
|
512 for f in all: |
|
513 to = None |
|
514 tn = None |
|
515 if f in mmap: |
|
516 to = repo.file(f).read(mmap[f]) |
|
517 if f not in removed: |
|
518 tn = read(f) |
|
519 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text, |
|
520 showfunc=showfunc, ignorews=ignorews, |
|
521 ignorewsamount=ignorewsamount, |
|
522 ignoreblanklines=ignoreblanklines)) |
|
523 |
389 |
524 def trimuser(ui, name, rev, revcache): |
390 def trimuser(ui, name, rev, revcache): |
525 """trim the name of the user who committed a change""" |
391 """trim the name of the user who committed a change""" |
526 user = revcache.get(rev) |
392 user = revcache.get(rev) |
527 if user is None: |
393 if user is None: |
920 node, p2 = repo.dirstate.parents() |
786 node, p2 = repo.dirstate.parents() |
921 if p2 != nullid: |
787 if p2 != nullid: |
922 raise util.Abort(_('uncommitted merge - please provide a ' |
788 raise util.Abort(_('uncommitted merge - please provide a ' |
923 'specific revision')) |
789 'specific revision')) |
924 |
790 |
925 dest = make_filename(repo, dest, node) |
791 dest = cmdutil.make_filename(repo, dest, node) |
926 if os.path.realpath(dest) == repo.root: |
792 if os.path.realpath(dest) == repo.root: |
927 raise util.Abort(_('repository root cannot be destination')) |
793 raise util.Abort(_('repository root cannot be destination')) |
928 dummy, matchfn, dummy = matchpats(repo, [], opts) |
794 dummy, matchfn, dummy = matchpats(repo, [], opts) |
929 kind = opts.get('type') or 'files' |
795 kind = opts.get('type') or 'files' |
930 prefix = opts['prefix'] |
796 prefix = opts['prefix'] |
931 if dest == '-': |
797 if dest == '-': |
932 if kind == 'files': |
798 if kind == 'files': |
933 raise util.Abort(_('cannot archive plain files to stdout')) |
799 raise util.Abort(_('cannot archive plain files to stdout')) |
934 dest = sys.stdout |
800 dest = sys.stdout |
935 if not prefix: prefix = os.path.basename(repo.root) + '-%h' |
801 if not prefix: prefix = os.path.basename(repo.root) + '-%h' |
936 prefix = make_filename(repo, prefix, node) |
802 prefix = cmdutil.make_filename(repo, prefix, node) |
937 archival.archive(repo, dest, node, kind, not opts['no_decode'], |
803 archival.archive(repo, dest, node, kind, not opts['no_decode'], |
938 matchfn, prefix) |
804 matchfn, prefix) |
939 |
805 |
940 def backout(ui, repo, rev, **opts): |
806 def backout(ui, repo, rev, **opts): |
941 '''reverse effect of earlier changeset |
807 '''reverse effect of earlier changeset |
1036 %d dirname of file being printed, or '.' if in repo root |
902 %d dirname of file being printed, or '.' if in repo root |
1037 %p root-relative path name of file being printed |
903 %p root-relative path name of file being printed |
1038 """ |
904 """ |
1039 ctx = repo.changectx(opts['rev'] or "-1") |
905 ctx = repo.changectx(opts['rev'] or "-1") |
1040 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, ctx.node()): |
906 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, ctx.node()): |
1041 fp = make_file(repo, opts['output'], ctx.node(), pathname=abs) |
907 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs) |
1042 fp.write(ctx.filectx(abs).data()) |
908 fp.write(ctx.filectx(abs).data()) |
1043 |
909 |
1044 def clone(ui, source, dest=None, **opts): |
910 def clone(ui, source, dest=None, **opts): |
1045 """make a copy of an existing repository |
911 """make a copy of an existing repository |
1046 |
912 |
1505 """ |
1371 """ |
1506 node1, node2 = revpair(ui, repo, opts['rev']) |
1372 node1, node2 = revpair(ui, repo, opts['rev']) |
1507 |
1373 |
1508 fns, matchfn, anypats = matchpats(repo, pats, opts) |
1374 fns, matchfn, anypats = matchpats(repo, pats, opts) |
1509 |
1375 |
1510 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn, |
1376 patch.diff(repo, node1, node2, fns, match=matchfn, |
1511 text=opts['text'], opts=opts) |
1377 opts=ui.diffopts(opts)) |
1512 |
|
1513 def doexport(ui, repo, changeset, seqno, total, revwidth, opts): |
|
1514 node = repo.lookup(changeset) |
|
1515 parents = [p for p in repo.changelog.parents(node) if p != nullid] |
|
1516 if opts['switch_parent']: |
|
1517 parents.reverse() |
|
1518 prev = (parents and parents[0]) or nullid |
|
1519 change = repo.changelog.read(node) |
|
1520 |
|
1521 fp = make_file(repo, opts['output'], node, total=total, seqno=seqno, |
|
1522 revwidth=revwidth) |
|
1523 if fp != sys.stdout: |
|
1524 ui.note("%s\n" % fp.name) |
|
1525 |
|
1526 fp.write("# HG changeset patch\n") |
|
1527 fp.write("# User %s\n" % change[1]) |
|
1528 fp.write("# Date %d %d\n" % change[2]) |
|
1529 fp.write("# Node ID %s\n" % hex(node)) |
|
1530 fp.write("# Parent %s\n" % hex(prev)) |
|
1531 if len(parents) > 1: |
|
1532 fp.write("# Parent %s\n" % hex(parents[1])) |
|
1533 fp.write(change[4].rstrip()) |
|
1534 fp.write("\n\n") |
|
1535 |
|
1536 dodiff(fp, ui, repo, prev, node, text=opts['text']) |
|
1537 if fp != sys.stdout: |
|
1538 fp.close() |
|
1539 |
1378 |
1540 def export(ui, repo, *changesets, **opts): |
1379 def export(ui, repo, *changesets, **opts): |
1541 """dump the header and diffs for one or more changesets |
1380 """dump the header and diffs for one or more changesets |
1542 |
1381 |
1543 Print the changeset header and diffs for one or more revisions. |
1382 Print the changeset header and diffs for one or more revisions. |
1564 With the --switch-parent option, the diff will be against the second |
1403 With the --switch-parent option, the diff will be against the second |
1565 parent. It can be useful to review a merge. |
1404 parent. It can be useful to review a merge. |
1566 """ |
1405 """ |
1567 if not changesets: |
1406 if not changesets: |
1568 raise util.Abort(_("export requires at least one changeset")) |
1407 raise util.Abort(_("export requires at least one changeset")) |
1569 seqno = 0 |
|
1570 revs = list(revrange(ui, repo, changesets)) |
1408 revs = list(revrange(ui, repo, changesets)) |
1571 total = len(revs) |
1409 if len(revs) > 1: |
1572 revwidth = max(map(len, revs)) |
1410 ui.note(_('exporting patches:\n')) |
1573 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n") |
1411 else: |
1574 ui.note(msg) |
1412 ui.note(_('exporting patch:\n')) |
1575 for cset in revs: |
1413 patch.export(repo, map(repo.lookup, revs), template=opts['output'], |
1576 seqno += 1 |
1414 switch_parent=opts['switch_parent'], opts=ui.diffopts(opts)) |
1577 doexport(ui, repo, cset, seqno, total, revwidth, opts) |
|
1578 |
1415 |
1579 def forget(ui, repo, *pats, **opts): |
1416 def forget(ui, repo, *pats, **opts): |
1580 """don't add the specified files on the next commit (DEPRECATED) |
1417 """don't add the specified files on the next commit (DEPRECATED) |
1581 |
1418 |
1582 (DEPRECATED) |
1419 (DEPRECATED) |
1961 if opts['no_merges'] and len(parents) == 2: |
1798 if opts['no_merges'] and len(parents) == 2: |
1962 continue |
1799 continue |
1963 displayer.show(changenode=n) |
1800 displayer.show(changenode=n) |
1964 if opts['patch']: |
1801 if opts['patch']: |
1965 prev = (parents and parents[0]) or nullid |
1802 prev = (parents and parents[0]) or nullid |
1966 dodiff(ui, ui, other, prev, n) |
1803 patch.diff(repo, other, prev, n) |
1967 ui.write("\n") |
1804 ui.write("\n") |
1968 finally: |
1805 finally: |
1969 if hasattr(other, 'close'): |
1806 if hasattr(other, 'close'): |
1970 other.close() |
1807 other.close() |
1971 if cleanup: |
1808 if cleanup: |
2112 br = repo.branchlookup([repo.changelog.node(rev)]) |
1949 br = repo.branchlookup([repo.changelog.node(rev)]) |
2113 |
1950 |
2114 displayer.show(rev, brinfo=br) |
1951 displayer.show(rev, brinfo=br) |
2115 if opts['patch']: |
1952 if opts['patch']: |
2116 prev = (parents and parents[0]) or nullid |
1953 prev = (parents and parents[0]) or nullid |
2117 dodiff(du, du, repo, prev, changenode, match=matchfn) |
1954 patch.diff(repo, prev, changenode, match=matchfn, fp=du) |
2118 du.write("\n\n") |
1955 du.write("\n\n") |
2119 elif st == 'iter': |
1956 elif st == 'iter': |
2120 if count == limit: break |
1957 if count == limit: break |
2121 if du.header[rev]: |
1958 if du.header[rev]: |
2122 for args in du.header[rev]: |
1959 for args in du.header[rev]: |
2193 if opts['no_merges'] and len(parents) == 2: |
2030 if opts['no_merges'] and len(parents) == 2: |
2194 continue |
2031 continue |
2195 displayer.show(changenode=n) |
2032 displayer.show(changenode=n) |
2196 if opts['patch']: |
2033 if opts['patch']: |
2197 prev = (parents and parents[0]) or nullid |
2034 prev = (parents and parents[0]) or nullid |
2198 dodiff(ui, ui, repo, prev, n) |
2035 patch.diff(repo, prev, n) |
2199 ui.write("\n") |
2036 ui.write("\n") |
2200 |
2037 |
2201 def parents(ui, repo, file_=None, rev=None, branches=None, **opts): |
2038 def parents(ui, repo, file_=None, rev=None, branches=None, **opts): |
2202 """show the parents of the working dir or revision |
2039 """show the parents of the working dir or revision |
2203 |
2040 |
2841 br = None |
2678 br = None |
2842 if opts['branches']: |
2679 if opts['branches']: |
2843 br = repo.branchlookup([n]) |
2680 br = repo.branchlookup([n]) |
2844 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br) |
2681 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br) |
2845 if opts['patch']: |
2682 if opts['patch']: |
2846 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n) |
2683 patch.diff(repo, repo.changelog.parents(n)[0], n) |
2847 |
2684 |
2848 def unbundle(ui, repo, fname, **opts): |
2685 def unbundle(ui, repo, fname, **opts): |
2849 """apply a changegroup file |
2686 """apply a changegroup file |
2850 |
2687 |
2851 Apply a compressed changegroup file generated by the bundle |
2688 Apply a compressed changegroup file generated by the bundle |