/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.40.10 by Parth Malwankar
assigned copyright to canonical
1
# Copyright (C) 2010 Canonical Ltd
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
0.40.147 by Jelmer Vernooij
Fix compatibility with newer versions of bzr: don't use relative imports in lazy imports, and import features from bzrlib.tests.features.
17
from __future__ import absolute_import
18
6621.22.2 by Martin
Use BytesIO or StringIO from bzrlib.sixish
19
import re
0.47.1 by Martin
Implement whole text search for fast failure on no match
20
import sys
21
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
22
from ...lazy_import import lazy_import
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
23
lazy_import(globals(), """
0.40.83 by Parth Malwankar
added support for -F/--fixed-string.
24
from fnmatch import fnmatch
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
25
6667.2.1 by Jelmer Vernooij
Some cleanup; s/BzrDir/ControlDir/, remove some unused imports.
26
from breezy._termcolor import color_string, FG
0.43.4 by Parth Malwankar
initial support for color for fixed string grep.
27
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
28
from breezy.revisionspec import (
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
29
    RevisionSpec,
30
    RevisionSpec_revid,
31
    RevisionSpec_revno,
32
    )
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
33
from breezy import (
6667.2.1 by Jelmer Vernooij
Some cleanup; s/BzrDir/ControlDir/, remove some unused imports.
34
    controldir,
0.48.5 by Parth Malwankar
fixed imports
35
    diff,
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
36
    errors,
37
    lazy_regex,
6621.22.2 by Martin
Use BytesIO or StringIO from bzrlib.sixish
38
    revision as _mod_revision,
39
    )
40
""")
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
41
from breezy import (
0.40.47 by Parth Malwankar
fixes bug #531336. binary files are now skipped.
42
    osutils,
43
    trace,
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
44
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
45
from breezy.sixish import (
6621.22.2 by Martin
Use BytesIO or StringIO from bzrlib.sixish
46
    BytesIO,
47
    )
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
48
0.40.83 by Parth Malwankar
added support for -F/--fixed-string.
49
_user_encoding = osutils.get_user_encoding()
50
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
51
0.40.95 by Parth Malwankar
faster mainline rev grep
52
class _RevisionNotLinear(Exception):
53
    """Raised when a revision is not on left-hand history."""
54
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
55
0.40.95 by Parth Malwankar
faster mainline rev grep
56
def _rev_on_mainline(rev_tuple):
57
    """returns True is rev tuple is on mainline"""
58
    if len(rev_tuple) == 1:
59
        return True
60
    return rev_tuple[1] == 0 and rev_tuple[2] == 0
61
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
62
0.40.100 by Parth Malwankar
removed dependency on log._graph_view_revisions
63
# NOTE: _linear_view_revisions is basided on
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
64
# breezy.log._linear_view_revisions.
0.40.100 by Parth Malwankar
removed dependency on log._graph_view_revisions
65
# This should probably be a common public API
0.40.95 by Parth Malwankar
faster mainline rev grep
66
def _linear_view_revisions(branch, start_rev_id, end_rev_id):
0.40.106 by Parth Malwankar
fixed error in dotted rev reverse search.
67
    # requires that start is older than end
0.40.95 by Parth Malwankar
faster mainline rev grep
68
    repo = branch.repository
6531.3.6 by Jelmer Vernooij
Use iter_lefthand_ancestry rather than removed iter_reverse_revision_history.
69
    graph = repo.get_graph()
6531.3.7 by Jelmer Vernooij
Formatting.
70
    for revision_id in graph.iter_lefthand_ancestry(
71
            end_rev_id, (_mod_revision.NULL_REVISION, )):
0.40.95 by Parth Malwankar
faster mainline rev grep
72
        revno = branch.revision_id_to_dotted_revno(revision_id)
73
        revno_str = '.'.join(str(n) for n in revno)
74
        if revision_id == start_rev_id:
75
            yield revision_id, revno_str, 0
76
            break
77
        yield revision_id, revno_str, 0
78
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
79
0.40.100 by Parth Malwankar
removed dependency on log._graph_view_revisions
80
# NOTE: _graph_view_revisions is copied from
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
81
# breezy.log._graph_view_revisions.
0.40.100 by Parth Malwankar
removed dependency on log._graph_view_revisions
82
# This should probably be a common public API
83
def _graph_view_revisions(branch, start_rev_id, end_rev_id,
84
                          rebase_initial_depths=True):
85
    """Calculate revisions to view including merges, newest to oldest.
86
87
    :param branch: the branch
88
    :param start_rev_id: the lower revision-id
89
    :param end_rev_id: the upper revision-id
90
    :param rebase_initial_depth: should depths be rebased until a mainline
91
      revision is found?
92
    :return: An iterator of (revision_id, dotted_revno, merge_depth) tuples.
93
    """
0.40.106 by Parth Malwankar
fixed error in dotted rev reverse search.
94
    # requires that start is older than end
0.40.100 by Parth Malwankar
removed dependency on log._graph_view_revisions
95
    view_revisions = branch.iter_merge_sorted_revisions(
96
        start_revision_id=end_rev_id, stop_revision_id=start_rev_id,
97
        stop_rule="with-merges")
98
    if not rebase_initial_depths:
99
        for (rev_id, merge_depth, revno, end_of_merge
100
             ) in view_revisions:
101
            yield rev_id, '.'.join(map(str, revno)), merge_depth
102
    else:
103
        # We're following a development line starting at a merged revision.
104
        # We need to adjust depths down by the initial depth until we find
105
        # a depth less than it. Then we use that depth as the adjustment.
106
        # If and when we reach the mainline, depth adjustment ends.
107
        depth_adjustment = None
108
        for (rev_id, merge_depth, revno, end_of_merge
109
             ) in view_revisions:
110
            if depth_adjustment is None:
111
                depth_adjustment = merge_depth
112
            if depth_adjustment:
113
                if merge_depth < depth_adjustment:
114
                    # From now on we reduce the depth adjustement, this can be
115
                    # surprising for users. The alternative requires two passes
116
                    # which breaks the fast display of the first revision
117
                    # though.
118
                    depth_adjustment = merge_depth
119
                merge_depth -= depth_adjustment
120
            yield rev_id, '.'.join(map(str, revno)), merge_depth
121
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
122
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
123
def compile_pattern(pattern, flags=0):
124
    patternc = None
125
    try:
126
        # use python's re.compile as we need to catch re.error in case of bad pattern
127
        lazy_regex.reset_compile()
128
        patternc = re.compile(pattern, flags)
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
129
    except re.error as e:
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
130
        raise errors.BzrError("Invalid pattern: '%s'" % pattern)
131
    return patternc
132
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
133
0.40.86 by Parth Malwankar
the check for implicit fixed_string now allows for spaces.
134
def is_fixed_string(s):
0.40.101 by Parth Malwankar
added underscore to --fixed-string whitelist
135
    if re.match("^([A-Za-z0-9_]|\s)*$", s):
0.40.86 by Parth Malwankar
the check for implicit fixed_string now allows for spaces.
136
        return True
137
    return False
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
138
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
139
0.48.7 by Parth Malwankar
initial outputter support for diff_grep
140
class _GrepDiffOutputter(object):
141
    """Precalculate formatting based on options given for diff grep.
142
    """
6531.3.8 by Jelmer Vernooij
Move color feature into bzrlib.tests.features.
143
0.48.7 by Parth Malwankar
initial outputter support for diff_grep
144
    def __init__(self, opts):
0.48.8 by Parth Malwankar
colored header for diff grep output
145
        self.opts = opts
0.48.7 by Parth Malwankar
initial outputter support for diff_grep
146
        self.outf = opts.outf
147
        if opts.show_color:
148
            pat = opts.pattern.encode(_user_encoding, 'replace')
149
            if opts.fixed_string:
150
                self._old = pat
151
                self._new = color_string(pat, FG.BOLD_RED)
152
                self.get_writer = self._get_writer_fixed_highlighted
153
            else:
154
                flags = opts.patternc.flags
155
                self._sub = re.compile(pat.join(("((?:",")+)")), flags).sub
156
                self._highlight = color_string("\\1", FG.BOLD_RED)
157
                self.get_writer = self._get_writer_regexp_highlighted
158
        else:
159
            self.get_writer = self._get_writer_plain
160
0.48.8 by Parth Malwankar
colored header for diff grep output
161
    def get_file_header_writer(self):
162
        """Get function for writing file headers"""
163
        write = self.outf.write
164
        eol_marker = self.opts.eol_marker
165
        def _line_writer(line):
166
            write(line + eol_marker)
167
        def _line_writer_color(line):
168
            write(FG.BOLD_MAGENTA + line + FG.NONE + eol_marker)
169
        if self.opts.show_color:
170
            return _line_writer_color
171
        else:
172
            return _line_writer
173
        return _line_writer
174
175
    def get_revision_header_writer(self):
176
        """Get function for writing revno lines"""
177
        write = self.outf.write
178
        eol_marker = self.opts.eol_marker
179
        def _line_writer(line):
180
            write(line + eol_marker)
181
        def _line_writer_color(line):
182
            write(FG.BOLD_BLUE + line + FG.NONE + eol_marker)
183
        if self.opts.show_color:
184
            return _line_writer_color
185
        else:
186
            return _line_writer
187
        return _line_writer
188
0.48.7 by Parth Malwankar
initial outputter support for diff_grep
189
    def _get_writer_plain(self):
190
        """Get function for writing uncoloured output"""
191
        write = self.outf.write
0.48.8 by Parth Malwankar
colored header for diff grep output
192
        eol_marker = self.opts.eol_marker
0.48.7 by Parth Malwankar
initial outputter support for diff_grep
193
        def _line_writer(line):
0.48.8 by Parth Malwankar
colored header for diff grep output
194
            write(line + eol_marker)
0.48.7 by Parth Malwankar
initial outputter support for diff_grep
195
        return _line_writer
196
197
    def _get_writer_regexp_highlighted(self):
198
        """Get function for writing output with regexp match highlighted"""
199
        _line_writer = self._get_writer_plain()
200
        sub, highlight = self._sub, self._highlight
201
        def _line_writer_regexp_highlighted(line):
202
            """Write formatted line with matched pattern highlighted"""
203
            return _line_writer(line=sub(highlight, line))
204
        return _line_writer_regexp_highlighted
205
206
    def _get_writer_fixed_highlighted(self):
207
        """Get function for writing output with search string highlighted"""
208
        _line_writer = self._get_writer_plain()
209
        old, new = self._old, self._new
210
        def _line_writer_fixed_highlighted(line):
211
            """Write formatted line with string searched for highlighted"""
212
            return _line_writer(line=line.replace(old, new))
213
        return _line_writer_fixed_highlighted
214
215
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
216
def grep_diff(opts):
217
    wt, branch, relpath = \
6667.2.1 by Jelmer Vernooij
Some cleanup; s/BzrDir/ControlDir/, remove some unused imports.
218
        controldir.ControlDir.open_containing_tree_or_branch('.')
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
219
    branch.lock_read()
220
    try:
0.48.3 by Parth Malwankar
for grep_diff, if rev is not specified, last is used as start.
221
        if opts.revision:
222
            start_rev = opts.revision[0]
223
        else:
0.48.9 by Parth Malwankar
added inital test for 'grep -p'
224
            # if no revision is sepcified for diff grep we grep all changesets.
225
            opts.revision = [RevisionSpec.from_string('revno:1'),
226
                RevisionSpec.from_string('last:1')]
0.48.3 by Parth Malwankar
for grep_diff, if rev is not specified, last is used as start.
227
            start_rev = opts.revision[0]
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
228
        start_revid = start_rev.as_revision_id(branch)
0.48.4 by Parth Malwankar
diff grep now works.
229
        if start_revid == 'null:':
230
            return
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
231
        srevno_tuple = branch.revision_id_to_dotted_revno(start_revid)
232
        if len(opts.revision) == 2:
233
            end_rev = opts.revision[1]
234
            end_revid = end_rev.as_revision_id(branch)
6531.3.8 by Jelmer Vernooij
Move color feature into bzrlib.tests.features.
235
            if end_revid is None:
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
236
                end_revno, end_revid = branch.last_revision_info()
237
            erevno_tuple = branch.revision_id_to_dotted_revno(end_revid)
238
239
            grep_mainline = (_rev_on_mainline(srevno_tuple) and
240
                _rev_on_mainline(erevno_tuple))
241
242
            # ensure that we go in reverse order
243
            if srevno_tuple > erevno_tuple:
244
                srevno_tuple, erevno_tuple = erevno_tuple, srevno_tuple
245
                start_revid, end_revid = end_revid, start_revid
246
247
            # Optimization: Traversing the mainline in reverse order is much
248
            # faster when we don't want to look at merged revs. We try this
249
            # with _linear_view_revisions. If all revs are to be grepped we
250
            # use the slower _graph_view_revisions
251
            if opts.levels==1 and grep_mainline:
252
                given_revs = _linear_view_revisions(branch, start_revid, end_revid)
253
            else:
254
                given_revs = _graph_view_revisions(branch, start_revid, end_revid)
255
        else:
256
            # We do an optimization below. For grepping a specific revison
257
            # We don't need to call _graph_view_revisions which is slow.
258
            # We create the start_rev_tuple for only that specific revision.
259
            # _graph_view_revisions is used only for revision range.
260
            start_revno = '.'.join(map(str, srevno_tuple))
261
            start_rev_tuple = (start_revid, start_revno, 0)
262
            given_revs = [start_rev_tuple]
263
        repo = branch.repository
0.48.4 by Parth Malwankar
diff grep now works.
264
        diff_pattern = re.compile("^[+\-].*(" + opts.pattern + ")")
0.48.11 by Parth Malwankar
unicode decode fix for diff grep.
265
        file_pattern = re.compile("=== (modified|added|removed) file '.*'", re.UNICODE)
0.48.8 by Parth Malwankar
colored header for diff grep output
266
        outputter = _GrepDiffOutputter(opts)
267
        writeline = outputter.get_writer()
268
        writerevno = outputter.get_revision_header_writer()
269
        writefileheader = outputter.get_file_header_writer()
0.48.11 by Parth Malwankar
unicode decode fix for diff grep.
270
        file_encoding = _user_encoding
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
271
        for revid, revno, merge_depth in given_revs:
272
            if opts.levels == 1 and merge_depth != 0:
273
                # with level=1 show only top level
274
                continue
275
276
            rev_spec = RevisionSpec_revid.from_string("revid:"+revid)
277
            new_rev = repo.get_revision(revid)
278
            new_tree = rev_spec.as_tree(branch)
279
            if len(new_rev.parent_ids) == 0:
280
                ancestor_id = _mod_revision.NULL_REVISION
281
            else:
282
                ancestor_id = new_rev.parent_ids[0]
283
            old_tree = repo.revision_tree(ancestor_id)
6621.22.2 by Martin
Use BytesIO or StringIO from bzrlib.sixish
284
            s = BytesIO()
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
285
            diff.show_diff_trees(old_tree, new_tree, s,
286
                old_label='', new_label='')
0.48.4 by Parth Malwankar
diff grep now works.
287
            display_revno = True
288
            display_file = False
289
            file_header = None
290
            text = s.getvalue()
0.48.6 by Parth Malwankar
removed fixed_string condition for diff grep
291
            for line in text.splitlines():
292
                if file_pattern.search(line):
293
                    file_header = line
294
                    display_file = True
0.48.11 by Parth Malwankar
unicode decode fix for diff grep.
295
                elif diff_pattern.search(line):
0.48.6 by Parth Malwankar
removed fixed_string condition for diff grep
296
                    if display_revno:
0.48.10 by Parth Malwankar
more tests for 'grep --diff'
297
                        writerevno("=== revno:%s ===" % (revno,))
0.48.6 by Parth Malwankar
removed fixed_string condition for diff grep
298
                        display_revno = False
299
                    if display_file:
0.48.8 by Parth Malwankar
colored header for diff grep output
300
                        writefileheader("  %s" % (file_header,))
0.48.6 by Parth Malwankar
removed fixed_string condition for diff grep
301
                        display_file = False
0.48.11 by Parth Malwankar
unicode decode fix for diff grep.
302
                    line = line.decode(file_encoding, 'replace')
303
                    writeline("    %s" % (line,))
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
304
    finally:
305
        branch.unlock()
306
307
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
308
def versioned_grep(opts):
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
309
    wt, branch, relpath = \
6667.2.1 by Jelmer Vernooij
Some cleanup; s/BzrDir/ControlDir/, remove some unused imports.
310
        controldir.ControlDir.open_containing_tree_or_branch('.')
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
311
    branch.lock_read()
0.40.88 by Parth Malwankar
updated to avoid relocking.
312
    try:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
313
        start_rev = opts.revision[0]
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
314
        start_revid = start_rev.as_revision_id(branch)
6531.3.8 by Jelmer Vernooij
Move color feature into bzrlib.tests.features.
315
        if start_revid is None:
0.40.95 by Parth Malwankar
faster mainline rev grep
316
            start_rev = RevisionSpec_revno.from_string("revno:1")
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
317
            start_revid = start_rev.as_revision_id(branch)
318
        srevno_tuple = branch.revision_id_to_dotted_revno(start_revid)
0.40.88 by Parth Malwankar
updated to avoid relocking.
319
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
320
        if len(opts.revision) == 2:
321
            end_rev = opts.revision[1]
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
322
            end_revid = end_rev.as_revision_id(branch)
6531.3.8 by Jelmer Vernooij
Move color feature into bzrlib.tests.features.
323
            if end_revid is None:
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
324
                end_revno, end_revid = branch.last_revision_info()
325
            erevno_tuple = branch.revision_id_to_dotted_revno(end_revid)
0.40.95 by Parth Malwankar
faster mainline rev grep
326
0.40.106 by Parth Malwankar
fixed error in dotted rev reverse search.
327
            grep_mainline = (_rev_on_mainline(srevno_tuple) and
328
                _rev_on_mainline(erevno_tuple))
329
330
            # ensure that we go in reverse order
331
            if srevno_tuple > erevno_tuple:
332
                srevno_tuple, erevno_tuple = erevno_tuple, srevno_tuple
333
                start_revid, end_revid = end_revid, start_revid
0.40.97 by Parth Malwankar
fixed caching bug for rev range.
334
0.40.95 by Parth Malwankar
faster mainline rev grep
335
            # Optimization: Traversing the mainline in reverse order is much
336
            # faster when we don't want to look at merged revs. We try this
337
            # with _linear_view_revisions. If all revs are to be grepped we
338
            # use the slower _graph_view_revisions
6531.3.9 by Jelmer Vernooij
Remove broken tests..
339
            if opts.levels == 1 and grep_mainline:
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
340
                given_revs = _linear_view_revisions(branch, start_revid, end_revid)
0.40.95 by Parth Malwankar
faster mainline rev grep
341
            else:
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
342
                given_revs = _graph_view_revisions(branch, start_revid, end_revid)
0.40.88 by Parth Malwankar
updated to avoid relocking.
343
        else:
0.40.94 by Parth Malwankar
code cleanup. moved start_rev_tuple into if cond that uses it.
344
            # We do an optimization below. For grepping a specific revison
345
            # We don't need to call _graph_view_revisions which is slow.
346
            # We create the start_rev_tuple for only that specific revision.
347
            # _graph_view_revisions is used only for revision range.
348
            start_revno = '.'.join(map(str, srevno_tuple))
349
            start_rev_tuple = (start_revid, start_revno, 0)
0.40.88 by Parth Malwankar
updated to avoid relocking.
350
            given_revs = [start_rev_tuple]
351
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
352
        # GZ 2010-06-02: Shouldn't be smuggling this on opts, but easy for now
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
353
        opts.outputter = _Outputter(opts, use_cache=True)
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
354
0.40.88 by Parth Malwankar
updated to avoid relocking.
355
        for revid, revno, merge_depth in given_revs:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
356
            if opts.levels == 1 and merge_depth != 0:
0.40.88 by Parth Malwankar
updated to avoid relocking.
357
                # with level=1 show only top level
358
                continue
359
360
            rev = RevisionSpec_revid.from_string("revid:"+revid)
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
361
            tree = rev.as_tree(branch)
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
362
            for path in opts.path_list:
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
363
                path_for_id = osutils.pathjoin(relpath, path)
364
                id = tree.path2id(path_for_id)
365
                if not id:
0.41.22 by Parth Malwankar
added basic --exclude/include tests
366
                    trace.warning("Skipped unknown file '%s'." % path)
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
367
                    continue
368
369
                if osutils.isdir(path):
370
                    path_prefix = path
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
371
                    dir_grep(tree, path, relpath, opts, revno, path_prefix)
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
372
                else:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
373
                    versioned_file_grep(tree, id, '.', path, opts, revno)
0.40.88 by Parth Malwankar
updated to avoid relocking.
374
    finally:
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
375
        branch.unlock()
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
376
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
377
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
378
def workingtree_grep(opts):
379
    revno = opts.print_revno = None # for working tree set revno to None
0.40.69 by Parth Malwankar
reduced lock/unlock
380
381
    tree, branch, relpath = \
6667.2.1 by Jelmer Vernooij
Some cleanup; s/BzrDir/ControlDir/, remove some unused imports.
382
        controldir.ControlDir.open_containing_tree_or_branch('.')
0.40.130 by Parth Malwankar
grep in a branch with no tree does not throw stack trace (#572658)
383
    if not tree:
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
384
        msg = ('Cannot search working tree. Working tree not found.\n'
385
            'To search for specific revision in history use the -r option.')
0.40.130 by Parth Malwankar
grep in a branch with no tree does not throw stack trace (#572658)
386
        raise errors.BzrCommandError(msg)
387
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
388
    # GZ 2010-06-02: Shouldn't be smuggling this on opts, but easy for now
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
389
    opts.outputter = _Outputter(opts)
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
390
0.40.69 by Parth Malwankar
reduced lock/unlock
391
    tree.lock_read()
392
    try:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
393
        for path in opts.path_list:
0.40.69 by Parth Malwankar
reduced lock/unlock
394
            if osutils.isdir(path):
395
                path_prefix = path
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
396
                dir_grep(tree, path, relpath, opts, revno, path_prefix)
0.40.69 by Parth Malwankar
reduced lock/unlock
397
            else:
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
398
                _file_grep(open(path).read(), path, opts, revno)
0.40.69 by Parth Malwankar
reduced lock/unlock
399
    finally:
400
        tree.unlock()
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
401
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
402
0.40.74 by Parth Malwankar
optimization. --include/exclude are checked before reading the file.
403
def _skip_file(include, exclude, path):
404
    if include and not _path_in_glob_list(path, include):
405
        return True
406
    if exclude and _path_in_glob_list(path, exclude):
407
        return True
408
    return False
409
410
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
411
def dir_grep(tree, path, relpath, opts, revno, path_prefix):
0.40.60 by Parth Malwankar
'binary file skipped' warning is only shown with --verbose flag
412
    # setup relpath to open files relative to cwd
413
    rpath = relpath
414
    if relpath:
415
        rpath = osutils.pathjoin('..',relpath)
416
417
    from_dir = osutils.pathjoin(relpath, path)
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
418
    if opts.from_root:
0.40.60 by Parth Malwankar
'binary file skipped' warning is only shown with --verbose flag
419
        # start searching recursively from root
420
        from_dir=None
421
        recursive=True
422
0.40.85 by Parth Malwankar
optimized versioned grep to use iter_files_bytes.
423
    to_grep = []
0.40.92 by Parth Malwankar
performance tweaks to core cached result print loop.
424
    to_grep_append = to_grep.append
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
425
    # GZ 2010-06-05: The cache dict used to be recycled every call to dir_grep
426
    #                and hits manually refilled. Could do this again if it was
427
    #                for a good reason, otherwise cache might want purging.
428
    outputter = opts.outputter
0.40.69 by Parth Malwankar
reduced lock/unlock
429
    for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
430
        from_dir=from_dir, recursive=opts.recursive):
0.40.69 by Parth Malwankar
reduced lock/unlock
431
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
432
        if _skip_file(opts.include, opts.exclude, fp):
0.40.74 by Parth Malwankar
optimization. --include/exclude are checked before reading the file.
433
            continue
434
0.40.69 by Parth Malwankar
reduced lock/unlock
435
        if fc == 'V' and fkind == 'file':
436
            if revno != None:
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
437
                # If old result is valid, print results immediately.
438
                # Otherwise, add file info to to_grep so that the
439
                # loop later will get chunks and grep them
6531.3.5 by Jelmer Vernooij
Use Tree.get_file_revision.
440
                cache_id = tree.get_file_revision(fid)
0.46.11 by Martin
Add method to outputter for writing cached lines
441
                if cache_id in outputter.cache:
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
442
                    # GZ 2010-06-05: Not really sure caching and re-outputting
443
                    #                the old path is really the right thing,
444
                    #                but it's what the old code seemed to do
0.46.11 by Martin
Add method to outputter for writing cached lines
445
                    outputter.write_cached_lines(cache_id, revno)
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
446
                else:
0.40.92 by Parth Malwankar
performance tweaks to core cached result print loop.
447
                    to_grep_append((fid, (fp, fid)))
0.40.69 by Parth Malwankar
reduced lock/unlock
448
            else:
449
                # we are grepping working tree.
6531.3.8 by Jelmer Vernooij
Move color feature into bzrlib.tests.features.
450
                if from_dir is None:
0.40.69 by Parth Malwankar
reduced lock/unlock
451
                    from_dir = '.'
452
453
                path_for_file = osutils.pathjoin(tree.basedir, from_dir, fp)
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
454
                if opts.files_with_matches or opts.files_without_match:
0.40.116 by Parth Malwankar
optimization for wtree list-only grep to avoid full file read.
455
                    # Optimize for wtree list-only as we don't need to read the
456
                    # entire file
0.46.20 by Martin
Remove some unneeded imports
457
                    file = open(path_for_file, 'r', buffering=4096)
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
458
                    _file_grep_list_only_wtree(file, fp, opts, path_prefix)
0.40.121 by Parth Malwankar
initial implementation of -L/--files-without-matches. no tests.
459
                else:
0.46.20 by Martin
Remove some unneeded imports
460
                    file_text = open(path_for_file, 'r').read()
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
461
                    _file_grep(file_text, fp, opts, revno, path_prefix)
0.40.43 by Parth Malwankar
moved cmd_grep._grep_dir to grep.dir_grep
462
0.40.85 by Parth Malwankar
optimized versioned grep to use iter_files_bytes.
463
    if revno != None: # grep versioned files
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
464
        for (path, fid), chunks in tree.iter_files_bytes(to_grep):
0.40.85 by Parth Malwankar
optimized versioned grep to use iter_files_bytes.
465
            path = _make_display_path(relpath, path)
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
466
            _file_grep(chunks[0], path, opts, revno, path_prefix,
6531.3.5 by Jelmer Vernooij
Use Tree.get_file_revision.
467
                tree.get_file_revision(fid, path))
0.40.43 by Parth Malwankar
moved cmd_grep._grep_dir to grep.dir_grep
468
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
469
0.41.8 by Parth Malwankar
code cleanup.
470
def _make_display_path(relpath, path):
471
    """Return path string relative to user cwd.
0.40.42 by Parth Malwankar
fix to make grep paths relative to cwd
472
0.41.8 by Parth Malwankar
code cleanup.
473
    Take tree's 'relpath' and user supplied 'path', and return path
474
    that can be displayed to the user.
475
    """
0.40.15 by Parth Malwankar
some fixes and test updates
476
    if relpath:
0.40.52 by Parth Malwankar
code cleanup and documentation
477
        # update path so to display it w.r.t cwd
478
        # handle windows slash separator
0.40.20 by Parth Malwankar
used path functions from bzrlib.osutils
479
        path = osutils.normpath(osutils.pathjoin(relpath, path))
0.40.22 by Parth Malwankar
fixed display path formatting on windows
480
        path = path.replace('\\', '/')
481
        path = path.replace(relpath + '/', '', 1)
0.41.8 by Parth Malwankar
code cleanup.
482
    return path
483
484
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
485
def versioned_file_grep(tree, id, relpath, path, opts, revno, path_prefix = None):
0.41.10 by Parth Malwankar
code cleanup. added comments. path adjustment is now done
486
    """Create a file object for the specified id and pass it on to _file_grep.
487
    """
488
489
    path = _make_display_path(relpath, path)
0.41.12 by Parth Malwankar
initial support for working tree grep (no test cases yet!)
490
    file_text = tree.get_file_text(id)
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
491
    _file_grep(file_text, path, opts, revno, path_prefix)
0.41.21 by Parth Malwankar
include/exclude working now. tests not added.
492
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
493
0.41.21 by Parth Malwankar
include/exclude working now. tests not added.
494
def _path_in_glob_list(path, glob_list):
495
    for glob in glob_list:
496
        if fnmatch(path, glob):
0.46.19 by Martin
Minor pokes, fixes a bug with working tree optimisation and binary files
497
            return True
498
    return False
0.41.12 by Parth Malwankar
initial support for working tree grep (no test cases yet!)
499
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
500
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
501
def _file_grep_list_only_wtree(file, path, opts, path_prefix=None):
0.40.116 by Parth Malwankar
optimization for wtree list-only grep to avoid full file read.
502
    # test and skip binary files
503
    if '\x00' in file.read(1024):
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
504
        if opts.verbose:
0.40.116 by Parth Malwankar
optimization for wtree list-only grep to avoid full file read.
505
            trace.warning("Binary file '%s' skipped." % path)
0.46.19 by Martin
Minor pokes, fixes a bug with working tree optimisation and binary files
506
        return
0.40.118 by Parth Malwankar
further optimization of _file_grep_list_only_wtree.
507
508
    file.seek(0) # search from beginning
509
510
    found = False
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
511
    if opts.fixed_string:
512
        pattern = opts.pattern.encode(_user_encoding, 'replace')
0.46.1 by Martin
Make -Fi use regexps for re.IGNORECASE rather than double str.lower
513
        for line in file:
514
            if pattern in line:
515
                found = True
516
                break
0.40.121 by Parth Malwankar
initial implementation of -L/--files-without-matches. no tests.
517
    else: # not fixed_string
0.40.116 by Parth Malwankar
optimization for wtree list-only grep to avoid full file read.
518
        for line in file:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
519
            if opts.patternc.search(line):
0.40.118 by Parth Malwankar
further optimization of _file_grep_list_only_wtree.
520
                found = True
0.40.116 by Parth Malwankar
optimization for wtree list-only grep to avoid full file read.
521
                break
522
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
523
    if (opts.files_with_matches and found) or \
524
        (opts.files_without_match and not found):
0.40.118 by Parth Malwankar
further optimization of _file_grep_list_only_wtree.
525
        if path_prefix and path_prefix != '.':
526
            # user has passed a dir arg, show that as result prefix
527
            path = osutils.pathjoin(path_prefix, path)
0.46.18 by Martin
Fix another, previously existing issue with colour and match-only
528
        opts.outputter.get_writer(path, None, None)()
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
529
530
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
531
class _Outputter(object):
532
    """Precalculate formatting based on options given
533
534
    The idea here is to do this work only once per run, and finally return a
535
    function that will do the minimum amount possible for each match.
0.46.3 by Martin
Start moving formatting setup out of _file_grep, only for files_with_matches so far
536
    """
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
537
    def __init__(self, opts, use_cache=False):
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
538
        self.outf = opts.outf
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
539
        if use_cache:
540
            # self.cache is used to cache results for dir grep based on fid.
541
            # If the fid is does not change between results, it means that
542
            # the result will be the same apart from revno. In such a case
543
            # we avoid getting file chunks from repo and grepping. The result
544
            # is just printed by replacing old revno with new one.
545
            self.cache = {}
546
        else:
547
            self.cache = None
0.46.17 by Martin
Fix previously untested issue with colour and match-only, and test a related issue
548
        no_line = opts.files_with_matches or opts.files_without_match
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
549
550
        if opts.show_color:
551
            pat = opts.pattern.encode(_user_encoding, 'replace')
0.46.17 by Martin
Fix previously untested issue with colour and match-only, and test a related issue
552
            if no_line:
553
                self.get_writer = self._get_writer_plain
554
            elif opts.fixed_string:
555
                self._old = pat
556
                self._new = color_string(pat, FG.BOLD_RED)
557
                self.get_writer = self._get_writer_fixed_highlighted
558
            else:
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
559
                flags = opts.patternc.flags
560
                self._sub = re.compile(pat.join(("((?:",")+)")), flags).sub
561
                self._highlight = color_string("\\1", FG.BOLD_RED)
562
                self.get_writer = self._get_writer_regexp_highlighted
563
            path_start = FG.MAGENTA
0.46.17 by Martin
Fix previously untested issue with colour and match-only, and test a related issue
564
            path_end = FG.NONE
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
565
            sep = color_string(':', FG.BOLD_CYAN)
566
            rev_sep = color_string('~', FG.BOLD_YELLOW)
567
        else:
568
            self.get_writer = self._get_writer_plain
0.46.17 by Martin
Fix previously untested issue with colour and match-only, and test a related issue
569
            path_start = path_end = ""
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
570
            sep = ":"
571
            rev_sep = "~"
572
573
        parts = [path_start, "%(path)s"]
0.46.3 by Martin
Start moving formatting setup out of _file_grep, only for files_with_matches so far
574
        if opts.print_revno:
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
575
            parts.extend([rev_sep, "%(revno)s"])
0.46.13 by Martin
Split format string into two parts for non-cached operations too
576
        self._format_initial = "".join(parts)
577
        parts = []
0.46.17 by Martin
Fix previously untested issue with colour and match-only, and test a related issue
578
        if no_line:
579
            if not opts.print_revno:
580
                parts.append(path_end)
581
        else:
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
582
            if opts.line_number:
0.46.13 by Martin
Split format string into two parts for non-cached operations too
583
                parts.extend([sep, "%(lineno)s"])
584
            parts.extend([sep, "%(line)s"])
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
585
        parts.append(opts.eol_marker)
0.46.19 by Martin
Minor pokes, fixes a bug with working tree optimisation and binary files
586
        self._format_perline = "".join(parts)
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
587
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
588
    def _get_writer_plain(self, path, revno, cache_id):
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
589
        """Get function for writing uncoloured output"""
0.46.13 by Martin
Split format string into two parts for non-cached operations too
590
        per_line = self._format_perline
591
        start = self._format_initial % {"path":path, "revno":revno}
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
592
        write = self.outf.write
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
593
        if self.cache is not None and cache_id is not None:
594
            result_list = []
595
            self.cache[cache_id] = path, result_list
596
            add_to_cache = result_list.append
597
            def _line_cache_and_writer(**kwargs):
598
                """Write formatted line and cache arguments"""
0.46.12 by Martin
Split format string for cache to only store a string, not a dict
599
                end = per_line % kwargs
600
                add_to_cache(end)
601
                write(start + end)
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
602
            return _line_cache_and_writer
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
603
        def _line_writer(**kwargs):
604
            """Write formatted line from arguments given by underlying opts"""
0.46.13 by Martin
Split format string into two parts for non-cached operations too
605
            write(start + per_line % kwargs)
0.46.8 by Martin
Move pattern highlighting out of _file_grep and into the line writing code
606
        return _line_writer
607
0.46.11 by Martin
Add method to outputter for writing cached lines
608
    def write_cached_lines(self, cache_id, revno):
609
        """Write cached results out again for new revision"""
610
        cached_path, cached_matches = self.cache[cache_id]
0.46.12 by Martin
Split format string for cache to only store a string, not a dict
611
        start = self._format_initial % {"path":cached_path, "revno":revno}
0.46.11 by Martin
Add method to outputter for writing cached lines
612
        write = self.outf.write
0.46.12 by Martin
Split format string for cache to only store a string, not a dict
613
        for end in cached_matches:
614
            write(start + end)
0.46.11 by Martin
Add method to outputter for writing cached lines
615
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
616
    def _get_writer_regexp_highlighted(self, path, revno, cache_id):
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
617
        """Get function for writing output with regexp match highlighted"""
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
618
        _line_writer = self._get_writer_plain(path, revno, cache_id)
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
619
        sub, highlight = self._sub, self._highlight
0.46.8 by Martin
Move pattern highlighting out of _file_grep and into the line writing code
620
        def _line_writer_regexp_highlighted(line, **kwargs):
621
            """Write formatted line with matched pattern highlighted"""
622
            return _line_writer(line=sub(highlight, line), **kwargs)
623
        return _line_writer_regexp_highlighted
624
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
625
    def _get_writer_fixed_highlighted(self, path, revno, cache_id):
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
626
        """Get function for writing output with search string highlighted"""
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
627
        _line_writer = self._get_writer_plain(path, revno, cache_id)
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
628
        old, new = self._old, self._new
629
        def _line_writer_fixed_highlighted(line, **kwargs):
630
            """Write formatted line with string searched for highlighted"""
631
            return _line_writer(line=line.replace(old, new), **kwargs)
632
        return _line_writer_fixed_highlighted
0.46.3 by Martin
Start moving formatting setup out of _file_grep, only for files_with_matches so far
633
634
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
635
def _file_grep(file_text, path, opts, revno, path_prefix=None, cache_id=None):
0.41.9 by Parth Malwankar
refactored code towards support for working tree grep.
636
    # test and skip binary files
0.40.62 by Parth Malwankar
performance optimization
637
    if '\x00' in file_text[:1024]:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
638
        if opts.verbose:
0.40.60 by Parth Malwankar
'binary file skipped' warning is only shown with --verbose flag
639
            trace.warning("Binary file '%s' skipped." % path)
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
640
        return
0.41.9 by Parth Malwankar
refactored code towards support for working tree grep.
641
0.40.52 by Parth Malwankar
code cleanup and documentation
642
    if path_prefix and path_prefix != '.':
643
        # user has passed a dir arg, show that as result prefix
644
        path = osutils.pathjoin(path_prefix, path)
645
0.46.21 by Martin
Fix and test bytes/unicode issue but there's more to do in this area
646
    # GZ 2010-06-07: There's no actual guarentee the file contents will be in
647
    #                the user encoding, but we have to guess something and it
648
    #                is a reasonable default without a better mechanism.
649
    file_encoding = _user_encoding
0.46.19 by Martin
Minor pokes, fixes a bug with working tree optimisation and binary files
650
    pattern = opts.pattern.encode(_user_encoding, 'replace')
0.43.8 by Parth Malwankar
added color for regex pattern.
651
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
652
    writeline = opts.outputter.get_writer(path, revno, cache_id)
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
653
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
654
    if opts.files_with_matches or opts.files_without_match:
0.46.2 by Martin
Remove redundant code on files_with_matches path in _file_grep
655
        if opts.fixed_string:
6691.1.5 by Jelmer Vernooij
Drop support for Python <= 2.5.
656
            found = pattern in file_text
0.40.112 by Parth Malwankar
support for -l, --files-with-matches. no tests yet.
657
        else:
0.46.16 by Martin
Save an attribute lookup on regexp object in inner loops
658
            search = opts.patternc.search
0.47.4 by Martin
Scale back no-match fast path to avoid some behaviour changes with line endings
659
            if "$" not in pattern:
0.47.2 by Martin
Use whole text search for match only cases where possible as well
660
                found = search(file_text) is not None
661
            else:
662
                for line in file_text.splitlines():
663
                    if search(line):
664
                        found = True
665
                        break
666
                else:
667
                    found = False
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
668
        if (opts.files_with_matches and found) or \
669
                (opts.files_without_match and not found):
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
670
            writeline()
0.46.19 by Martin
Minor pokes, fixes a bug with working tree optimisation and binary files
671
    elif opts.fixed_string:
0.47.1 by Martin
Implement whole text search for fast failure on no match
672
        # Fast path for no match, search through the entire file at once rather
6619.3.25 by Jelmer Vernooij
Drop some old dependency checks.
673
        # than a line at a time. <http://effbot.org/zone/stringlib.htm>
674
        i = file_text.find(pattern)
675
        if i == -1:
676
            return
677
        b = file_text.rfind("\n", 0, i) + 1
678
        if opts.line_number:
679
            start = file_text.count("\n", 0, b) + 1
680
        file_text = file_text[b:]
0.46.15 by Martin
Swap fixed_string/line_number branches in _file_grep
681
        if opts.line_number:
0.46.5 by Martin
Delete now redundant duplicated loops in _file_grep
682
            for index, line in enumerate(file_text.splitlines()):
683
                if pattern in line:
0.40.137 by Parth Malwankar
(Martin [gz]) Add seperate output formatter
684
                    line = line.decode(file_encoding, 'replace')
0.47.1 by Martin
Implement whole text search for fast failure on no match
685
                    writeline(lineno=index+start, line=line)
0.46.5 by Martin
Delete now redundant duplicated loops in _file_grep
686
        else:
0.46.15 by Martin
Swap fixed_string/line_number branches in _file_grep
687
            for line in file_text.splitlines():
688
                if pattern in line:
0.40.137 by Parth Malwankar
(Martin [gz]) Add seperate output formatter
689
                    line = line.decode(file_encoding, 'replace')
0.46.15 by Martin
Swap fixed_string/line_number branches in _file_grep
690
                    writeline(line=line)
0.40.63 by Parth Malwankar
performance: moved conditionals out of core loop.
691
    else:
0.47.1 by Martin
Implement whole text search for fast failure on no match
692
        # Fast path on no match, the re module avoids bad behaviour in most
693
        # standard cases, but perhaps could try and detect backtracking
694
        # patterns here and avoid whole text search in those cases
0.46.16 by Martin
Save an attribute lookup on regexp object in inner loops
695
        search = opts.patternc.search
0.47.4 by Martin
Scale back no-match fast path to avoid some behaviour changes with line endings
696
        if "$" not in pattern:
0.47.1 by Martin
Implement whole text search for fast failure on no match
697
            # GZ 2010-06-05: Grr, re.MULTILINE can't save us when searching
698
            #                through revisions as bazaar returns binary mode
699
            #                and trailing \r breaks $ as line ending match
700
            m = search(file_text)
701
            if m is None:
702
                return
703
            b = file_text.rfind("\n", 0, m.start()) + 1
704
            if opts.line_number:
0.47.4 by Martin
Scale back no-match fast path to avoid some behaviour changes with line endings
705
                start = file_text.count("\n", 0, b) + 1
706
            file_text = file_text[b:]
0.47.3 by Martin
Fix previously untested bug with regexp and line numbers introduced by optimisation
707
        else:
708
            start = 1
0.46.15 by Martin
Swap fixed_string/line_number branches in _file_grep
709
        if opts.line_number:
710
            for index, line in enumerate(file_text.splitlines()):
0.46.16 by Martin
Save an attribute lookup on regexp object in inner loops
711
                if search(line):
0.40.137 by Parth Malwankar
(Martin [gz]) Add seperate output formatter
712
                    line = line.decode(file_encoding, 'replace')
0.47.3 by Martin
Fix previously untested bug with regexp and line numbers introduced by optimisation
713
                    writeline(lineno=index+start, line=line)
0.40.83 by Parth Malwankar
added support for -F/--fixed-string.
714
        else:
715
            for line in file_text.splitlines():
0.46.16 by Martin
Save an attribute lookup on regexp object in inner loops
716
                if search(line):
0.40.137 by Parth Malwankar
(Martin [gz]) Add seperate output formatter
717
                    line = line.decode(file_encoding, 'replace')
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
718
                    writeline(line=line)
0.40.139 by Parth Malwankar
(Martin [gz]) Added fast path for no match that avoids splitting the
719