/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
17
from bzrlib.lazy_import import lazy_import
18
lazy_import(globals(), """
0.40.83 by Parth Malwankar
added support for -F/--fixed-string.
19
import codecs
20
import cStringIO
21
from fnmatch import fnmatch
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
22
import os
23
import re
0.40.119 by Parth Malwankar
optimization: ignore case is not checked for every line.
24
import string
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
25
0.43.8 by Parth Malwankar
added color for regex pattern.
26
from termcolor import color_string, re_color_string, FG
0.43.4 by Parth Malwankar
initial support for color for fixed string grep.
27
28
0.41.12 by Parth Malwankar
initial support for working tree grep (no test cases yet!)
29
from bzrlib import bzrdir
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
30
from bzrlib.workingtree import WorkingTree
0.40.95 by Parth Malwankar
faster mainline rev grep
31
from bzrlib.revisionspec import RevisionSpec, RevisionSpec_revid, RevisionSpec_revno
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
32
from bzrlib import (
33
    errors,
34
    lazy_regex,
0.40.47 by Parth Malwankar
fixes bug #531336. binary files are now skipped.
35
    osutils,
36
    textfile,
37
    trace,
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
38
    )
39
""")
40
0.40.83 by Parth Malwankar
added support for -F/--fixed-string.
41
_terminal_encoding = osutils.get_terminal_encoding()
42
_user_encoding = osutils.get_user_encoding()
43
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
44
0.40.95 by Parth Malwankar
faster mainline rev grep
45
class _RevisionNotLinear(Exception):
46
    """Raised when a revision is not on left-hand history."""
47
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
48
0.40.95 by Parth Malwankar
faster mainline rev grep
49
def _rev_on_mainline(rev_tuple):
50
    """returns True is rev tuple is on mainline"""
51
    if len(rev_tuple) == 1:
52
        return True
53
    return rev_tuple[1] == 0 and rev_tuple[2] == 0
54
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
55
0.40.100 by Parth Malwankar
removed dependency on log._graph_view_revisions
56
# NOTE: _linear_view_revisions is basided on
57
# bzrlib.log._linear_view_revisions.
58
# This should probably be a common public API
0.40.95 by Parth Malwankar
faster mainline rev grep
59
def _linear_view_revisions(branch, start_rev_id, end_rev_id):
0.40.106 by Parth Malwankar
fixed error in dotted rev reverse search.
60
    # requires that start is older than end
0.40.95 by Parth Malwankar
faster mainline rev grep
61
    repo = branch.repository
62
    for revision_id in repo.iter_reverse_revision_history(end_rev_id):
63
        revno = branch.revision_id_to_dotted_revno(revision_id)
64
        revno_str = '.'.join(str(n) for n in revno)
65
        if revision_id == start_rev_id:
66
            yield revision_id, revno_str, 0
67
            break
68
        yield revision_id, revno_str, 0
69
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
70
0.40.100 by Parth Malwankar
removed dependency on log._graph_view_revisions
71
# NOTE: _graph_view_revisions is copied from
72
# bzrlib.log._graph_view_revisions.
73
# This should probably be a common public API
74
def _graph_view_revisions(branch, start_rev_id, end_rev_id,
75
                          rebase_initial_depths=True):
76
    """Calculate revisions to view including merges, newest to oldest.
77
78
    :param branch: the branch
79
    :param start_rev_id: the lower revision-id
80
    :param end_rev_id: the upper revision-id
81
    :param rebase_initial_depth: should depths be rebased until a mainline
82
      revision is found?
83
    :return: An iterator of (revision_id, dotted_revno, merge_depth) tuples.
84
    """
0.40.106 by Parth Malwankar
fixed error in dotted rev reverse search.
85
    # requires that start is older than end
0.40.100 by Parth Malwankar
removed dependency on log._graph_view_revisions
86
    view_revisions = branch.iter_merge_sorted_revisions(
87
        start_revision_id=end_rev_id, stop_revision_id=start_rev_id,
88
        stop_rule="with-merges")
89
    if not rebase_initial_depths:
