/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/grep.py

  • Committer: Jelmer Vernooij
  • Date: 2019-06-02 02:35:46 UTC
  • mfrom: (7309 work)
  • mto: This revision was merged to the branch mainline in revision 7319.
  • Revision ID: jelmer@jelmer.uk-20190602023546-lqco868tnv26d8ow
merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2010 Canonical Ltd
2
 
#
 
2
 
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
from __future__ import absolute_import
18
18
 
19
19
import re
20
 
import sys
21
20
 
22
 
from ...lazy_import import lazy_import
 
21
from .lazy_import import lazy_import
23
22
lazy_import(globals(), """
24
23
from fnmatch import fnmatch
25
24
 
26
25
from breezy._termcolor import color_string, FG
27
26
 
28
27
from breezy import (
 
28
    diff,
 
29
    )
 
30
""")
 
31
from . import (
29
32
    controldir,
30
 
    diff,
31
33
    errors,
32
 
    lazy_regex,
 
34
    osutils,
33
35
    revision as _mod_revision,
34
 
    )
35
 
""")
36
 
from breezy import (
37
 
    osutils,
38
36
    trace,
39
37
    )
40
 
from breezy.revisionspec import (
 
38
from .revisionspec import (
41
39
    RevisionSpec,
42
40
    RevisionSpec_revid,
43
41
    RevisionSpec_revno,
44
42
    )
45
 
from breezy.sixish import (
 
43
from .sixish import (
46
44
    BytesIO,
47
45
    )
48
46
 
53
51
    """Raised when a revision is not on left-hand history."""
54
52
 
55
53
 
 
54
class GrepOptions(object):
 
55
    """Container to pass around grep options.
 
56
 
 
57
    This class is used as a container to pass around user option and
 
58
    some other params (like outf) to processing functions. This makes
 
59
    it easier to add more options as grep evolves.
 
