Show More
@@ -46,6 +46,80 b" def walk(repo, pats, opts, head = ''):" | |||
|
46 | 46 | files, matchfn, results = makewalk(repo, pats, opts, head) |
|
47 | 47 | for r in results: yield r |
|
48 | 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 | |
|
122 | ||
|
49 | 123 | revrangesep = ':' |
|
50 | 124 | |
|
51 | 125 | def revrange(ui, repo, revs, revlog=None): |
@@ -716,6 +790,89 b' def forget(ui, repo, *pats, **opts):' | |||
|
716 | 790 | if not exact: ui.status('forgetting ', rel, '\n') |
|
717 | 791 | repo.forget(forget) |
|
718 | 792 | |
|
793 | def grep(ui, repo, pattern = None, *pats, **opts): | |
|
794 | if pattern is None: pattern = opts['regexp'] | |
|
795 | if not pattern: raise util.Abort('no pattern to search for') | |
|
796 | reflags = 0 | |
|
797 | if opts['ignore_case']: reflags |= re.I | |
|
798 | regexp = re.compile(pattern, reflags) | |
|
799 | sep, end = ':', '\n' | |
|
800 | if opts['null'] or opts['print0']: sep = end = '\0' | |
|
801 | ||
|
802 | fcache = {} | |
|
803 | def getfile(fn): | |
|
804 | if fn not in fcache: | |
|
805 | fcache[fn] = repo.file(fn) | |
|
806 | return fcache[fn] | |
|
807 | ||
|
808 | def matchlines(body): | |
|
809 | for match in regexp.finditer(body): | |
|
810 | start, end = match.span() | |
|
811 | lnum = body.count('\n', 0, start) + 1 | |
|
812 | lstart = body.rfind('\n', 0, start) + 1 | |
|
813 | lend = body.find('\n', end) | |
|
814 | yield lnum, start - lstart, end - lstart, body[lstart:lend] | |
|
815 | ||
|
816 | class linestate: | |
|
817 | def __init__(self, line, linenum, colstart, colend): | |
|
818 | self.line = line | |
|
819 | self.linenum = linenum | |
|
820 | self.colstart = colstart | |
|
821 | self.colend = colend | |
|
822 | def __eq__(self, other): return self.line == other.line | |
|
823 | def __hash__(self): return hash(self.line) | |
|
824 | ||
|
825 | matches = {} | |
|
826 | def grepbody(fn, rev, body): | |
|
827 | matches[rev].setdefault(fn, {}) | |
|
828 | m = matches[rev][fn] | |
|
829 | for lnum, cstart, cend, line in matchlines(body): | |
|
830 | s = linestate(line, lnum, cstart, cend) | |
|
831 | m[s] = s | |
|
832 | ||
|
833 | prev = {} | |
|
834 | def display(fn, rev, states, prevstates): | |
|
835 | diff = list(set(states).symmetric_difference(set(prevstates))) | |
|
836 | diff.sort(lambda x, y: cmp(x.linenum, y.linenum)) | |
|
837 | for l in diff: | |
|
838 | if incrementing: | |
|
839 | change = ((l in prevstates) and '-') or '+' | |
|
840 | r = rev | |
|
841 | else: | |
|
842 | change = ((l in states) and '-') or '+' | |
|
843 | r = prev[fn] | |
|
844 | ui.write('%s:%s:%s:%s%s\n' % (fn, r, l.linenum, change, l.line)) | |
|
845 | ||
|
846 | fstate = {} | |
|
847 | for st, rev, fns in walkchangerevs(ui, repo, repo.getcwd(), pats, opts): | |
|
848 | if st == 'window': | |
|
849 | incrementing = rev | |
|
850 | matches.clear() | |
|
851 | elif st == 'add': | |
|
852 | change = repo.changelog.read(repo.lookup(str(rev))) | |
|
853 | mf = repo.manifest.read(change[0]) | |
|
854 | matches[rev] = {} | |
|
855 | for fn in fns: | |
|
856 | fstate.setdefault(fn, {}) | |
|
857 | try: | |
|
858 | grepbody(fn, rev, getfile(fn).read(mf[fn])) | |
|
859 | except KeyError: | |
|
860 | pass | |
|
861 | elif st == 'iter': | |
|
862 | states = matches[rev].items() | |
|
863 | states.sort() | |
|
864 | for fn, m in states: | |
|
865 | if incrementing or fstate[fn]: | |
|
866 | display(fn, rev, m, fstate[fn]) | |
|
867 | fstate[fn] = m | |
|
868 | prev[fn] = rev | |
|
869 | ||
|
870 | if not incrementing: | |
|
871 | fstate = fstate.items() | |
|
872 | fstate.sort() | |
|
873 | for fn, state in fstate: | |
|
874 | display(fn, rev, {}, state) | |
|
875 | ||
|
719 | 876 | def heads(ui, repo, **opts): |
|
720 | 877 | """show current repository heads""" |
|
721 | 878 | heads = repo.changelog.heads() |
@@ -845,59 +1002,6 b' def locate(ui, repo, *pats, **opts):' | |||
|
845 | 1002 | |
|
846 | 1003 | def log(ui, repo, *pats, **opts): |
|
847 | 1004 | """show revision history of entire repository or files""" |
|
848 | # This code most commonly needs to iterate backwards over the | |
|
849 | # history it is interested in. This has awful (quadratic-looking) | |
|
850 | # performance, so we use iterators that walk forwards through | |
|
851 | # windows of revisions, yielding revisions in reverse order, while | |
|
852 | # walking the windows backwards. | |
|
853 | cwd = repo.getcwd() | |
|
854 | if not pats and cwd: | |
|
855 | opts['include'] = [os.path.join(cwd, i) for i in opts['include']] | |
|
856 | opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']] | |
|
857 | files, matchfn, anypats = matchpats(repo, (pats and cwd) or '', | |
|
858 | pats, opts) | |
|
859 | revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0'])) | |
|
860 | wanted = {} | |
|
861 | slowpath = anypats | |
|
862 | window = 300 | |
|
863 | if not slowpath and not files: | |
|
864 | # No files, no patterns. Display all revs. | |
|
865 | wanted = dict(zip(revs, revs)) | |
|
866 | if not slowpath: | |
|
867 | # Only files, no patterns. Check the history of each file. | |
|
868 | def filerevgen(filelog): | |
|
869 | for i in xrange(filelog.count() - 1, -1, -window): | |
|
870 | print "filelog" | |
|
871 | revs = [] | |
|
872 | for j in xrange(max(0, i - window), i + 1): | |
|
873 | revs.append(filelog.linkrev(filelog.node(j))) | |
|
874 | revs.reverse() | |
|
875 | for rev in revs: | |
|
876 | yield rev | |
|
877 | ||
|
878 | minrev, maxrev = min(revs), max(revs) | |
|
879 | for filelog in map(repo.file, files): | |
|
880 | # A zero count may be a directory or deleted file, so | |
|
881 | # try to find matching entries on the slow path. | |
|
882 | if filelog.count() == 0: | |
|
883 | slowpath = True | |
|
884 | break | |
|
885 | for rev in filerevgen(filelog): | |
|
886 | if rev <= maxrev: | |
|
887 | if rev < minrev: break | |
|
888 | wanted[rev] = 1 | |
|
889 | if slowpath: | |
|
890 | # The slow path checks files modified in every changeset. | |
|
891 | def mfrevgen(): | |
|
892 | for i in xrange(repo.changelog.count() - 1, -1, -window): | |
|
893 | for j in xrange(max(0, i - window), i + 1): | |
|
894 | yield j, repo.changelog.read(repo.lookup(str(j)))[3] | |
|
895 | ||
|
896 | for rev, mf in mfrevgen(): | |
|
897 | if filter(matchfn, mf): | |
|
898 | wanted[rev] = 1 | |
|
899 | ||
|
900 | def changerevgen(): | |
|
901 | 1005 |
|
|
902 | 1006 |
|
|
903 | 1007 |
|
@@ -915,26 +1019,25 b' def log(ui, repo, *pats, **opts):' | |||
|
915 | 1019 |
|
|
916 | 1020 |
|
|
917 | 1021 |
|
|
918 | for i in xrange(0, len(revs), window): | |
|
919 | nrevs = [rev for rev in revs[i : min(i + window, len(revs))] | |
|
920 | if rev in wanted] | |
|
921 | srevs = list(nrevs) | |
|
922 | srevs.sort() | |
|
1022 | cwd = repo.getcwd() | |
|
1023 | if not pats and cwd: | |
|
1024 | opts['include'] = [os.path.join(cwd, i) for i in opts['include']] | |
|
1025 | opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']] | |
|
1026 | for st, rev, fns in walkchangerevs(ui, repo, (pats and cwd) or '', pats, | |
|
1027 | opts): | |
|
1028 | if st == 'window': | |
|
923 | 1029 | du = dui(ui) |
|
924 | for rev in srevs: | |
|
1030 | elif st == 'add': | |
|
925 | 1031 |
|
|
926 | yield rev, du | |
|
927 | for rev in nrevs: | |
|
928 | for args in du.hunk[rev]: | |
|
929 | ui.write(*args) | |
|
930 | ||
|
931 | for rev, dui in changerevgen(): | |
|
932 | show_changeset(dui, repo, rev) | |
|
1032 | show_changeset(du, repo, rev) | |
|
933 | 1033 | if opts['patch']: |
|
934 | 1034 | changenode = repo.changelog.node(rev) |
|
935 | 1035 | prev, other = repo.changelog.parents(changenode) |
|
936 |
dodiff(du |
|
|
937 |
du |
|
|
1036 | dodiff(du, du, repo, prev, changenode, fns) | |
|
1037 | du.write("\n\n") | |
|
1038 | elif st == 'iter': | |
|
1039 | for args in du.hunk[rev]: | |
|
1040 | ui.write(*args) | |
|
938 | 1041 | |
|
939 | 1042 | def manifest(ui, repo, rev=None): |
|
940 | 1043 | """output the latest or given revision of the project manifest""" |
@@ -1408,6 +1511,21 b' table = {' | |||
|
1408 | 1511 | [('I', 'include', [], 'include path in search'), |
|
1409 | 1512 | ('X', 'exclude', [], 'exclude path from search')], |
|
1410 | 1513 | "hg forget [OPTION]... FILE..."), |
|
1514 | "grep": (grep, | |
|
1515 | [('0', 'print0', None, 'terminate file names with NUL'), | |
|
1516 | ('I', 'include', [], 'include path in search'), | |
|
1517 | ('X', 'exclude', [], 'include path in search'), | |
|
1518 | ('Z', 'null', None, 'terminate file names with NUL'), | |
|
1519 | ('a', 'all-revs', '', 'search all revs'), | |
|
1520 | ('e', 'regexp', '', 'pattern to search for'), | |
|
1521 | ('f', 'full-path', None, 'print complete paths'), | |
|
1522 | ('i', 'ignore-case', None, 'ignore case when matching'), | |
|
1523 | ('l', 'files-with-matches', None, 'print names of files with matches'), | |
|
1524 | ('n', 'line-number', '', 'print line numbers'), | |
|
1525 | ('r', 'rev', [], 'search in revision rev'), | |
|
1526 | ('s', 'no-messages', None, 'do not print error messages'), | |
|
1527 | ('v', 'invert-match', None, 'select non-matching lines')], | |
|
1528 | "hg grep [options] [pat] [files]"), | |
|
1411 | 1529 | "heads": |
|
1412 | 1530 | (heads, |
|
1413 | 1531 | [('b', 'branches', None, 'find branch info')], |
General Comments 0
You need to be logged in to leave comments.
Login now