90
        for (rev_id, merge_depth, revno, end_of_merge
91
             ) in view_revisions:
92
            yield rev_id, '.'.join(map(str, revno)), merge_depth
93
    else:
94
        # We're following a development line starting at a merged revision.
95
        # We need to adjust depths down by the initial depth until we find
96
        # a depth less than it. Then we use that depth as the adjustment.
97
        # If and when we reach the mainline, depth adjustment ends.
98
        depth_adjustment = None
99
        for (rev_id, merge_depth, revno, end_of_merge
100
             ) in view_revisions:
101
            if depth_adjustment is None:
102
                depth_adjustment = merge_depth
103
            if depth_adjustment:
104
                if merge_depth < depth_adjustment:
105
                    # From now on we reduce the depth adjustement, this can be
106
                    # surprising for users. The alternative requires two passes
107
                    # which breaks the fast display of the first revision
108
                    # though.
109
                    depth_adjustment = merge_depth
110
                merge_depth -= depth_adjustment
111
            yield rev_id, '.'.join(map(str, revno)), merge_depth
112
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
113
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
114
def compile_pattern(pattern, flags=0):
115
    patternc = None
116
    try:
117
        # use python's re.compile as we need to catch re.error in case of bad pattern
118
        lazy_regex.reset_compile()
119
        patternc = re.compile(pattern, flags)
120
    except re.error, e:
121
        raise errors.BzrError("Invalid pattern: '%s'" % pattern)
122
    return patternc
123
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
124
0.40.86 by Parth Malwankar
the check for implicit fixed_string now allows for spaces.
125
def is_fixed_string(s):
0.40.101 by Parth Malwankar
added underscore to --fixed-string whitelist
126
    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.
127
        return True
128
    return False
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
129
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
130
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
131
def versioned_grep(opts):
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
132
    wt, branch, relpath = \
133
        bzrdir.BzrDir.open_containing_tree_or_branch('.')
134
    branch.lock_read()
0.40.88 by Parth Malwankar
updated to avoid relocking.
135
    try:
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
136
        # res_cache is used to cache results for dir grep based on fid.
137
        # If the fid is does not change between results, it means that
138
        # the result will be the same apart from revno. In such a case
139
        # we avoid getting file chunks from repo and grepping. The result
140
        # is just printed by replacing old revno with new one.
141
        res_cache = {}
0.40.88 by Parth Malwankar
updated to avoid relocking.
142
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
143
        start_rev = opts.revision[0]
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
144
        start_revid = start_rev.as_revision_id(branch)
0.40.95 by Parth Malwankar
faster mainline rev grep
145
        if start_revid == None:
146
            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.
147
            start_revid = start_rev.as_revision_id(branch)
148
        srevno_tuple = branch.revision_id_to_dotted_revno(start_revid)
0.40.88 by Parth Malwankar
updated to avoid relocking.
149
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
150
        if len(opts.revision) == 2:
151
            end_rev = opts.revision[1]
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
152
            end_revid = end_rev.as_revision_id(branch)
0.40.95 by Parth Malwankar
faster mainline rev grep
153
            if end_revid == None:
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
154
                end_revno, end_revid = branch.last_revision_info()
155
            erevno_tuple = branch.revision_id_to_dotted_revno(end_revid)
0.40.95 by Parth Malwankar
faster mainline rev grep
156
0.40.106 by Parth Malwankar
fixed error in dotted rev reverse search.
157
            grep_mainline = (_rev_on_mainline(srevno_tuple) and
158
                _rev_on_mainline(erevno_tuple))
159
160
            # ensure that we go in reverse order
161
            if srevno_tuple > erevno_tuple:
162
                srevno_tuple, erevno_tuple = erevno_tuple, srevno_tuple
163
                start_revid, end_revid = end_revid, start_revid
0.40.97 by Parth Malwankar
fixed caching bug for rev range.
164
0.40.95 by Parth Malwankar
faster mainline rev grep
165
            # Optimization: Traversing the mainline in reverse order is much
166
            # faster when we don't want to look at merged revs. We try this
