Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/commands.py @ 1060:e453d2053b2e
Merge from BOS, fix help
author | mpm@selenic.com |
---|---|
date | Fri, 26 Aug 2005 01:56:49 -0700 |
parents | 5139ee9bfc38 4eab07ef66e2 |
children | fed8d078840b |
comparison
equal
deleted
inserted
replaced
1055:ea465485f54f | 1060:e453d2053b2e |
---|---|
43 return files, matchfn, walk() | 43 return files, matchfn, walk() |
44 | 44 |
45 def walk(repo, pats, opts, head = ''): | 45 def walk(repo, pats, opts, head = ''): |
46 files, matchfn, results = makewalk(repo, pats, opts, head) | 46 files, matchfn, results = makewalk(repo, pats, opts, head) |
47 for r in results: yield r | 47 for r in results: yield r |
48 | |
49 def walkchangerevs(ui, repo, cwd, pats, opts): | |
50 # This code most commonly needs to iterate backwards over the | |
51 # history it is interested in. Doing so has awful | |
52 # (quadratic-looking) performance, so we use iterators in a | |
53 # "windowed" way. Walk forwards through a window of revisions, | |
54 # yielding them in the desired order, and walk the windows | |
55 # themselves backwards. | |
56 cwd = repo.getcwd() | |
57 if not pats and cwd: | |
58 opts['include'] = [os.path.join(cwd, i) for i in opts['include']] | |
59 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']] | |
60 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '', | |
61 pats, opts) | |
62 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0'])) | |
63 wanted = {} | |
64 slowpath = anypats | |
65 window = 300 | |
66 fncache = {} | |
67 if not slowpath and not files: | |
68 # No files, no patterns. Display all revs. | |
69 wanted = dict(zip(revs, revs)) | |
70 if not slowpath: | |
71 # Only files, no patterns. Check the history of each file. | |
72 def filerevgen(filelog): | |
73 for i in xrange(filelog.count() - 1, -1, -window): | |
74 revs = [] | |
75 for j in xrange(max(0, i - window), i + 1): | |
76 revs.append(filelog.linkrev(filelog.node(j))) | |
77 revs.reverse() | |
78 for rev in revs: | |
79 yield rev | |
80 | |
81 minrev, maxrev = min(revs), max(revs) | |
82 for file in files: | |
83 filelog = repo.file(file) | |
84 # A zero count may be a directory or deleted file, so | |
85 # try to find matching entries on the slow path. | |
86 if filelog.count() == 0: | |
87 slowpath = True | |
88 break | |
89 for rev in filerevgen(filelog): | |
90 if rev <= maxrev: | |
91 if rev < minrev: break | |
92 fncache.setdefault(rev, []) | |
93 fncache[rev].append(file) | |
94 wanted[rev] = 1 | |
95 if slowpath: | |
96 # The slow path checks files modified in every changeset. | |
97 def changerevgen(): | |
98 for i in xrange(repo.changelog.count() - 1, -1, -window): | |
99 for j in xrange(max(0, i - window), i + 1): | |
100 yield j, repo.changelog.read(repo.lookup(str(j)))[3] | |
101 | |
102 for rev, changefiles in changerevgen(): | |
103 matches = filter(matchfn, changefiles) | |
104 if matches: | |
105 fncache[rev] = matches | |
106 wanted[rev] = 1 | |
107 | |
108 for i in xrange(0, len(revs), window): | |
109 yield 'window', revs[0] < revs[-1], revs[-1] | |
110 nrevs = [rev for rev in revs[i : min(i + window, len(revs))] | |
111 if rev in wanted] | |
112 srevs = list(nrevs) | |
113 srevs.sort() | |
114 for rev in srevs: | |
115 fns = fncache.get(rev) | |
116 if not fns: | |
117 fns = repo.changelog.read(repo.lookup(str(rev)))[3] | |
118 fns = filter(matchfn, fns) | |
119 yield 'add', rev, fns | |
120 for rev in nrevs: | |
121 yield 'iter', rev, None | |
48 | 122 |
49 revrangesep = ':' | 123 revrangesep = ':' |
50 | 124 |
51 def revrange(ui, repo, revs, revlog=None): | 125 def revrange(ui, repo, revs, revlog=None): |
52 if revlog is None: | 126 if revlog is None: |
643 | 717 |
644 def debugwalk(ui, repo, *pats, **opts): | 718 def debugwalk(ui, repo, *pats, **opts): |
645 """show how files match on given patterns""" | 719 """show how files match on given patterns""" |
646 items = list(walk(repo, pats, opts)) | 720 items = list(walk(repo, pats, opts)) |
647 if not items: return | 721 if not items: return |
648 fmt = '%%s %%-%ds %%-%ds %%s' % ( | 722 fmt = '%%s %%-%ds %%-%ds %%s\n' % ( |
649 max([len(abs) for (src, abs, rel, exact) in items]), | 723 max([len(abs) for (src, abs, rel, exact) in items]), |
650 max([len(rel) for (src, abs, rel, exact) in items])) | 724 max([len(rel) for (src, abs, rel, exact) in items])) |
651 exactly = {True: 'exact', False: ''} | 725 exactly = {True: 'exact', False: ''} |
652 for src, abs, rel, exact in items: | 726 for src, abs, rel, exact in items: |
653 print fmt % (src, abs, rel, exactly[exact]) | 727 ui.write(fmt % (src, abs, rel, exactly[exact])) |
654 | 728 |
655 def diff(ui, repo, *pats, **opts): | 729 def diff(ui, repo, *pats, **opts): |
656 """diff working directory (or selected files)""" | 730 """diff working directory (or selected files)""" |
657 node1, node2 = None, None | 731 node1, node2 = None, None |
658 revs = [repo.lookup(x) for x in opts['rev']] | 732 revs = [repo.lookup(x) for x in opts['rev']] |
716 for src, abs, rel, exact in walk(repo, pats, opts): | 790 for src, abs, rel, exact in walk(repo, pats, opts): |
717 if repo.dirstate.state(abs) == 'a': | 791 if repo.dirstate.state(abs) == 'a': |
718 forget.append(abs) | 792 forget.append(abs) |
719 if not exact: ui.status('forgetting ', rel, '\n') | 793 if not exact: ui.status('forgetting ', rel, '\n') |
720 repo.forget(forget) | 794 repo.forget(forget) |
795 | |
796 def grep(ui, repo, pattern = None, *pats, **opts): | |
797 """search for a pattern in specified files and revisions""" | |
798 if pattern is None: pattern = opts['regexp'] | |
799 if not pattern: raise util.Abort('no pattern to search for') | |
800 reflags = 0 | |
801 if opts['ignore_case']: reflags |= re.I | |
802 regexp = re.compile(pattern, reflags) | |
803 sep, end = ':', '\n' | |
804 if opts['null'] or opts['print0']: sep = end = '\0' | |
805 | |
806 fcache = {} | |
807 def getfile(fn): | |
808 if fn not in fcache: | |
809 fcache[fn] = repo.file(fn) | |
810 return fcache[fn] | |
811 | |
812 def matchlines(body): | |
813 begin = 0 | |
814 linenum = 0 | |
815 while True: | |
816 match = regexp.search(body, begin) | |
817 if not match: break | |
818 mstart, mend = match.span() | |
819 linenum += body.count('\n', begin, mstart) + 1 | |
820 lstart = body.rfind('\n', begin, mstart) + 1 or begin | |
821 lend = body.find('\n', mend) | |
822 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend] | |
823 begin = lend + 1 | |
824 | |
825 class linestate: | |
826 def __init__(self, line, linenum, colstart, colend): | |
827 self.line = line | |
828 self.linenum = linenum | |
829 self.colstart = colstart | |
830 self.colend = colend | |
831 def __eq__(self, other): return self.line == other.line | |
832 def __hash__(self): return hash(self.line) | |
833 | |
834 matches = {} | |
835 def grepbody(fn, rev, body): | |
836 matches[rev].setdefault(fn, {}) | |
837 m = matches[rev][fn] | |
838 for lnum, cstart, cend, line in matchlines(body): | |
839 s = linestate(line, lnum, cstart, cend) | |
840 m[s] = s | |
841 | |
842 prev = {} | |
843 def display(fn, rev, states, prevstates): | |
844 diff = list(set(states).symmetric_difference(set(prevstates))) | |
845 diff.sort(lambda x, y: cmp(x.linenum, y.linenum)) | |
846 for l in diff: | |
847 if incrementing: | |
848 change = ((l in prevstates) and '-') or '+' | |
849 r = rev | |
850 else: | |
851 change = ((l in states) and '-') or '+' | |
852 r = prev[fn] | |
853 ui.write('%s:%s:%s:%s%s\n' % (fn, r, l.linenum, change, l.line)) | |
854 | |
855 fstate = {} | |
856 for st, rev, fns in walkchangerevs(ui, repo, repo.getcwd(), pats, opts): | |
857 if st == 'window': | |
858 incrementing = rev | |
859 matches.clear() | |
860 elif st == 'add': | |
861 change = repo.changelog.read(repo.lookup(str(rev))) | |
862 mf = repo.manifest.read(change[0]) | |
863 matches[rev] = {} | |
864 for fn in fns: | |
865 fstate.setdefault(fn, {}) | |
866 try: | |
867 grepbody(fn, rev, getfile(fn).read(mf[fn])) | |
868 except KeyError: | |
869 pass | |
870 elif st == 'iter': | |
871 states = matches[rev].items() | |
872 states.sort() | |
873 for fn, m in states: | |
874 if incrementing or fstate[fn]: | |
875 display(fn, rev, m, fstate[fn]) | |
876 fstate[fn] = m | |
877 prev[fn] = rev | |
878 | |
879 if not incrementing: | |
880 fstate = fstate.items() | |
881 fstate.sort() | |
882 for fn, state in fstate: | |
883 display(fn, rev, {}, state) | |
721 | 884 |
722 def heads(ui, repo, **opts): | 885 def heads(ui, repo, **opts): |
723 """show current repository heads""" | 886 """show current repository heads""" |
724 heads = repo.changelog.heads() | 887 heads = repo.changelog.heads() |
725 br = None | 888 br = None |
846 else: | 1009 else: |
847 ui.write(rel, end) | 1010 ui.write(rel, end) |
848 | 1011 |
849 def log(ui, repo, *pats, **opts): | 1012 def log(ui, repo, *pats, **opts): |
850 """show revision history of entire repository or files""" | 1013 """show revision history of entire repository or files""" |
851 # This code most commonly needs to iterate backwards over the | 1014 class dui: |
852 # history it is interested in. This has awful (quadratic-looking) | 1015 # Implement and delegate some ui protocol. Save hunks of |
853 # performance, so we use iterators that walk forwards through | 1016 # output for later display in the desired order. |
854 # windows of revisions, yielding revisions in reverse order, while | 1017 def __init__(self, ui): |
855 # walking the windows backwards. | 1018 self.ui = ui |
1019 self.hunk = {} | |
1020 def bump(self, rev): | |
1021 self.rev = rev | |
1022 self.hunk[rev] = [] | |
1023 def note(self, *args): | |
1024 if self.verbose: self.write(*args) | |
1025 def status(self, *args): | |
1026 if not self.quiet: self.write(*args) | |
1027 def write(self, *args): | |
1028 self.hunk[self.rev].append(args) | |
1029 def __getattr__(self, key): | |
1030 return getattr(self.ui, key) | |
856 cwd = repo.getcwd() | 1031 cwd = repo.getcwd() |
857 if not pats and cwd: | 1032 if not pats and cwd: |
858 opts['include'] = [os.path.join(cwd, i) for i in opts['include']] | 1033 opts['include'] = [os.path.join(cwd, i) for i in opts['include']] |
859 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']] | 1034 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']] |
860 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '', | 1035 for st, rev, fns in walkchangerevs(ui, repo, (pats and cwd) or '', pats, |
861 pats, opts) | 1036 opts): |
862 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0'])) | 1037 if st == 'window': |
863 wanted = {} | |
864 slowpath = anypats | |
865 window = 300 | |
866 if not slowpath and not files: | |
867 # No files, no patterns. Display all revs. | |
868 wanted = dict(zip(revs, revs)) | |
869 if not slowpath: | |
870 # Only files, no patterns. Check the history of each file. | |
871 def filerevgen(filelog): | |
872 for i in xrange(filelog.count() - 1, -1, -window): | |
873 revs = [] | |
874 for j in xrange(max(0, i - window), i + 1): | |
875 revs.append(filelog.linkrev(filelog.node(j))) | |
876 revs.reverse() | |
877 for rev in revs: | |
878 yield rev | |
879 | |
880 minrev, maxrev = min(revs), max(revs) | |
881 for filelog in map(repo.file, files): | |
882 # A zero count may be a directory or deleted file, so | |
883 # try to find matching entries on the slow path. | |
884 if filelog.count() == 0: | |
885 slowpath = True | |
886 break | |
887 for rev in filerevgen(filelog): | |
888 if rev <= maxrev: | |
889 if rev < minrev: break | |
890 wanted[rev] = 1 | |
891 if slowpath: | |
892 # The slow path checks files modified in every changeset. | |
893 def mfrevgen(): | |
894 for i in xrange(repo.changelog.count() - 1, -1, -window): | |
895 for j in xrange(max(0, i - window), i + 1): | |
896 yield j, repo.changelog.read(repo.lookup(str(j)))[3] | |
897 | |
898 for rev, mf in mfrevgen(): | |
899 if filter(matchfn, mf): | |
900 wanted[rev] = 1 | |
901 | |
902 def changerevgen(): | |
903 class dui: | |
904 # Implement and delegate some ui protocol. Save hunks of | |
905 # output for later display in the desired order. | |
906 def __init__(self, ui): | |
907 self.ui = ui | |
908 self.hunk = {} | |
909 def bump(self, rev): | |
910 self.rev = rev | |
911 self.hunk[rev] = [] | |
912 def note(self, *args): | |
913 if self.verbose: self.write(*args) | |
914 def status(self, *args): | |
915 if not self.quiet: self.write(*args) | |
916 def write(self, *args): | |
917 self.hunk[self.rev].append(args) | |
918 def __getattr__(self, key): | |
919 return getattr(self.ui, key) | |
920 for i in xrange(0, len(revs), window): | |
921 nrevs = [rev for rev in revs[i : min(i + window, len(revs))] | |
922 if rev in wanted] | |
923 srevs = list(nrevs) | |
924 srevs.sort() | |
925 du = dui(ui) | 1038 du = dui(ui) |
926 for rev in srevs: | 1039 elif st == 'add': |
927 du.bump(rev) | 1040 du.bump(rev) |
928 yield rev, du | 1041 show_changeset(du, repo, rev) |
929 for rev in nrevs: | 1042 if opts['patch']: |
930 for args in du.hunk[rev]: | 1043 changenode = repo.changelog.node(rev) |
931 ui.write(*args) | 1044 prev, other = repo.changelog.parents(changenode) |
932 | 1045 dodiff(du, du, repo, prev, changenode, fns) |
933 for rev, dui in changerevgen(): | 1046 du.write("\n\n") |
934 show_changeset(dui, repo, rev) | 1047 elif st == 'iter': |
935 if opts['patch']: | 1048 for args in du.hunk[rev]: |
936 changenode = repo.changelog.node(rev) | 1049 ui.write(*args) |
937 prev, other = repo.changelog.parents(changenode) | |
938 dodiff(dui, dui, repo, prev, changenode, files) | |
939 dui.write("\n\n") | |
940 | 1050 |
941 def manifest(ui, repo, rev=None): | 1051 def manifest(ui, repo, rev=None): |
942 """output the latest or given revision of the project manifest""" | 1052 """output the latest or given revision of the project manifest""" |
943 if rev: | 1053 if rev: |
944 try: | 1054 try: |
1408 "forget": | 1518 "forget": |
1409 (forget, | 1519 (forget, |
1410 [('I', 'include', [], 'include path in search'), | 1520 [('I', 'include', [], 'include path in search'), |
1411 ('X', 'exclude', [], 'exclude path from search')], | 1521 ('X', 'exclude', [], 'exclude path from search')], |
1412 "hg forget [OPTION]... FILE..."), | 1522 "hg forget [OPTION]... FILE..."), |
1523 "grep": (grep, | |
1524 [('0', 'print0', None, 'terminate file names with NUL'), | |
1525 ('I', 'include', [], 'include path in search'), | |
1526 ('X', 'exclude', [], 'include path in search'), | |
1527 ('Z', 'null', None, 'terminate file names with NUL'), | |
1528 ('a', 'all-revs', '', 'search all revs'), | |
1529 ('e', 'regexp', '', 'pattern to search for'), | |
1530 ('f', 'full-path', None, 'print complete paths'), | |
1531 ('i', 'ignore-case', None, 'ignore case when matching'), | |
1532 ('l', 'files-with-matches', None, 'print names of files with matches'), | |
1533 ('n', 'line-number', '', 'print line numbers'), | |
1534 ('r', 'rev', [], 'search in revision rev'), | |
1535 ('s', 'no-messages', None, 'do not print error messages'), | |
1536 ('v', 'invert-match', None, 'select non-matching lines')], | |
1537 "hg grep [options] [pat] [files]"), | |
1413 "heads": | 1538 "heads": |
1414 (heads, | 1539 (heads, |
1415 [('b', 'branches', None, 'find branch info')], | 1540 [('b', 'branches', None, 'find branch info')], |
1416 'hg heads [-b]'), | 1541 'hg heads [-b]'), |
1417 "help": (help_, [], 'hg help [COMMAND]'), | 1542 "help": (help_, [], 'hg help [COMMAND]'), |