Show More
@@ -0,0 +1,22 | |||||
|
1 | #!/bin/sh | |||
|
2 | ||||
|
3 | mkdir t | |||
|
4 | cd t | |||
|
5 | hg init | |||
|
6 | echo import > port | |||
|
7 | hg add port | |||
|
8 | hg commit -m 0 -u spam -d '0 0' | |||
|
9 | echo export >> port | |||
|
10 | hg commit -m 1 -u eggs -d '1 0' | |||
|
11 | echo export > port | |||
|
12 | echo vaportight >> port | |||
|
13 | echo 'import/export' >> port | |||
|
14 | hg commit -m 2 -u spam -d '2 0' | |||
|
15 | echo 'import/export' >> port | |||
|
16 | hg commit -m 3 -u eggs -d '3 0' | |||
|
17 | head -3 port > port1 | |||
|
18 | mv port1 port | |||
|
19 | hg commit -m 4 -u spam -d '4 0' | |||
|
20 | hg grep port port | |||
|
21 | hg grep -enu port port | |||
|
22 | hg grep import port |
@@ -207,6 +207,31 forget [options] [files]:: | |||||
207 | -I, --include <pat> include names matching the given patterns |
|
207 | -I, --include <pat> include names matching the given patterns | |
208 | -X, --exclude <pat> exclude names matching the given patterns |
|
208 | -X, --exclude <pat> exclude names matching the given patterns | |
209 |
|
209 | |||
|
210 | grep [options] pattern [files]:: | |||
|
211 | Search revisions of files for a regular expression. | |||
|
212 | ||||
|
213 | This command behaves differently than Unix grep. It only accepts | |||
|
214 | Python/Perl regexps. It searches repository history, not the | |||
|
215 | working directory. It always prints the revision number in which | |||
|
216 | a match appears. | |||
|
217 | ||||
|
218 | By default, grep only prints output for the first revision of a | |||
|
219 | file in which it finds a match. To get it to print every revision | |||
|
220 | that contains a change in match status ("-" for a match that | |||
|
221 | becomes a non-match, or "+" for a non-match that becomes a match), | |||
|
222 | use the --every-match flag. | |||
|
223 | ||||
|
224 | options: | |||
|
225 | -0, --print0 end fields with NUL | |||
|
226 | -I, --include <pat> include names matching the given patterns | |||
|
227 | -X, --exclude <pat> exclude names matching the given patterns | |||
|
228 | -e, --every-match print every revision that matches | |||
|
229 | -i, --ignore-case ignore case when matching | |||
|
230 | -l, --files-with-matches print only file names and revs that match | |||
|
231 | -n, --line-number print matching line numbers | |||
|
232 | -r <rev>, --rev <rev> search in given revision range | |||
|
233 | -u, --user print user who committed change | |||
|
234 | ||||
210 | heads:: |
|
235 | heads:: | |
211 | Show all repository head changesets. |
|
236 | Show all repository head changesets. | |
212 |
|
237 |
@@ -49,12 +49,31 def walk(repo, pats, opts, head=''): | |||||
49 | yield r |
|
49 | yield r | |
50 |
|
50 | |||
51 | def walkchangerevs(ui, repo, cwd, pats, opts): |
|
51 | def walkchangerevs(ui, repo, cwd, pats, opts): | |
52 | # This code most commonly needs to iterate backwards over the |
|
52 | '''Iterate over files and the revs they changed in. | |
53 | # history it is interested in. Doing so has awful |
|
53 | ||
54 | # (quadratic-looking) performance, so we use iterators in a |
|
54 | Callers most commonly need to iterate backwards over the history | |
55 | # "windowed" way. Walk forwards through a window of revisions, |
|
55 | it is interested in. Doing so has awful (quadratic-looking) | |
56 | # yielding them in the desired order, and walk the windows |
|
56 | performance, so we use iterators in a "windowed" way. | |
57 | # themselves backwards. |
|
57 | ||
|
58 | We walk a window of revisions in the desired order. Within the | |||
|
59 | window, we first walk forwards to gather data, then in the desired | |||
|
60 | order (usually backwards) to display it. | |||
|
61 | ||||
|
62 | This function returns an (iterator, getchange) pair. The | |||
|
63 | getchange function returns the changelog entry for a numeric | |||
|
64 | revision. The iterator yields 3-tuples. They will be of one of | |||
|
65 | the following forms: | |||
|
66 | ||||
|
67 | "window", incrementing, lastrev: stepping through a window, | |||
|
68 | positive if walking forwards through revs, last rev in the | |||
|
69 | sequence iterated over - use to reset state for the current window | |||
|
70 | ||||
|
71 | "add", rev, fns: out-of-order traversal of the given file names | |||
|
72 | fns, which changed during revision rev - use to gather data for | |||
|
73 | possible display | |||
|
74 | ||||
|
75 | "iter", rev, None: in-order traversal of the revs earlier iterated | |||
|
76 | over with "add" - use to display data''' | |||
58 | cwd = repo.getcwd() |
|
77 | cwd = repo.getcwd() | |
59 | if not pats and cwd: |
|
78 | if not pats and cwd: | |
60 | opts['include'] = [os.path.join(cwd, i) for i in opts['include']] |
|
79 | opts['include'] = [os.path.join(cwd, i) for i in opts['include']] | |
@@ -66,6 +85,14 def walkchangerevs(ui, repo, cwd, pats, | |||||
66 | slowpath = anypats |
|
85 | slowpath = anypats | |
67 | window = 300 |
|
86 | window = 300 | |
68 | fncache = {} |
|
87 | fncache = {} | |
|
88 | ||||
|
89 | chcache = {} | |||
|
90 | def getchange(rev): | |||
|
91 | ch = chcache.get(rev) | |||
|
92 | if ch is None: | |||
|
93 | chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev))) | |||
|
94 | return ch | |||
|
95 | ||||
69 | if not slowpath and not files: |
|
96 | if not slowpath and not files: | |
70 | # No files, no patterns. Display all revs. |
|
97 | # No files, no patterns. Display all revs. | |
71 | wanted = dict(zip(revs, revs)) |
|
98 | wanted = dict(zip(revs, revs)) | |
@@ -100,7 +127,7 def walkchangerevs(ui, repo, cwd, pats, | |||||
100 | def changerevgen(): |
|
127 | def changerevgen(): | |
101 | for i in xrange(repo.changelog.count() - 1, -1, -window): |
|
128 | for i in xrange(repo.changelog.count() - 1, -1, -window): | |
102 | for j in xrange(max(0, i - window), i + 1): |
|
129 | for j in xrange(max(0, i - window), i + 1): | |
103 |
yield j, |
|
130 | yield j, getchange(j)[3] | |
104 |
|
131 | |||
105 | for rev, changefiles in changerevgen(): |
|
132 | for rev, changefiles in changerevgen(): | |
106 | matches = filter(matchfn, changefiles) |
|
133 | matches = filter(matchfn, changefiles) | |
@@ -108,6 +135,7 def walkchangerevs(ui, repo, cwd, pats, | |||||
108 | fncache[rev] = matches |
|
135 | fncache[rev] = matches | |
109 | wanted[rev] = 1 |
|
136 | wanted[rev] = 1 | |
110 |
|
137 | |||
|
138 | def iterate(): | |||
111 | for i in xrange(0, len(revs), window): |
|
139 | for i in xrange(0, len(revs), window): | |
112 | yield 'window', revs[0] < revs[-1], revs[-1] |
|
140 | yield 'window', revs[0] < revs[-1], revs[-1] | |
113 | nrevs = [rev for rev in revs[i:min(i+window, len(revs))] |
|
141 | nrevs = [rev for rev in revs[i:min(i+window, len(revs))] | |
@@ -115,13 +143,11 def walkchangerevs(ui, repo, cwd, pats, | |||||
115 | srevs = list(nrevs) |
|
143 | srevs = list(nrevs) | |
116 | srevs.sort() |
|
144 | srevs.sort() | |
117 | for rev in srevs: |
|
145 | for rev in srevs: | |
118 | fns = fncache.get(rev) |
|
146 | fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3]) | |
119 | if not fns: |
|
|||
120 | fns = repo.changelog.read(repo.lookup(str(rev)))[3] |
|
|||
121 | fns = filter(matchfn, fns) |
|
|||
122 | yield 'add', rev, fns |
|
147 | yield 'add', rev, fns | |
123 | for rev in nrevs: |
|
148 | for rev in nrevs: | |
124 | yield 'iter', rev, None |
|
149 | yield 'iter', rev, None | |
|
150 | return iterate(), getchange | |||
125 |
|
151 | |||
126 | revrangesep = ':' |
|
152 | revrangesep = ':' | |
127 |
|
153 | |||
@@ -150,6 +176,7 def revrange(ui, repo, revs, revlog=None | |||||
150 | except KeyError: |
|
176 | except KeyError: | |
151 | raise util.Abort('invalid revision identifier %s', val) |
|
177 | raise util.Abort('invalid revision identifier %s', val) | |
152 | return num |
|
178 | return num | |
|
179 | seen = {} | |||
153 | for spec in revs: |
|
180 | for spec in revs: | |
154 | if spec.find(revrangesep) >= 0: |
|
181 | if spec.find(revrangesep) >= 0: | |
155 | start, end = spec.split(revrangesep, 1) |
|
182 | start, end = spec.split(revrangesep, 1) | |
@@ -157,9 +184,14 def revrange(ui, repo, revs, revlog=None | |||||
157 | end = fix(end, revcount - 1) |
|
184 | end = fix(end, revcount - 1) | |
158 | step = start > end and -1 or 1 |
|
185 | step = start > end and -1 or 1 | |
159 | for rev in xrange(start, end+step, step): |
|
186 | for rev in xrange(start, end+step, step): | |
|
187 | if rev in seen: continue | |||
|
188 | seen[rev] = 1 | |||
160 | yield str(rev) |
|
189 | yield str(rev) | |
161 | else: |
|
190 | else: | |
162 |
|
|
191 | rev = fix(spec, None) | |
|
192 | if rev in seen: continue | |||
|
193 | seen[rev] = 1 | |||
|
194 | yield str(rev) | |||
163 |
|
195 | |||
164 | def make_filename(repo, r, pat, node=None, |
|
196 | def make_filename(repo, r, pat, node=None, | |
165 | total=None, seqno=None, revwidth=None): |
|
197 | total=None, seqno=None, revwidth=None): | |
@@ -265,6 +297,21 def dodiff(fp, ui, repo, node1, node2, f | |||||
265 | tn = None |
|
297 | tn = None | |
266 | fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text)) |
|
298 | fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text)) | |
267 |
|
299 | |||
|
300 | def trimuser(ui, rev, name, revcache): | |||
|
301 | """trim the name of the user who committed a change""" | |||
|
302 | try: | |||
|
303 | return revcache[rev] | |||
|
304 | except KeyError: | |||
|
305 | if not ui.verbose: | |||
|
306 | f = name.find('@') | |||
|
307 | if f >= 0: | |||
|
308 | name = name[:f] | |||
|
309 | f = name.find('<') | |||
|
310 | if f >= 0: | |||
|
311 | name = name[f+1:] | |||
|
312 | revcache[rev] = name | |||
|
313 | return name | |||
|
314 | ||||
268 | def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None): |
|
315 | def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None): | |
269 | """show a single changeset or file revision""" |
|
316 | """show a single changeset or file revision""" | |
270 | log = repo.changelog |
|
317 | log = repo.changelog | |
@@ -467,25 +514,14 def annotate(ui, repo, *pats, **opts): | |||||
467 | def getnode(rev): |
|
514 | def getnode(rev): | |
468 | return short(repo.changelog.node(rev)) |
|
515 | return short(repo.changelog.node(rev)) | |
469 |
|
516 | |||
|
517 | ucache = {} | |||
470 | def getname(rev): |
|
518 | def getname(rev): | |
471 | try: |
|
|||
472 | return bcache[rev] |
|
|||
473 | except KeyError: |
|
|||
474 |
|
|
519 | cl = repo.changelog.read(repo.changelog.node(rev)) | |
475 | name = cl[1] |
|
520 | return trimuser(ui, rev, cl[1], ucache) | |
476 | f = name.find('@') |
|
|||
477 | if f >= 0: |
|
|||
478 | name = name[:f] |
|
|||
479 | f = name.find('<') |
|
|||
480 | if f >= 0: |
|
|||
481 | name = name[f+1:] |
|
|||
482 | bcache[rev] = name |
|
|||
483 | return name |
|
|||
484 |
|
521 | |||
485 | if not pats: |
|
522 | if not pats: | |
486 | raise util.Abort('at least one file name or pattern required') |
|
523 | raise util.Abort('at least one file name or pattern required') | |
487 |
|
524 | |||
488 | bcache = {} |
|
|||
489 | opmap = [['user', getname], ['number', str], ['changeset', getnode]] |
|
525 | opmap = [['user', getname], ['number', str], ['changeset', getnode]] | |
490 | if not opts['user'] and not opts['changeset']: |
|
526 | if not opts['user'] and not opts['changeset']: | |
491 | opts['number'] = 1 |
|
527 | opts['number'] = 1 | |
@@ -826,9 +862,9 def grep(ui, repo, pattern, *pats, **opt | |||||
826 | if opts['ignore_case']: |
|
862 | if opts['ignore_case']: | |
827 | reflags |= re.I |
|
863 | reflags |= re.I | |
828 | regexp = re.compile(pattern, reflags) |
|
864 | regexp = re.compile(pattern, reflags) | |
829 |
sep, e |
|
865 | sep, eol = ':', '\n' | |
830 | if opts['print0']: |
|
866 | if opts['print0']: | |
831 |
sep = e |
|
867 | sep = eol = '\0' | |
832 |
|
868 | |||
833 | fcache = {} |
|
869 | fcache = {} | |
834 | def getfile(fn): |
|
870 | def getfile(fn): | |
@@ -870,10 +906,12 def grep(ui, repo, pattern, *pats, **opt | |||||
870 | m[s] = s |
|
906 | m[s] = s | |
871 |
|
907 | |||
872 | prev = {} |
|
908 | prev = {} | |
|
909 | ucache = {} | |||
873 | def display(fn, rev, states, prevstates): |
|
910 | def display(fn, rev, states, prevstates): | |
874 | diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates))) |
|
911 | diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates))) | |
875 | diff.sort(lambda x, y: cmp(x.linenum, y.linenum)) |
|
912 | diff.sort(lambda x, y: cmp(x.linenum, y.linenum)) | |
876 | counts = {'-': 0, '+': 0} |
|
913 | counts = {'-': 0, '+': 0} | |
|
914 | filerevmatches = {} | |||
877 | for l in diff: |
|
915 | for l in diff: | |
878 | if incrementing or not opts['every_match']: |
|
916 | if incrementing or not opts['every_match']: | |
879 | change = ((l in prevstates) and '-') or '+' |
|
917 | change = ((l in prevstates) and '-') or '+' | |
@@ -881,13 +919,26 def grep(ui, repo, pattern, *pats, **opt | |||||
881 | else: |
|
919 | else: | |
882 | change = ((l in states) and '-') or '+' |
|
920 | change = ((l in states) and '-') or '+' | |
883 | r = prev[fn] |
|
921 | r = prev[fn] | |
884 | ui.write('%s:%s:%s:%s%s\n' % (fn, r, l.linenum, change, l.line)) |
|
922 | cols = [fn, str(rev)] | |
|
923 | if opts['line_number']: cols.append(str(l.linenum)) | |||
|
924 | if opts['every_match']: cols.append(change) | |||
|
925 | if opts['user']: cols.append(trimuser(ui, rev, getchange(rev)[1], | |||
|
926 | ucache)) | |||
|
927 | if opts['files_with_matches']: | |||
|
928 | c = (fn, rev) | |||
|
929 | if c in filerevmatches: continue | |||
|
930 | filerevmatches[c] = 1 | |||
|
931 | else: | |||
|
932 | cols.append(l.line) | |||
|
933 | ui.write(sep.join(cols), eol) | |||
885 | counts[change] += 1 |
|
934 | counts[change] += 1 | |
886 | return counts['+'], counts['-'] |
|
935 | return counts['+'], counts['-'] | |
887 |
|
936 | |||
888 | fstate = {} |
|
937 | fstate = {} | |
889 | skip = {} |
|
938 | skip = {} | |
890 |
|
|
939 | changeiter, getchange = walkchangerevs(ui, repo, repo.getcwd(), pats, opts) | |
|
940 | count = 0 | |||
|
941 | for st, rev, fns in changeiter: | |||
891 | if st == 'window': |
|
942 | if st == 'window': | |
892 | incrementing = rev |
|
943 | incrementing = rev | |
893 | matches.clear() |
|
944 | matches.clear() | |
@@ -909,6 +960,7 def grep(ui, repo, pattern, *pats, **opt | |||||
909 | if fn in skip: continue |
|
960 | if fn in skip: continue | |
910 | if incrementing or not opts['every_match'] or fstate[fn]: |
|
961 | if incrementing or not opts['every_match'] or fstate[fn]: | |
911 | pos, neg = display(fn, rev, m, fstate[fn]) |
|
962 | pos, neg = display(fn, rev, m, fstate[fn]) | |
|
963 | count += pos + neg | |||
912 | if pos and not opts['every_match']: |
|
964 | if pos and not opts['every_match']: | |
913 | skip[fn] = True |
|
965 | skip[fn] = True | |
914 | fstate[fn] = m |
|
966 | fstate[fn] = m | |
@@ -920,6 +972,7 def grep(ui, repo, pattern, *pats, **opt | |||||
920 | for fn, state in fstate: |
|
972 | for fn, state in fstate: | |
921 | if fn in skip: continue |
|
973 | if fn in skip: continue | |
922 | display(fn, rev, {}, state) |
|
974 | display(fn, rev, {}, state) | |
|
975 | return (count == 0 and 1) or 0 | |||
923 |
|
976 | |||
924 | def heads(ui, repo, **opts): |
|
977 | def heads(ui, repo, **opts): | |
925 | """show current repository heads""" |
|
978 | """show current repository heads""" | |
@@ -1073,8 +1126,9 def log(ui, repo, *pats, **opts): | |||||
1073 | if not pats and cwd: |
|
1126 | if not pats and cwd: | |
1074 | opts['include'] = [os.path.join(cwd, i) for i in opts['include']] |
|
1127 | opts['include'] = [os.path.join(cwd, i) for i in opts['include']] | |
1075 | opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']] |
|
1128 | opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']] | |
1076 |
|
|
1129 | changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '', | |
1077 |
opts) |
|
1130 | pats, opts) | |
|
1131 | for st, rev, fns in changeiter: | |||
1078 | if st == 'window': |
|
1132 | if st == 'window': | |
1079 | du = dui(ui) |
|
1133 | du = dui(ui) | |
1080 | elif st == 'add': |
|
1134 | elif st == 'add': | |
@@ -1571,14 +1625,15 table = { | |||||
1571 | "hg forget [OPTION]... FILE..."), |
|
1625 | "hg forget [OPTION]... FILE..."), | |
1572 | "grep": |
|
1626 | "grep": | |
1573 | (grep, |
|
1627 | (grep, | |
1574 |
[('0', 'print0', None, 'end fi |
|
1628 | [('0', 'print0', None, 'end fields with NUL'), | |
1575 | ('I', 'include', [], 'include path in search'), |
|
1629 | ('I', 'include', [], 'include path in search'), | |
1576 | ('X', 'exclude', [], 'include path in search'), |
|
1630 | ('X', 'exclude', [], 'include path in search'), | |
1577 |
('e', 'every-match', None, 'print every match |
|
1631 | ('e', 'every-match', None, 'print every rev with matches'), | |
1578 | ('i', 'ignore-case', None, 'ignore case when matching'), |
|
1632 | ('i', 'ignore-case', None, 'ignore case when matching'), | |
1579 | ('l', 'files-with-matches', None, 'print names of files with matches'), |
|
1633 | ('l', 'files-with-matches', None, 'print names of files and revs with matches'), | |
1580 |
('n', 'line-number', |
|
1634 | ('n', 'line-number', None, 'print line numbers'), | |
1581 |
('r', 'rev', [], 'search in revision rev') |
|
1635 | ('r', 'rev', [], 'search in revision rev'), | |
|
1636 | ('u', 'user', None, 'print user who made change')], | |||
1582 | "hg grep [OPTION]... PATTERN [FILE]..."), |
|
1637 | "hg grep [OPTION]... PATTERN [FILE]..."), | |
1583 | "heads": |
|
1638 | "heads": | |
1584 | (heads, |
|
1639 | (heads, |
General Comments 0
You need to be logged in to leave comments.
Login now