167
            # with _linear_view_revisions. If all revs are to be grepped we
168
            # use the slower _graph_view_revisions
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
169
            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.
170
                given_revs = _linear_view_revisions(branch, start_revid, end_revid)
0.40.95 by Parth Malwankar
faster mainline rev grep
171
            else:
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
172
                given_revs = _graph_view_revisions(branch, start_revid, end_revid)
0.40.88 by Parth Malwankar
updated to avoid relocking.
173
        else:
0.40.94 by Parth Malwankar
code cleanup. moved start_rev_tuple into if cond that uses it.
174
            # We do an optimization below. For grepping a specific revison
175
            # We don't need to call _graph_view_revisions which is slow.
176
            # We create the start_rev_tuple for only that specific revision.
177
            # _graph_view_revisions is used only for revision range.
178
            start_revno = '.'.join(map(str, srevno_tuple))
179
            start_rev_tuple = (start_revid, start_revno, 0)
0.40.88 by Parth Malwankar
updated to avoid relocking.
180
            given_revs = [start_rev_tuple]
181
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
182
        # GZ 2010-06-02: Shouldn't be smuggling this on opts, but easy for now
183
        opts.line_writer = staticmethod(_make_line_writer(opts))
184
0.40.88 by Parth Malwankar
updated to avoid relocking.
185
        for revid, revno, merge_depth in given_revs:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
186
            if opts.levels == 1 and merge_depth != 0:
0.40.88 by Parth Malwankar
updated to avoid relocking.
187
                # with level=1 show only top level
188
                continue
189
190
            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.
191
            tree = rev.as_tree(branch)
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
192
            for path in opts.path_list:
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
193
                path_for_id = osutils.pathjoin(relpath, path)
194
                id = tree.path2id(path_for_id)
195
                if not id:
0.41.22 by Parth Malwankar
added basic --exclude/include tests
196
                    trace.warning("Skipped unknown file '%s'." % path)
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
197
                    continue
198
199
                if osutils.isdir(path):
200
                    path_prefix = path
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
201
                    res_cache = dir_grep(tree, path, relpath, opts,
202
                        revno, path_prefix, res_cache)
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
203
                else:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
204
                    versioned_file_grep(tree, id, '.', path, opts, revno)
0.40.88 by Parth Malwankar
updated to avoid relocking.
205
    finally:
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
206
        branch.unlock()
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
207
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
208
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
209
def workingtree_grep(opts):
210
    revno = opts.print_revno = None # for working tree set revno to None
0.40.69 by Parth Malwankar
reduced lock/unlock
211
212
    tree, branch, relpath = \
213
        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)
214
    if not tree:
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
215
        msg = ('Cannot search working tree. Working tree not found.\n'
216
            '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)
217
        raise errors.BzrCommandError(msg)
218
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
219
    # GZ 2010-06-02: Shouldn't be smuggling this on opts, but easy for now
220
    opts.line_writer = staticmethod(_make_line_writer(opts))
221
0.40.69 by Parth Malwankar
reduced lock/unlock
222
    tree.lock_read()
223
    try:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
224
        for path in opts.path_list:
0.40.69 by Parth Malwankar
reduced lock/unlock
225
            if osutils.isdir(path):
226
                path_prefix = path
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
227
                dir_grep(tree, path, relpath, opts, revno, path_prefix)
0.40.69 by Parth Malwankar
reduced lock/unlock
228
            else:
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
229
                _file_grep(open(path).read(), path, opts, revno)
0.40.69 by Parth Malwankar
reduced lock/unlock
230
    finally:
231
        tree.unlock()
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
232
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
233
0.40.74 by Parth Malwankar
optimization. --include/exclude are checked before reading the file.
234
def _skip_file(include, exclude, path):
235
    if include and not _path_in_glob_list(path, include):
236
        return True
237
    if exclude and _path_in_glob_list(path, exclude):
238
        return True
239
    return False
240
241
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
242
def dir_grep(tree, path, relpath, opts, revno, path_prefix, res_cache={}):
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
243
    _revno_pattern = re.compile("\~[0-9.]+:")
0.40.115 by Parth Malwankar
added test for versioned grep.
244
    _revno_pattern_list_only = re.compile("\~[0-9.]+")
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
245
    dir_res = {}
246
0.40.60 by Parth Malwankar
'binary file skipped' warning is only shown with --verbose flag
247
    # setup relpath to open files relative to cwd
248
    rpath = relpath
249
    if relpath:
250
        rpath = osutils.pathjoin('..',relpath)
251
252
    from_dir = osutils.pathjoin(relpath, path)
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
253
    if opts.from_root:
0.40.60 by Parth Malwankar
'binary file skipped' warning is only shown with --verbose flag
254
        # start searching recursively from root
255
        from_dir=None
256
        recursive=True
257
0.40.85 by Parth Malwankar
optimized versioned grep to use iter_files_bytes.
258
    to_grep = []
0.40.92 by Parth Malwankar
performance tweaks to core cached result print loop.
259
    to_grep_append = to_grep.append
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
260
    outf_write = opts.outf.write
0.40.69 by Parth Malwankar
reduced lock/unlock
261
    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
262
        from_dir=from_dir, recursive=opts.recursive):
