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