60
    """
 
61
    verbose = False
 
62
    ignore_case = False
 
63
    no_recursive = False
 
64
    from_root = False
 
65
    null = False
 
66
    levels = None
 
67
    line_number = False
 
68
    path_list = None
 
69
    revision = None
 
70
    pattern = None
 
71
    include = None
 
72
    exclude = None
 
73
    fixed_string = False
 
74
    files_with_matches = False
 
75
    files_without_match = False
 
76
    color = None
 
77
    diff = False
 
78
 
 
79
    # derived options
 
80
    recursive = None
 
81
    eol_marker = None
 
82
    patternc = None
 
83
    sub_patternc = None
 
84
    print_revno = None
 
85
    fixed_string = None
 
86
    outf = None
 
87
    show_color = False
 
88
 
 
89
 
56
90
def _rev_on_mainline(rev_tuple):
57
91
    """returns True is rev tuple is on mainline"""
58
92
    if len(rev_tuple) == 1:
121
155
 
122
156
 
123
157
def compile_pattern(pattern, flags=0):
124
 
    patternc = None
125
158
    try:
126
 
        # use python's re.compile as we need to catch re.error in case of bad pattern
127
 
        lazy_regex.reset_compile()
128
 
        patternc = re.compile(pattern, flags)
 
159
        return re.compile(pattern, flags)
129
160
    except re.error as e:
130
161
        raise errors.BzrError("Invalid pattern: '%s'" % pattern)
131
 
    return patternc
 
162
    return None
132
163
 
133
164
 
134
165
def is_fixed_string(s):
151
182
                self.get_writer = self._get_writer_fixed_highlighted
152
183
            else:
153
184
                flags = opts.patternc.flags
154
 
                self._sub = re.compile(opts.pattern.join(("((?:", ")+)")), flags).sub
 
185
                self._sub = re.compile(
 
186
                    opts.pattern.join(("((?:", ")+)")), flags).sub
155
187
                self._highlight = color_string("\\1", FG.BOLD_RED)
156
188
                self.get_writer = self._get_writer_regexp_highlighted
157
189
        else:
161
193
        """Get function for writing file headers"""
162
194
        write = self.outf.write
163
195
        eol_marker = self.opts.eol_marker
 
196
 
164
197
        def _line_writer(line):
165
198
            write(line + eol_marker)
 
199
 
166
200
        def _line_writer_color(line):
167
201
            write(FG.BOLD_MAGENTA + line + FG.NONE + eol_marker)
168
202
        if self.opts.show_color:
175
209
        """Get function for writing revno lines"""
176
210
        write = self.outf.write
177
211
        eol_marker = self.opts.eol_marker
 
212
 
178
213
        def _line_writer(line):
179
214
            write(line + eol_marker)
 
215
 
180
216
        def _line_writer_color(line):
181
217
            write(FG.BOLD_BLUE + line + FG.NONE + eol_marker)
182
218
        if self.opts.show_color:
189
225
        """Get function for writing uncoloured output"""
190
226
        write = self.outf.write
191
227
        eol_marker = self.opts.eol_marker
 
228
 
192
229
        def _line_writer(line):
193
230
            write(line + eol_marker)
194
231
        return _line_writer
197
234
        """Get function for writing output with regexp match highlighted"""
198
235
        _line_writer = self._get_writer_plain()
199
236
        sub, highlight = self._sub, self._highlight
 
237
 
200
238
        def _line_writer_regexp_highlighted(line):
201
239
            """Write formatted line with matched pattern highlighted"""
202
240
            return _line_writer(line=sub(highlight, line))
206
244
        """Get function for writing output with search string highlighted"""
207
245
        _line_writer = self._get_writer_plain()
208
246
        old, new = self._old, self._new
 
247
 
209
248
        def _line_writer_fixed_highlighted(line):
210
249
            """Write formatted line with string searched for highlighted"""
211
250
            return _line_writer(line=line.replace(old, new))
221
260
        else:
222
261
            # if no revision is sepcified for diff grep we grep all changesets.
223
262
            opts.revision = [RevisionSpec.from_string('revno:1'),
224
 
                RevisionSpec.from_string('last:1')]
 
263
                             RevisionSpec.from_string('last:1')]
225
264
            start_rev = opts.revision[0]
226
265
        start_revid = start_rev.as_revision_id(branch)
227
266
        if start_revid == b'null:':
234
273
                end_revno, end_revid = branch.last_revision_info()
235
274
            erevno_tuple = branch.revision_id_to_dotted_revno(end_revid)
236
275
 
237
 
            grep_mainline = (_rev_on_mainline(srevno_tuple) and
238
 
                _rev_on_mainline(erevno_tuple))
 
276
            grep_mainline = (_rev_on_mainline(srevno_tuple)
 
277
                             and _rev_on_mainline(erevno_tuple))
239
278
 
240
279
            # ensure that we go in reverse order
241
280
            if srevno_tuple > erevno_tuple:
246
285
            # faster when we don't want to look at merged revs. We try this
247
286
            # with _linear_view_revisions. If all revs are to be grepped we
248
287
            # 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)
 
288
            if opts.levels == 1 and grep_mainline:
 
289
                given_revs = _linear_view_revisions(
 
290
                    branch, start_revid, end_revid)
251
291
            else:
252
 
                given_revs = _graph_view_revisions(branch, start_revid, end_revid)
 
292
                given_revs = _graph_view_revisions(
 
293
                    branch, start_revid, end_revid)
253
294
        else:
254
295
            # We do an optimization below. For grepping a specific revison
255
296
            # We don't need to call _graph_view_revisions which is slow.
259
300
            start_rev_tuple = (start_revid, start_revno, 0)
260
301
            given_revs = [start_rev_tuple]
261
302
        repo = branch.repository
262
 
        diff_pattern = re.compile(b"^[+\\-].*(" + opts.pattern.encode(_user_encoding) + b")")
 
303
        diff_pattern = re.compile(
 
304
            b"^[+\\-].*(" + opts.pattern.encode(_user_encoding) + b")")
263
305
        file_pattern = re.compile(b"=== (modified|added|removed) file '.*'")
264
306
        outputter = _GrepDiffOutputter(opts)
265
307
        writeline = outputter.get_writer()
271
313
                # with level=1 show only top level
272
314
                continue
273
315
 
274
 
            rev_spec = RevisionSpec_revid.from_string("revid:"+revid.decode('utf-8'))
 
316
            rev_spec = RevisionSpec_revid.from_string(
 
317
                "revid:" + revid.decode('utf-8'))
275
318
            new_rev = repo.get_revision(revid)
276
319
            new_tree = rev_spec.as_tree(branch)
277
320
            if len(new_rev.parent_ids) == 0:
281
324
            old_tree = repo.revision_tree(ancestor_id)
282
325
            s = BytesIO()
283
326
            diff.show_diff_trees(old_tree, new_tree, s,
284
 
                old_label='', new_label='')
 
327
                                 old_label='', new_label='')
285
328
            display_revno = True
286
329
            display_file = False
287
330
            file_header = None
295
338
                        writerevno("=== revno:%s ===" % (revno,))
296
339
                        display_revno = False
297
340
                    if display_file:
298
 
                        writefileheader("  %s" % (file_header.decode(file_encoding, 'replace'),))
 
341
                        writefileheader(
 
342
                            "  %s" % (file_header.decode(file_encoding, 'replace'),))
299
343
                        display_file = False
300
344
                    line = line.decode(file_encoding, 'replace')
301
345
                    writeline("    %s" % (line,))
319
363
                end_revno, end_revid = branch.last_revision_info()
320
364
            erevno_tuple = branch.revision_id_to_dotted_revno(end_revid)
321
365
 
322
 
            grep_mainline = (_rev_on_mainline(srevno_tuple) and
323
 
                _rev_on_mainline(erevno_tuple))
 
366
            grep_mainline = (_rev_on_mainline(srevno_tuple)
 
367
                             and _rev_on_mainline(erevno_tuple))
324
368
 
325
369
            # ensure that we go in reverse order
326
370
            if srevno_tuple > erevno_tuple:
332
376
            # with _linear_view_revisions. If all revs are to be grepped we
333
377
            # use the slower _graph_view_revisions
334
378
            if opts.levels == 1 and grep_mainline:
335
 
                given_revs = _linear_view_revisions(branch, start_revid, end_revid)
 
379
                given_revs = _linear_view_revisions(
 
380
                    branch, start_revid, end_revid)
336
381
            else:
337
 
                given_revs = _graph_view_revisions(branch, start_revid, end_revid)
 
382
                given_revs = _graph_view_revisions(
 
383
                    branch, start_revid, end_revid)
338
384
        else:
339
385
            # We do an optimization below. For grepping a specific revison
340
386
            # We don't need to call _graph_view_revisions which is slow.
352
398
                # with level=1 show only top level
353
399
                continue
354
400
 
355
 
            rev = RevisionSpec_revid.from_string("revid:"+revid.decode('utf-8'))
 
401
            rev = RevisionSpec_revid.from_string(
 
402
                "revid:" + revid.decode('utf-8'))
356
403
            tree = rev.as_tree(branch)
357
404
            for path in opts.path_list:
358
405
                tree_path = osutils.pathjoin(relpath, path)
364
411
                    path_prefix = path
365
412
                    dir_grep(tree, path, relpath, opts, revno, path_prefix)
366
413
                else:
367
 
                    versioned_file_grep(tree, tree_path, '.', path, opts, revno)
 
414
                    versioned_file_grep(
 
415
                        tree, tree_path, '.', path, opts, revno)
368
416
 
369
417
 
370
418
def workingtree_grep(opts):
371
 
    revno = opts.print_revno = None # for working tree set revno to None
 
419
    revno = opts.print_revno = None  # for working tree set revno to None
372
420
 
373
421
    tree, branch, relpath = \
374
422
        controldir.ControlDir.open_containing_tree_or_branch('.')
375
423
    if not tree:
376
424
        msg = ('Cannot search working tree. Working tree not found.\n'
377
 
            'To search for specific revision in history use the -r option.')
 
425
               'To search for specific revision in history use the -r option.')
378
426
        raise errors.BzrCommandError(msg)
379
427
 
380
428
    # GZ 2010-06-02: Shouldn't be smuggling this on opts, but easy for now
416
464
    #                and hits manually refilled. Could do this again if it was
417
465
    #                for a good reason, otherwise cache might want purging.
418
466
    outputter = opts.outputter
419
 
    for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
420
 
        from_dir=from_dir, recursive=opts.recursive):
 
467
    for fp, fc, fkind, entry in tree.list_files(
 
468
            include_root=False, from_dir=from_dir, recursive=opts.recursive):
421
469
 
422
470
        if _skip_file(opts.include, opts.exclude, fp):
423
471
            continue
428
476
                # If old result is valid, print results immediately.
429
477
                # Otherwise, add file info to to_grep so that the
430
478
                # loop later will get chunks and grep them
431
 
                cache_id = tree.get_file_revision(tree_path, fid)
 
479
                cache_id = tree.get_file_revision(tree_path)
432
480
                if cache_id in outputter.cache:
433
481
                    # GZ 2010-06-05: Not really sure caching and re-outputting
434
482
                    #                the old path is really the right thing,
451
499
                    with open(path_for_file, 'rb') as f:
452
500
                        _file_grep(f.read(), fp, opts, revno, path_prefix)
453
501
 
454
 
    if revno is not None: # grep versioned files
 
502
    if revno is not None:  # grep versioned files
455
503
        for (path, tree_path), chunks in tree.iter_files_bytes(to_grep):
456
504
            path = _make_display_path(relpath, path)
457
505
            _file_grep(b''.join(chunks), path, opts, revno, path_prefix,
458
 
                tree.get_file_revision(tree_path))
 
506
                       tree.get_file_revision(tree_path))
459
507
 
460
508
 
461
509
def _make_display_path(relpath, path):
473
521
    return path
474
522
 
475
523
 
476
 
def versioned_file_grep(tree, tree_path, relpath, path, opts, revno, path_prefix = None):
 
524
def versioned_file_grep(tree, tree_path, relpath, path, opts, revno, path_prefix=None):
477
525
    """Create a file object for the specified id and pass it on to _file_grep.