0.40.69 by Parth Malwankar
reduced lock/unlock
263
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
264
        if _skip_file(opts.include, opts.exclude, fp):
0.40.74 by Parth Malwankar
optimization. --include/exclude are checked before reading the file.
265
            continue
266
0.40.69 by Parth Malwankar
reduced lock/unlock
267
        if fc == 'V' and fkind == 'file':
268
            if revno != None:
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
269
                # If old result is valid, print results immediately.
270
                # Otherwise, add file info to to_grep so that the
271
                # loop later will get chunks and grep them
0.40.97 by Parth Malwankar
fixed caching bug for rev range.
272
                file_rev = tree.inventory[fid].revision
273
                old_res = res_cache.get(file_rev)
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
274
                if old_res != None:
275
                    res = []
0.40.92 by Parth Malwankar
performance tweaks to core cached result print loop.
276
                    res_append = res.append
0.40.115 by Parth Malwankar
added test for versioned grep.
277
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
278
                    if opts.files_with_matches or opts.files_without_match:
0.40.122 by Parth Malwankar
added test for versioned -L and some fixes.
279
                        new_rev = '~' + revno
280
                    else:
0.40.115 by Parth Malwankar
added test for versioned grep.
281
                        new_rev = ('~%s:' % (revno,))
282
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
283
                    for line in old_res:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
284
                        if opts.files_with_matches or opts.files_without_match:
0.40.122 by Parth Malwankar
added test for versioned -L and some fixes.
285
                            s = _revno_pattern_list_only.sub(new_rev, line)
286
                        else:
0.40.115 by Parth Malwankar
added test for versioned grep.
287
                            s = _revno_pattern.sub(new_rev, line)
0.40.92 by Parth Malwankar
performance tweaks to core cached result print loop.
288
                        res_append(s)
289
                        outf_write(s)
0.40.97 by Parth Malwankar
fixed caching bug for rev range.
290
                    dir_res[file_rev] = res
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
291
                else:
0.40.92 by Parth Malwankar
performance tweaks to core cached result print loop.
292
                    to_grep_append((fid, (fp, fid)))
0.40.69 by Parth Malwankar
reduced lock/unlock
293
            else:
294
                # we are grepping working tree.
295
                if from_dir == None:
296
                    from_dir = '.'
297
298
                path_for_file = osutils.pathjoin(tree.basedir, from_dir, fp)
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
299
                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.
300
                    # Optimize for wtree list-only as we don't need to read the
301
                    # entire file
0.40.119 by Parth Malwankar
optimization: ignore case is not checked for every line.
302
                    file = codecs.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
303
                    _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.
304
                else:
305
                    file_text = codecs.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
306
                    _file_grep(file_text, fp, opts, revno, path_prefix)
0.40.43 by Parth Malwankar
moved cmd_grep._grep_dir to grep.dir_grep
307
0.40.85 by Parth Malwankar
optimized versioned grep to use iter_files_bytes.
308
    if revno != None: # grep versioned files
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
309
        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.
310
            path = _make_display_path(relpath, path)
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
311
            res = _file_grep(chunks[0], path, opts, revno, path_prefix)
0.40.97 by Parth Malwankar
fixed caching bug for rev range.
312
            file_rev = tree.inventory[fid].revision
313
            dir_res[file_rev] = res
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
314
    return dir_res
0.40.43 by Parth Malwankar
moved cmd_grep._grep_dir to grep.dir_grep
315
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
316
0.41.8 by Parth Malwankar
code cleanup.
317
def _make_display_path(relpath, path):
318
    """Return path string relative to user cwd.
0.40.42 by Parth Malwankar
fix to make grep paths relative to cwd
319
0.41.8 by Parth Malwankar
code cleanup.
320
    Take tree's 'relpath' and user supplied 'path', and return path
321
    that can be displayed to the user.
322
    """
0.40.15 by Parth Malwankar
some fixes and test updates
323
    if relpath:
0.40.52 by Parth Malwankar
code cleanup and documentation
324
        # update path so to display it w.r.t cwd
325
        # handle windows slash separator
0.40.20 by Parth Malwankar
used path functions from bzrlib.osutils
326
        path = osutils.normpath(osutils.pathjoin(relpath, path))
0.40.22 by Parth Malwankar
fixed display path formatting on windows
327
        path = path.replace('\\', '/')
328
        path = path.replace(relpath + '/', '', 1)
0.41.8 by Parth Malwankar
code cleanup.
329
    return path
330
331
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
332
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
333
    """Create a file object for the specified id and pass it on to _file_grep.
334
    """
335
336
    path = _make_display_path(relpath, path)
0.41.12 by Parth Malwankar
initial support for working tree grep (no test cases yet!)
337
    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
338
    _file_grep(file_text, path, opts, revno, path_prefix)
0.41.21 by Parth Malwankar
include/exclude working now. tests not added.
339
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
340
0.41.21 by Parth Malwankar
include/exclude working now. tests not added.
341
def _path_in_glob_list(path, glob_list):
342
    present = False
343
    for glob in glob_list:
344
        if fnmatch(path, glob):
345
            present = True
346
            break
347
    return present
0.41.12 by Parth Malwankar
initial support for working tree grep (no test cases yet!)
348
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
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
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.
351
    # test and skip binary files
352
    if '\x00' in file.read(1024):
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
353
        if opts.verbose:
0.40.116 by Parth Malwankar
optimization for wtree list-only grep to avoid full file read.
354
            trace.warning("Binary file '%s' skipped." % path)
0.40.118 by Parth Malwankar
further optimization of _file_grep_list_only_wtree.
355
            return
356
357
    file.seek(0) # search from beginning
358
359
    found = False
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
360
    if opts.fixed_string:
361
        pattern = opts.pattern.encode(_user_encoding, 'replace')
0.46.1 by Martin
Make -Fi use regexps for re.IGNORECASE rather than double str.lower
362
        for line in file:
363
            if pattern in line:
364
                found = True
365
                break
0.40.121 by Parth Malwankar
initial implementation of -L/--files-without-matches. no tests.
366
    else: # not fixed_string
0.40.116 by Parth Malwankar
optimization for wtree list-only grep to avoid full file read.
367
        for line in file:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
368
            if opts.patternc.search(line):
0.40.118 by Parth Malwankar
further optimization of _file_grep_list_only_wtree.
369
                found = True
0.40.116 by Parth Malwankar
optimization for wtree list-only grep to avoid full file read.
370
                break
371
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
372
    if (opts.files_with_matches and found) or \
373
        (opts.files_without_match and not found):
0.40.118 by Parth Malwankar
further optimization of _file_grep_list_only_wtree.
374
        if path_prefix and path_prefix != '.':
375
            # user has passed a dir arg, show that as result prefix
376
            path = osutils.pathjoin(path_prefix, path)