478
526
    """
479
527
 
496
544
            trace.warning("Binary file '%s' skipped.", path)
497
545
        return
498
546
 
499
 
    file.seek(0) # search from beginning
 
547
    file.seek(0)  # search from beginning
500
548
 
501
549
    found = False
502
550
    if opts.fixed_string:
505
553
            if pattern in line:
506
554
                found = True
507
555
                break
508
 
    else: # not fixed_string
 
556
    else:  # not fixed_string
509
557
        for line in file:
510
558
            if opts.patternc.search(line):
511
559
                found = True
512
560
                break
513
561
 
514
562
    if (opts.files_with_matches and found) or \
515
 
        (opts.files_without_match and not found):
 
563
            (opts.files_without_match and not found):
516
564
        if path_prefix and path_prefix != '.':
517
565
            # user has passed a dir arg, show that as result prefix
518
566
            path = osutils.pathjoin(path_prefix, path)
525
573
    The idea here is to do this work only once per run, and finally return a
526
574
    function that will do the minimum amount possible for each match.
527
575
    """
 
576
 
528
577
    def __init__(self, opts, use_cache=False):
529
578
        self.outf = opts.outf
530
579
        if use_cache:
547
596
                self.get_writer = self._get_writer_fixed_highlighted
548
597
            else:
549
598
                flags = opts.patternc.flags
550
 
                self._sub = re.compile(opts.pattern.join(("((?:", ")+)")), flags).sub
 
599
                self._sub = re.compile(
 
600
                    opts.pattern.join(("((?:", ")+)")), flags).sub
551
601
                self._highlight = color_string("\\1", FG.BOLD_RED)
552
602
                self.get_writer = self._get_writer_regexp_highlighted
553
603
            path_start = FG.MAGENTA
578
628
    def _get_writer_plain(self, path, revno, cache_id):
579
629
        """Get function for writing uncoloured output"""
580
630
        per_line = self._format_perline
581
 
        start = self._format_initial % {"path":path, "revno":revno}
 
631
        start = self._format_initial % {"path": path, "revno": revno}
582
632
        write = self.outf.write
583
633
        if self.cache is not None and cache_id is not None:
584
634
            result_list = []
585
635
            self.cache[cache_id] = path, result_list
586
636
            add_to_cache = result_list.append
 
637
 
587
638
            def _line_cache_and_writer(**kwargs):
588
639
                """Write formatted line and cache arguments"""
589
640
                end = per_line % kwargs
590
641
                add_to_cache(end)
591
642
                write(start + end)
592
643
            return _line_cache_and_writer
 
644
 
593
645
        def _line_writer(**kwargs):
594
646
            """Write formatted line from arguments given by underlying opts"""
595
647
            write(start + per_line % kwargs)
598
650
    def write_cached_lines(self, cache_id, revno):
599
651
        """Write cached results out again for new revision"""
600
652
        cached_path, cached_matches = self.cache[cache_id]
601
 
        start = self._format_initial % {"path":cached_path, "revno":revno}
 
653
        start = self._format_initial % {"path": cached_path, "revno": revno}
602
654
        write = self.outf.write
603
655
        for end in cached_matches:
604
656
            write(start + end)
607
659
        """Get function for writing output with regexp match highlighted"""
608
660
        _line_writer = self._get_writer_plain(path, revno, cache_id)
609
661
        sub, highlight = self._sub, self._highlight
 
662
 
610
663
        def _line_writer_regexp_highlighted(line, **kwargs):
611
664
            """Write formatted line with matched pattern highlighted"""
612
665
            return _line_writer(line=sub(highlight, line), **kwargs)
616
669
        """Get function for writing output with search string highlighted"""
617
670
        _line_writer = self._get_writer_plain(path, revno, cache_id)
618
671
        old, new = self._old, self._new
 
672
 
619
673
        def _line_writer_fixed_highlighted(line, **kwargs):
620
674
            """Write formatted line with string searched for highlighted"""
621
675
            return _line_writer(line=line.replace(old, new), **kwargs)
672
726
            for index, line in enumerate(file_text.splitlines()):
673
727
                if pattern in line:
674
728
                    line = line.decode(file_encoding, 'replace')
675
 
                    writeline(lineno=index+start, line=line)
 
729
                    writeline(lineno=index + start, line=line)
676
730
        else:
677
731
            for line in file_text.splitlines():
678
732
                if pattern in line:
700
754
            for index, line in enumerate(file_text.splitlines()):
701
755
                if search(line):
702
756
                    line = line.decode(file_encoding, 'replace')
703
 
                    writeline(lineno=index+start, line=line)
 
757
                    writeline(lineno=index + start, line=line)
704
758
        else:
705
759
            for line in file_text.splitlines():
706
760
                if search(line):
707
761
                    line = line.decode(file_encoding, 'replace')
708
762
                    writeline(line=line)
709