377
        path = path.encode(_terminal_encoding, 'replace')
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
378
        s = path + opts.eol_marker
379
        opts.outf.write(s)
380
381
0.46.3 by Martin
Start moving formatting setup out of _file_grep, only for files_with_matches so far
382
def _make_line_writer(opts):
383
    """Precalculate formatting and return function for writing output lines
384
385
    The idea here is to do this work only once per run, and return a function
386
    that will do the minimum amount possible for each match.
387
388
    It may be best to add another step that also preformats things that don't
389
    change across a file like the path and revno to leave the line writer
390
    even smaller.
391
    """
392
    write = opts.outf.write
393
    pattern = opts.pattern.encode(_user_encoding, 'replace')
394
    patternc = opts.patternc
395
    eol = opts.eol_marker
396
397
    if opts.show_color:
398
        path_start = FG.MAGENTA
399
        sep = color_string(':', FG.BOLD_CYAN)
400
        rev_sep = color_string('~', FG.BOLD_YELLOW)
401
    else:
402
        path_start = ""
403
        sep = ":"
404
        rev_sep = "~"
405
406
    if opts.files_with_matches or opts.files_without_match:
407
        if opts.print_revno:
408
            format = "%s%%(path)s%s%%(revno)s%s" % (path_start, rev_sep, eol)
409
        else:
410
            format = "%s%%(path)s%s" % (path_start, eol)
0.46.4 by Martin
Move remaining formatting setup out of _file_grep and save a colour switch as a side effect
411
    elif opts.print_revno and opts.line_number:
412
        format = "%s%%(path)s%s%%(revno)s%s%%(lineno)d%s%%(line)s%s" % (
413
            path_start, rev_sep, sep, sep, eol)
414
    elif opts.print_revno:
415
        format = "%s%%(path)s%s%%(revno)s%s%%(line)s%s" % (
416
            path_start, rev_sep, sep, eol)
417
    elif opts.line_number:
418
        format = "%s%%(path)s%s%%(lineno)d%s%%(line)s%s" % (
419
            path_start, sep, sep, eol)
420
    else:
421
        format = "%s%%(path)s%s%%(line)s%s" % (path_start, sep, eol)
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
422
0.46.3 by Martin
Start moving formatting setup out of _file_grep, only for files_with_matches so far
423
    def _line_writer(**kwargs):
0.46.8 by Martin
Move pattern highlighting out of _file_grep and into the line writing code
424
        """Write formatted line from arguments given by underlying opts"""
0.46.3 by Martin
Start moving formatting setup out of _file_grep, only for files_with_matches so far
425
        line = format % kwargs
426
        write(line)
0.46.8 by Martin
Move pattern highlighting out of _file_grep and into the line writing code
427
        # GZ 2010-06-02: Need to return line for the 'res_cache' hack to
0.46.3 by Martin
Start moving formatting setup out of _file_grep, only for files_with_matches so far
428
        #                avoiding checking the same file twice, clean this
429
        #                up later by changing that mechanism.
430
        return line
0.46.8 by Martin
Move pattern highlighting out of _file_grep and into the line writing code
431
    if not opts.show_color:
432
        return _line_writer
433
434
    if not opts.fixed_string:
435
        sub = re.compile(pattern.join(("((?:",")+)")), patternc.flags).sub
436
        highlight = color_string("\\1", FG.BOLD_RED)
437
        def _line_writer_regexp_highlighted(line, **kwargs):
438
            """Write formatted line with matched pattern highlighted"""
439
            return _line_writer(line=sub(highlight, line), **kwargs)
440
        return _line_writer_regexp_highlighted
441
442
    highlighted = color_string(pattern, FG.BOLD_RED)
443
    def _line_writer_fixed_highlighted(line, **kwargs):
444
        """Write formatted line with string searched for highlighted"""
445
        return _line_writer(line=line.replace(pattern, highlighted), **kwargs)
446
    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
447
448
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
449
def _file_grep(file_text, path, opts, revno, path_prefix=None):
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
450
    res = []
0.40.119 by Parth Malwankar
optimization: ignore case is not checked for every line.
451
    res_append = res.append
0.40.83 by Parth Malwankar
added support for -F/--fixed-string.
452
0.46.8 by Martin
Move pattern highlighting out of _file_grep and into the line writing code
453
    pattern = opts.pattern.encode(_user_encoding, 'replace')
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
454
    patternc = opts.patternc
455
0.41.9 by Parth Malwankar
refactored code towards support for working tree grep.
456
    # test and skip binary files
0.40.62 by Parth Malwankar
performance optimization
457
    if '\x00' in file_text[:1024]:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
458
        if opts.verbose:
0.40.60 by Parth Malwankar
'binary file skipped' warning is only shown with --verbose flag
459
            trace.warning("Binary file '%s' skipped." % path)
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
460
        return res
0.41.9 by Parth Malwankar
refactored code towards support for working tree grep.
461
0.40.52 by Parth Malwankar
code cleanup and documentation
462
    if path_prefix and path_prefix != '.':
463
        # user has passed a dir arg, show that as result prefix
464
        path = osutils.pathjoin(path_prefix, path)
465
0.46.8 by Martin
Move pattern highlighting out of _file_grep and into the line writing code
466
    path = path.encode(_terminal_encoding, 'replace')
0.43.8 by Parth Malwankar
added color for regex pattern.
467
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
468
    writeline = opts.line_writer
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
469
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
470
    if opts.files_with_matches or opts.files_without_match:
0.40.112 by Parth Malwankar
support for -l, --files-with-matches. no tests yet.
471
        # While printing files with matches we only have two case
472
        # print file name or print file name with revno.
0.40.121 by Parth Malwankar
initial implementation of -L/--files-without-matches. no tests.
473
        found = False
0.46.2 by Martin
Remove redundant code on files_with_matches path in _file_grep
474
        if opts.fixed_string:
475
            for line in file_text.splitlines():
476
                if pattern in line:
477
                    found = True
478
                    break
0.40.112 by Parth Malwankar
support for -l, --files-with-matches. no tests yet.
479
        else:
0.46.2 by Martin
Remove redundant code on files_with_matches path in _file_grep
480
            for line in file_text.splitlines():
481
                if patternc.search(line):
482
                    found = True
483
                    break
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
484
        if (opts.files_with_matches and found) or \
485
                (opts.files_without_match and not found):
0.46.3 by Martin
Start moving formatting setup out of _file_grep, only for files_with_matches so far
486
            res_append(writeline(path=path, revno=revno))
0.40.122 by Parth Malwankar
added test for versioned -L and some fixes.
487
        return res # return from files_with|without_matches
0.40.112 by Parth Malwankar
support for -l, --files-with-matches. no tests yet.
488
489
0.46.5 by Martin
Delete now redundant duplicated loops in _file_grep
490
    if opts.line_number:
491
        if opts.fixed_string:
492
            for index, line in enumerate(file_text.splitlines()):
493
                if pattern in line:
494
                    res_append(writeline(path=path, revno=revno,
495
                        lineno=index+1, line=line))
496
        else:
497
            for index, line in enumerate(file_text.splitlines()):
498
                if patternc.search(line):
499
                    res_append(writeline(path=path, revno=revno,
500
                        lineno=index+1, line=line))
0.40.63 by Parth Malwankar
performance: moved conditionals out of core loop.
501
    else:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
502
        if opts.fixed_string:
0.46.1 by Martin
Make -Fi use regexps for re.IGNORECASE rather than double str.lower
503
            for line in file_text.splitlines():
504
                if pattern in line:
0.46.5 by Martin
Delete now redundant duplicated loops in _file_grep
505
                    res_append(writeline(path=path, revno=revno, line=line))
0.40.83 by Parth Malwankar
added support for -F/--fixed-string.
506
        else:
507
            for line in file_text.splitlines():
508
                if patternc.search(line):
0.46.5 by Martin
Delete now redundant duplicated loops in _file_grep
509
                    res_append(writeline(path=path, revno=revno, line=line))
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
510
    return res
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
511