Merge lp:~jelmer/bzr/merge-grep into lp:bzr

Proposed by Jelmer Vernooij
Status: Superseded
Proposed branch: lp:~jelmer/bzr/merge-grep
Merge into: lp:bzr
Diff against target: 3488 lines (+3435/-0)
9 files modified
bzrlib/plugins/grep/.bzrignore (+1/-0)
bzrlib/plugins/grep/NEWS (+73/-0)
bzrlib/plugins/grep/__init__.py (+38/-0)
bzrlib/plugins/grep/cmds.py (+249/-0)
bzrlib/plugins/grep/grep.py (+728/-0)
bzrlib/plugins/grep/test_grep.py (+2254/-0)
bzrlib/termcolor.py (+78/-0)
bzrlib/tests/features.py (+12/-0)
doc/en/release-notes/bzr-2.6.txt (+2/-0)
To merge this branch: bzr merge lp:~jelmer/bzr/merge-grep
Reviewer Review Type Date Requested Status
Martin Packman (community) Needs Fixing
Review via email: mp+112723@code.launchpad.net

Commit message

Add bzr-grep as a core plugin

To post a comment you must log in.
Revision history for this message
Martin Packman (gz) wrote :

Let's do this.

review: Approve
Revision history for this message
Martin Packman (gz) wrote :

sent to pqm by email

Revision history for this message
Martin Packman (gz) wrote :

Tests do not pass.

bp.grep.test_grep.TestGrep.test_dotted_rev_grep
bp.grep.test_grep.TestGrep.test_levels
                          .test_no_tree
                          .test_revno_basic_history_grep_full
                          .test_revno_range_versioned_file_in_dir
                          .test_ver_basic_exclude
                          .test_ver_basic_include
                          .test_ver_files_with_matches
                          .test_ver_files_without_matches
                          .test_ver_multiple_files
                          .test_ver_null_option
                          .test_versioned_exclude_file_within_dir
                          .test_versioned_exclude_from_outside_dir
                          .test_versioned_file_in_dir_no_recursive
                          .test_versioned_file_in_dir_recurse
                          .test_versioned_file_within_dir
                          .test_versioned_file_within_dir_two_levels
                          .test_versioned_files_from_outside_dir
                          .test_versioned_files_from_outside_two_dirs
                          .test_versioned_from_root_pass
                          .test_versioned_include_file_within_dir
                          .test_versioned_include_from_outside_dir
bp.grep.test_grep.TestNonAscii.test_unicode_only_file

Traceback (most recent call last):
  ...
  File "/home/pqm/pqm-workdir/srv/+trunk/bp/grep/cmds.py", line 249, in run
    grep.versioned_grep(GrepOptions)
  File "/home/pqm/pqm-workdir/srv/+trunk/bp/grep/grep.py", line 369, in versioned_grep
    dir_grep(tree, path, relpath, opts, revno, path_prefix)
  File "/home/pqm/pqm-workdir/srv/+trunk/bp/grep/grep.py", line 438, in dir_grep
    cache_id = tree.inventory[fid].revision
  File "/home/pqm/pqm-workdir/srv/+trunk/bzrlib/symbol_versioning.py", line 138, in decorated_method
    warn(deprecation_version % symbol, DeprecationWarning, stacklevel=2)
DeprecationWarning: bzrlib.revisiontree.InventoryRevisionTree._get_inventory was deprecated in version 2.5.0.

bp.grep.test_grep.TestGrep.test_revno_range_basic_history_grep
                          .test_revno_range_versioned_file_from_outside_dir
                          .test_revspec
bp.grep.test_grep.TestGrepDiff.test_grep_diff_basic
                              .test_grep_diff_revision_range

Traceback (most recent call last):
  ...
  File "/home/pqm/pqm-workdir/srv/+trunk/bp/grep/cmds.py", line 249, in run
    grep.versioned_grep(GrepOptions)
  File "/home/pqm/pqm-workdir/srv/+trunk/bp/grep/grep.py", line 353, in versioned_grep
    for revid, revno, merge_depth in given_revs:
  File "/home/pqm/pqm-workdir/srv/+trunk/bp/grep/grep.py", line 70, in _linear_view_revisions
    for revision_id in repo.iter_reverse_revision_history(end_rev_id):
AttributeError: 'CHKInventoryRepository' object has no attribute 'iter_reverse_revision_history'

review: Needs Fixing

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'bzrlib/plugins/grep'
2=== added file 'bzrlib/plugins/grep/.bzrignore'
3--- bzrlib/plugins/grep/.bzrignore 1970-01-01 00:00:00 +0000
4+++ bzrlib/plugins/grep/.bzrignore 2012-08-03 11:45:28 +0000
5@@ -0,0 +1,1 @@
6+./build
7
8=== added file 'bzrlib/plugins/grep/NEWS'
9--- bzrlib/plugins/grep/NEWS 1970-01-01 00:00:00 +0000
10+++ bzrlib/plugins/grep/NEWS 2012-08-03 11:45:28 +0000
11@@ -0,0 +1,73 @@
12+This is the NEWS file from bzr-grep from before it was merged into bzr core.
13+For changes before then, please refer to the main bzr log file.
14+
15+bzr-grep 0.5.0-final - Unreleased
16+==================================
17+* ``bzr grep`` now supports ``--diff|-p`` option to search through
18+ changesets. (Parth Malwankar, #540705)
19+
20+* Option ``grep_color`` can be set in ``bazaar.conf`` instead of using
21+ the option ``--color`` from the command line. (Johan Dahlin)
22+
23+bzr-grep 0.4.0-final - 08-Jun-2010
24+==================================
25+* Add seperate output formatter to reduce duplication of search loops,
26+ additionally make -Fi use regexp rather than lowercasing pattern and
27+ entirety of text for the same reason. This also fixes bug #590589
28+ - UnicodeDecodeError with options -Fi. (Martin [gz])
29+
30+* Added fast path for no match that avoids splitting the file text into
31+ seperate lines and testing each one, by checking the entire text for a
32+ possible match initially. (Martin [gz])
33+
34+* Added Makefile. (Parth Malwankar)
35+
36+* Fixed setup.py to work correctly. (Martin [gz])
37+
38+bzr-grep 0.3.0-final - 23-May-2010
39+==================================
40+* Support for --color option (POSIX only). (Parth Malwankar, #571694)
41+
42+* Revisions in branches without trees can now be searched with
43+ -r option. (Parth Malwankar, #584240)
44+
45+* Trying to search working tree for a treeless branch no longer
46+ produces a stack trace but gives an error message suggesting use of
47+ -r option. (Parth Malwankar, #572658)
48+
49+bzr-grep 0.2.0-final - 30-Mar-2010
50+==================================
51+* 'binary file skipped' warning is not shows without --verbose flag
52+ (Parth Malwankar, #539031)
53+
54+* Added support for -F/--fixed-string for faster search.
55+ Simple patterns [a-zA-Z0-9 _] are now implicitly -F and searched faster.
56+ (Parth Malwankar, #539263)
57+
58+* Better unicode handling. bzr-grep no longer crashes with UnicodeDecode
59+ error for some outputs. (Parth Malwankar, #539258)
60+
61+* Faster grep for revision range. bzr-grep now caches results for
62+ files that have not changed between revisions.
63+ (Parth Malwankar, #542375)
64+
65+* Faster grep for specific revision. (Parth Malwankar, #539429)
66+
67+* Significant performance improvement. Working tree grep for bzr.dev
68+ has gone from ~7.5s to ~1s. (Parth Malwankar, #539028)
69+
70+* Support for -L/--files-without-match and -l/files-with-matches
71+ (Parth Malwankar, #540097)
72+
73+bzr-grep 0.1.0-final - 14-Mar-2010
74+==================================
75+* --recursive is now default. (Parth Malwankar, #536688)
76+
77+* ``bzr grep`` searches working copy by default. (Parth Malwankar, #537072)
78+
79+* --include/exclude=GLOB is now supported. (Parth Malwankar, #529889)
80+
81+bzr-grep 0.0.1-final - 10-Mar-2010
82+==================================
83+* Initial release (Parth Malwankar)
84+
85
86=== added file 'bzrlib/plugins/grep/__init__.py'
87--- bzrlib/plugins/grep/__init__.py 1970-01-01 00:00:00 +0000
88+++ bzrlib/plugins/grep/__init__.py 2012-08-03 11:45:28 +0000
89@@ -0,0 +1,38 @@
90+# Copyright (C) 2010 Canonical Ltd
91+#
92+# This program is free software; you can redistribute it and/or modify
93+# it under the terms of the GNU General Public License as published by
94+# the Free Software Foundation; either version 2 of the License, or
95+# (at your option) any later version.
96+#
97+# This program is distributed in the hope that it will be useful,
98+# but WITHOUT ANY WARRANTY; without even the implied warranty of
99+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
100+# GNU General Public License for more details.
101+#
102+# You should have received a copy of the GNU General Public License
103+# along with this program; if not, write to the Free Software
104+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
105+
106+"""Print lines matching PATTERN for specified files and revisions."""
107+
108+from __future__ import absolute_import
109+
110+from bzrlib import version_info
111+from bzrlib.commands import plugin_cmds
112+
113+plugin_cmds.register_lazy("cmd_grep", [], "bzrlib.plugins.grep.cmds")
114+
115+def test_suite():
116+ from bzrlib.tests import TestUtil
117+
118+ suite = TestUtil.TestSuite()
119+ loader = TestUtil.TestLoader()
120+ testmod_names = [
121+ 'test_grep',
122+ ]
123+
124+ suite.addTest(loader.loadTestsFromModuleNames(
125+ ["%s.%s" % (__name__, tmn) for tmn in testmod_names]))
126+ return suite
127+
128
129=== added file 'bzrlib/plugins/grep/cmds.py'
130--- bzrlib/plugins/grep/cmds.py 1970-01-01 00:00:00 +0000
131+++ bzrlib/plugins/grep/cmds.py 2012-08-03 11:45:28 +0000
132@@ -0,0 +1,249 @@
133+# Copyright (C) 2010 Canonical Ltd
134+#
135+# This program is free software; you can redistribute it and/or modify
136+# it under the terms of the GNU General Public License as published by
137+# the Free Software Foundation; either version 2 of the License, or
138+# (at your option) any later version.
139+#
140+# This program is distributed in the hope that it will be useful,
141+# but WITHOUT ANY WARRANTY; without even the implied warranty of
142+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
143+# GNU General Public License for more details.
144+#
145+# You should have received a copy of the GNU General Public License
146+# along with this program; if not, write to the Free Software
147+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
148+
149+"""Print lines matching PATTERN for specified files and revisions."""
150+
151+from __future__ import absolute_import
152+
153+from bzrlib import errors
154+from bzrlib.commands import Command, display_command
155+from bzrlib.option import Option, ListOption
156+from bzrlib.config import GlobalConfig
157+
158+# FIXME: _parse_levels should be shared with bzrlib.builtins. this is a copy
159+# to avoid the error
160+# "IllegalUseOfScopeReplacer: ScopeReplacer object '_parse_levels' was used
161+# incorrectly: Object already cleaned up, did you assign it to another
162+# variable?: _factory
163+# with lazy import
164+def _parse_levels(s):
165+ try:
166+ return int(s)
167+ except ValueError:
168+ msg = "The levels argument must be an integer."
169+ raise errors.BzrCommandError(msg)
170+
171+
172+class GrepOptions(object):
173+ """Container to pass around grep options.
174+
175+ This class is used as a container to pass around user option and
176+ some other params (like outf) to processing functions. This makes
177+ it easier to add more options as grep evolves.
178+ """
179+ verbose = False
180+ ignore_case = False
181+ no_recursive = False
182+ from_root = False
183+ null = False
184+ levels = None
185+ line_number = False
186+ path_list = None
187+ revision = None
188+ pattern = None
189+ include = None
190+ exclude = None
191+ fixed_string = False
192+ files_with_matches = False
193+ files_without_match = False
194+ color = None
195+ diff = False
196+
197+ # derived options
198+ recursive = None
199+ eol_marker = None
200+ patternc = None
201+ sub_patternc = None
202+ print_revno = None
203+ fixed_string = None
204+ outf = None
205+ show_color = False
206+
207+
208+class cmd_grep(Command):
209+ """Print lines matching PATTERN for specified files and revisions.
210+
211+ This command searches the specified files and revisions for a given
212+ pattern. The pattern is specified as a Python regular expressions[1].
213+
214+ If the file name is not specified, the revisions starting with the
215+ current directory are searched recursively. If the revision number is
216+ not specified, the working copy is searched. To search the last committed
217+ revision, use the '-r -1' or '-r last:1' option.
218+
219+ Unversioned files are not searched unless explicitly specified on the
220+ command line. Unversioned directores are not searched.
221+
222+ When searching a pattern, the output is shown in the 'filepath:string'
223+ format. If a revision is explicitly searched, the output is shown as
224+ 'filepath~N:string', where N is the revision number.
225+
226+ --include and --exclude options can be used to search only (or exclude
227+ from search) files with base name matches the specified Unix style GLOB
228+ pattern. The GLOB pattern an use *, ?, and [...] as wildcards, and \\
229+ to quote wildcard or backslash character literally. Note that the glob
230+ pattern is not a regular expression.
231+
232+ [1] http://docs.python.org/library/re.html#regular-expression-syntax
233+ """
234+
235+ encoding_type = 'replace'
236+ takes_args = ['pattern', 'path*']
237+ takes_options = [
238+ 'verbose',
239+ 'revision',
240+ Option('color', type=str, argname='when',
241+ help='Show match in color. WHEN is never, always or auto.'),
242+ Option('diff', short_name='p',
243+ help='Grep for pattern in changeset for each revision.'),
244+ ListOption('exclude', type=str, argname='glob', short_name='X',
245+ help="Skip files whose base name matches GLOB."),
246+ ListOption('include', type=str, argname='glob', short_name='I',
247+ help="Search only files whose base name matches GLOB."),
248+ Option('files-with-matches', short_name='l',
249+ help='Print only the name of each input file in '
250+ 'which PATTERN is found.'),
251+ Option('files-without-match', short_name='L',
252+ help='Print only the name of each input file in '
253+ 'which PATTERN is not found.'),
254+ Option('fixed-string', short_name='F',
255+ help='Interpret PATTERN is a single fixed string (not regex).'),
256+ Option('from-root',
257+ help='Search for pattern starting from the root of the branch. '
258+ '(implies --recursive)'),
259+ Option('ignore-case', short_name='i',
260+ help='ignore case distinctions while matching.'),
261+ Option('levels',
262+ help='Number of levels to display - 0 for all, 1 for collapsed '
263+ '(1 is default).',
264+ argname='N',
265+ type=_parse_levels),
266+ Option('line-number', short_name='n',
267+ help='show 1-based line number.'),
268+ Option('no-recursive',
269+ help="Don't recurse into subdirectories. (default is --recursive)"),
270+ Option('null', short_name='Z',
271+ help='Write an ASCII NUL (\\0) separator '
272+ 'between output lines rather than a newline.'),
273+ ]
274+
275+
276+ @display_command
277+ def run(self, verbose=False, ignore_case=False, no_recursive=False,
278+ from_root=False, null=False, levels=None, line_number=False,
279+ path_list=None, revision=None, pattern=None, include=None,
280+ exclude=None, fixed_string=False, files_with_matches=False,
281+ files_without_match=False, color=None, diff=False):
282+ from bzrlib import termcolor
283+ from bzrlib.plugins.grep import (
284+ grep,
285+ )
286+ import re
287+ if path_list is None:
288+ path_list = ['.']
289+ else:
290+ if from_root:
291+ raise errors.BzrCommandError('cannot specify both --from-root and PATH.')
292+
293+ if files_with_matches and files_without_match:
294+ raise errors.BzrCommandError('cannot specify both '
295+ '-l/--files-with-matches and -L/--files-without-matches.')
296+
297+ global_config = GlobalConfig()
298+
299+ if color is None:
300+ color = global_config.get_user_option('grep_color')
301+
302+ if color is None:
303+ color = 'never'
304+
305+ if color not in ['always', 'never', 'auto']:
306+ raise errors.BzrCommandError('Valid values for --color are '
307+ '"always", "never" or "auto".')
308+
309+ if levels==None:
310+ levels=1
311+
312+ print_revno = False
313+ if revision != None or levels == 0:
314+ # print revision numbers as we may be showing multiple revisions
315+ print_revno = True
316+
317+ eol_marker = '\n'
318+ if null:
319+ eol_marker = '\0'
320+
321+ if not ignore_case and grep.is_fixed_string(pattern):
322+ # if the pattern isalnum, implicitly use to -F for faster grep
323+ fixed_string = True
324+ elif ignore_case and fixed_string:
325+ # GZ 2010-06-02: Fall back to regexp rather than lowercasing
326+ # pattern and text which will cause pain later
327+ fixed_string = False
328+ pattern = re.escape(pattern)
329+
330+ patternc = None
331+ re_flags = re.MULTILINE
332+ if ignore_case:
333+ re_flags |= re.IGNORECASE
334+
335+ if not fixed_string:
336+ patternc = grep.compile_pattern(pattern, re_flags)
337+
338+ if color == 'always':
339+ show_color = True
340+ elif color == 'never':
341+ show_color = False
342+ elif color == 'auto':
343+ show_color = termcolor.allow_color()
344+
345+ GrepOptions.verbose = verbose
346+ GrepOptions.ignore_case = ignore_case
347+ GrepOptions.no_recursive = no_recursive
348+ GrepOptions.from_root = from_root
349+ GrepOptions.null = null
350+ GrepOptions.levels = levels
351+ GrepOptions.line_number = line_number
352+ GrepOptions.path_list = path_list
353+ GrepOptions.revision = revision
354+ GrepOptions.pattern = pattern
355+ GrepOptions.include = include
356+ GrepOptions.exclude = exclude
357+ GrepOptions.fixed_string = fixed_string
358+ GrepOptions.files_with_matches = files_with_matches
359+ GrepOptions.files_without_match = files_without_match
360+ GrepOptions.color = color
361+ GrepOptions.diff = False
362+
363+ GrepOptions.eol_marker = eol_marker
364+ GrepOptions.print_revno = print_revno
365+ GrepOptions.patternc = patternc
366+ GrepOptions.recursive = not no_recursive
367+ GrepOptions.fixed_string = fixed_string
368+ GrepOptions.outf = self.outf
369+ GrepOptions.show_color = show_color
370+
371+ if diff:
372+ # options not used:
373+ # files_with_matches, files_without_match
374+ # levels(?), line_number, from_root
375+ # include, exclude
376+ # These are silently ignored.
377+ grep.grep_diff(GrepOptions)
378+ elif revision is None:
379+ grep.workingtree_grep(GrepOptions)
380+ else:
381+ grep.versioned_grep(GrepOptions)
382
383=== added file 'bzrlib/plugins/grep/grep.py'
384--- bzrlib/plugins/grep/grep.py 1970-01-01 00:00:00 +0000
385+++ bzrlib/plugins/grep/grep.py 2012-08-03 11:45:28 +0000
386@@ -0,0 +1,728 @@
387+# Copyright (C) 2010 Canonical Ltd
388+#
389+# This program is free software; you can redistribute it and/or modify
390+# it under the terms of the GNU General Public License as published by
391+# the Free Software Foundation; either version 2 of the License, or
392+# (at your option) any later version.
393+#
394+# This program is distributed in the hope that it will be useful,
395+# but WITHOUT ANY WARRANTY; without even the implied warranty of
396+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
397+# GNU General Public License for more details.
398+#
399+# You should have received a copy of the GNU General Public License
400+# along with this program; if not, write to the Free Software
401+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
402+
403+from __future__ import absolute_import
404+
405+import sys
406+
407+from bzrlib.lazy_import import lazy_import
408+lazy_import(globals(), """
409+from fnmatch import fnmatch
410+import re
411+from cStringIO import StringIO
412+
413+from bzrlib.termcolor import color_string, re_color_string, FG
414+
415+from bzrlib.revisionspec import (
416+ RevisionSpec,
417+ RevisionSpec_revid,
418+ RevisionSpec_revno,
419+ )
420+from bzrlib import (
421+ bzrdir,
422+ diff,
423+ errors,
424+ lazy_regex,
425+ osutils,
426+ revision as _mod_revision,
427+ trace,
428+ )
429+""")
430+
431+_user_encoding = osutils.get_user_encoding()
432+
433+
434+class _RevisionNotLinear(Exception):
435+ """Raised when a revision is not on left-hand history."""
436+
437+
438+def _rev_on_mainline(rev_tuple):
439+ """returns True is rev tuple is on mainline"""
440+ if len(rev_tuple) == 1:
441+ return True
442+ return rev_tuple[1] == 0 and rev_tuple[2] == 0
443+
444+
445+# NOTE: _linear_view_revisions is basided on
446+# bzrlib.log._linear_view_revisions.
447+# This should probably be a common public API
448+def _linear_view_revisions(branch, start_rev_id, end_rev_id):
449+ # requires that start is older than end
450+ repo = branch.repository
451+ graph = repo.get_graph()
452+ for revision_id in graph.iter_lefthand_ancestry(
453+ end_rev_id, (_mod_revision.NULL_REVISION, )):
454+ revno = branch.revision_id_to_dotted_revno(revision_id)
455+ revno_str = '.'.join(str(n) for n in revno)
456+ if revision_id == start_rev_id:
457+ yield revision_id, revno_str, 0
458+ break
459+ yield revision_id, revno_str, 0
460+
461+
462+# NOTE: _graph_view_revisions is copied from
463+# bzrlib.log._graph_view_revisions.
464+# This should probably be a common public API
465+def _graph_view_revisions(branch, start_rev_id, end_rev_id,
466+ rebase_initial_depths=True):
467+ """Calculate revisions to view including merges, newest to oldest.
468+
469+ :param branch: the branch
470+ :param start_rev_id: the lower revision-id
471+ :param end_rev_id: the upper revision-id
472+ :param rebase_initial_depth: should depths be rebased until a mainline
473+ revision is found?
474+ :return: An iterator of (revision_id, dotted_revno, merge_depth) tuples.
475+ """
476+ # requires that start is older than end
477+ view_revisions = branch.iter_merge_sorted_revisions(
478+ start_revision_id=end_rev_id, stop_revision_id=start_rev_id,
479+ stop_rule="with-merges")
480+ if not rebase_initial_depths:
481+ for (rev_id, merge_depth, revno, end_of_merge
482+ ) in view_revisions:
483+ yield rev_id, '.'.join(map(str, revno)), merge_depth
484+ else:
485+ # We're following a development line starting at a merged revision.
486+ # We need to adjust depths down by the initial depth until we find
487+ # a depth less than it. Then we use that depth as the adjustment.
488+ # If and when we reach the mainline, depth adjustment ends.
489+ depth_adjustment = None
490+ for (rev_id, merge_depth, revno, end_of_merge
491+ ) in view_revisions:
492+ if depth_adjustment is None:
493+ depth_adjustment = merge_depth
494+ if depth_adjustment:
495+ if merge_depth < depth_adjustment:
496+ # From now on we reduce the depth adjustement, this can be
497+ # surprising for users. The alternative requires two passes
498+ # which breaks the fast display of the first revision
499+ # though.
500+ depth_adjustment = merge_depth
501+ merge_depth -= depth_adjustment
502+ yield rev_id, '.'.join(map(str, revno)), merge_depth
503+
504+
505+def compile_pattern(pattern, flags=0):
506+ patternc = None
507+ try:
508+ # use python's re.compile as we need to catch re.error in case of bad pattern
509+ lazy_regex.reset_compile()
510+ patternc = re.compile(pattern, flags)
511+ except re.error, e:
512+ raise errors.BzrError("Invalid pattern: '%s'" % pattern)
513+ return patternc
514+
515+
516+def is_fixed_string(s):
517+ if re.match("^([A-Za-z0-9_]|\s)*$", s):
518+ return True
519+ return False
520+
521+
522+class _GrepDiffOutputter(object):
523+ """Precalculate formatting based on options given for diff grep.
524+ """
525+
526+ def __init__(self, opts):
527+ self.opts = opts
528+ self.outf = opts.outf
529+ if opts.show_color:
530+ pat = opts.pattern.encode(_user_encoding, 'replace')
531+ if opts.fixed_string:
532+ self._old = pat
533+ self._new = color_string(pat, FG.BOLD_RED)
534+ self.get_writer = self._get_writer_fixed_highlighted
535+ else:
536+ flags = opts.patternc.flags
537+ self._sub = re.compile(pat.join(("((?:",")+)")), flags).sub
538+ self._highlight = color_string("\\1", FG.BOLD_RED)
539+ self.get_writer = self._get_writer_regexp_highlighted
540+ else:
541+ self.get_writer = self._get_writer_plain
542+
543+ def get_file_header_writer(self):
544+ """Get function for writing file headers"""
545+ write = self.outf.write
546+ eol_marker = self.opts.eol_marker
547+ def _line_writer(line):
548+ write(line + eol_marker)
549+ def _line_writer_color(line):
550+ write(FG.BOLD_MAGENTA + line + FG.NONE + eol_marker)
551+ if self.opts.show_color:
552+ return _line_writer_color
553+ else:
554+ return _line_writer
555+ return _line_writer
556+
557+ def get_revision_header_writer(self):
558+ """Get function for writing revno lines"""
559+ write = self.outf.write
560+ eol_marker = self.opts.eol_marker
561+ def _line_writer(line):
562+ write(line + eol_marker)
563+ def _line_writer_color(line):
564+ write(FG.BOLD_BLUE + line + FG.NONE + eol_marker)
565+ if self.opts.show_color:
566+ return _line_writer_color
567+ else:
568+ return _line_writer
569+ return _line_writer
570+
571+ def _get_writer_plain(self):
572+ """Get function for writing uncoloured output"""
573+ write = self.outf.write
574+ eol_marker = self.opts.eol_marker
575+ def _line_writer(line):
576+ write(line + eol_marker)
577+ return _line_writer
578+
579+ def _get_writer_regexp_highlighted(self):
580+ """Get function for writing output with regexp match highlighted"""
581+ _line_writer = self._get_writer_plain()
582+ sub, highlight = self._sub, self._highlight
583+ def _line_writer_regexp_highlighted(line):
584+ """Write formatted line with matched pattern highlighted"""
585+ return _line_writer(line=sub(highlight, line))
586+ return _line_writer_regexp_highlighted
587+
588+ def _get_writer_fixed_highlighted(self):
589+ """Get function for writing output with search string highlighted"""
590+ _line_writer = self._get_writer_plain()
591+ old, new = self._old, self._new
592+ def _line_writer_fixed_highlighted(line):
593+ """Write formatted line with string searched for highlighted"""
594+ return _line_writer(line=line.replace(old, new))
595+ return _line_writer_fixed_highlighted
596+
597+
598+def grep_diff(opts):
599+ wt, branch, relpath = \
600+ bzrdir.BzrDir.open_containing_tree_or_branch('.')
601+ branch.lock_read()
602+ try:
603+ if opts.revision:
604+ start_rev = opts.revision[0]
605+ else:
606+ # if no revision is sepcified for diff grep we grep all changesets.
607+ opts.revision = [RevisionSpec.from_string('revno:1'),
608+ RevisionSpec.from_string('last:1')]
609+ start_rev = opts.revision[0]
610+ start_revid = start_rev.as_revision_id(branch)
611+ if start_revid == 'null:':
612+ return
613+ srevno_tuple = branch.revision_id_to_dotted_revno(start_revid)
614+ if len(opts.revision) == 2:
615+ end_rev = opts.revision[1]
616+ end_revid = end_rev.as_revision_id(branch)
617+ if end_revid is None:
618+ end_revno, end_revid = branch.last_revision_info()
619+ erevno_tuple = branch.revision_id_to_dotted_revno(end_revid)
620+
621+ grep_mainline = (_rev_on_mainline(srevno_tuple) and
622+ _rev_on_mainline(erevno_tuple))
623+
624+ # ensure that we go in reverse order
625+ if srevno_tuple > erevno_tuple:
626+ srevno_tuple, erevno_tuple = erevno_tuple, srevno_tuple
627+ start_revid, end_revid = end_revid, start_revid
628+
629+ # Optimization: Traversing the mainline in reverse order is much
630+ # faster when we don't want to look at merged revs. We try this
631+ # with _linear_view_revisions. If all revs are to be grepped we
632+ # use the slower _graph_view_revisions
633+ if opts.levels==1 and grep_mainline:
634+ given_revs = _linear_view_revisions(branch, start_revid, end_revid)
635+ else:
636+ given_revs = _graph_view_revisions(branch, start_revid, end_revid)
637+ else:
638+ # We do an optimization below. For grepping a specific revison
639+ # We don't need to call _graph_view_revisions which is slow.
640+ # We create the start_rev_tuple for only that specific revision.
641+ # _graph_view_revisions is used only for revision range.
642+ start_revno = '.'.join(map(str, srevno_tuple))
643+ start_rev_tuple = (start_revid, start_revno, 0)
644+ given_revs = [start_rev_tuple]
645+ repo = branch.repository
646+ diff_pattern = re.compile("^[+\-].*(" + opts.pattern + ")")
647+ file_pattern = re.compile("=== (modified|added|removed) file '.*'", re.UNICODE)
648+ outputter = _GrepDiffOutputter(opts)
649+ writeline = outputter.get_writer()
650+ writerevno = outputter.get_revision_header_writer()
651+ writefileheader = outputter.get_file_header_writer()
652+ file_encoding = _user_encoding
653+ for revid, revno, merge_depth in given_revs:
654+ if opts.levels == 1 and merge_depth != 0:
655+ # with level=1 show only top level
656+ continue
657+
658+ rev_spec = RevisionSpec_revid.from_string("revid:"+revid)
659+ new_rev = repo.get_revision(revid)
660+ new_tree = rev_spec.as_tree(branch)
661+ if len(new_rev.parent_ids) == 0:
662+ ancestor_id = _mod_revision.NULL_REVISION
663+ else:
664+ ancestor_id = new_rev.parent_ids[0]
665+ old_tree = repo.revision_tree(ancestor_id)
666+ s = StringIO()
667+ diff.show_diff_trees(old_tree, new_tree, s,
668+ old_label='', new_label='')
669+ display_revno = True
670+ display_file = False
671+ file_header = None
672+ text = s.getvalue()
673+ for line in text.splitlines():
674+ if file_pattern.search(line):
675+ file_header = line
676+ display_file = True
677+ elif diff_pattern.search(line):
678+ if display_revno:
679+ writerevno("=== revno:%s ===" % (revno,))
680+ display_revno = False
681+ if display_file:
682+ writefileheader(" %s" % (file_header,))
683+ display_file = False
684+ line = line.decode(file_encoding, 'replace')
685+ writeline(" %s" % (line,))
686+ finally:
687+ branch.unlock()
688+
689+
690+def versioned_grep(opts):
691+ wt, branch, relpath = \
692+ bzrdir.BzrDir.open_containing_tree_or_branch('.')
693+ branch.lock_read()
694+ try:
695+ start_rev = opts.revision[0]
696+ start_revid = start_rev.as_revision_id(branch)
697+ if start_revid is None:
698+ start_rev = RevisionSpec_revno.from_string("revno:1")
699+ start_revid = start_rev.as_revision_id(branch)
700+ srevno_tuple = branch.revision_id_to_dotted_revno(start_revid)
701+
702+ if len(opts.revision) == 2:
703+ end_rev = opts.revision[1]
704+ end_revid = end_rev.as_revision_id(branch)
705+ if end_revid is None:
706+ end_revno, end_revid = branch.last_revision_info()
707+ erevno_tuple = branch.revision_id_to_dotted_revno(end_revid)
708+
709+ grep_mainline = (_rev_on_mainline(srevno_tuple) and
710+ _rev_on_mainline(erevno_tuple))
711+
712+ # ensure that we go in reverse order
713+ if srevno_tuple > erevno_tuple:
714+ srevno_tuple, erevno_tuple = erevno_tuple, srevno_tuple
715+ start_revid, end_revid = end_revid, start_revid
716+
717+ # Optimization: Traversing the mainline in reverse order is much
718+ # faster when we don't want to look at merged revs. We try this
719+ # with _linear_view_revisions. If all revs are to be grepped we
720+ # use the slower _graph_view_revisions
721+ if opts.levels == 1 and grep_mainline:
722+ given_revs = _linear_view_revisions(branch, start_revid, end_revid)
723+ else:
724+ given_revs = _graph_view_revisions(branch, start_revid, end_revid)
725+ else:
726+ # We do an optimization below. For grepping a specific revison
727+ # We don't need to call _graph_view_revisions which is slow.
728+ # We create the start_rev_tuple for only that specific revision.
729+ # _graph_view_revisions is used only for revision range.
730+ start_revno = '.'.join(map(str, srevno_tuple))
731+ start_rev_tuple = (start_revid, start_revno, 0)
732+ given_revs = [start_rev_tuple]
733+
734+ # GZ 2010-06-02: Shouldn't be smuggling this on opts, but easy for now
735+ opts.outputter = _Outputter(opts, use_cache=True)
736+
737+ for revid, revno, merge_depth in given_revs:
738+ if opts.levels == 1 and merge_depth != 0:
739+ # with level=1 show only top level
740+ continue
741+
742+ rev = RevisionSpec_revid.from_string("revid:"+revid)
743+ tree = rev.as_tree(branch)
744+ for path in opts.path_list:
745+ path_for_id = osutils.pathjoin(relpath, path)
746+ id = tree.path2id(path_for_id)
747+ if not id:
748+ trace.warning("Skipped unknown file '%s'." % path)
749+ continue
750+
751+ if osutils.isdir(path):
752+ path_prefix = path
753+ dir_grep(tree, path, relpath, opts, revno, path_prefix)
754+ else:
755+ versioned_file_grep(tree, id, '.', path, opts, revno)
756+ finally:
757+ branch.unlock()
758+
759+
760+def workingtree_grep(opts):
761+ revno = opts.print_revno = None # for working tree set revno to None
762+
763+ tree, branch, relpath = \
764+ bzrdir.BzrDir.open_containing_tree_or_branch('.')
765+ if not tree:
766+ msg = ('Cannot search working tree. Working tree not found.\n'
767+ 'To search for specific revision in history use the -r option.')
768+ raise errors.BzrCommandError(msg)
769+
770+ # GZ 2010-06-02: Shouldn't be smuggling this on opts, but easy for now
771+ opts.outputter = _Outputter(opts)
772+
773+ tree.lock_read()
774+ try:
775+ for path in opts.path_list:
776+ if osutils.isdir(path):
777+ path_prefix = path
778+ dir_grep(tree, path, relpath, opts, revno, path_prefix)
779+ else:
780+ _file_grep(open(path).read(), path, opts, revno)
781+ finally:
782+ tree.unlock()
783+
784+
785+def _skip_file(include, exclude, path):
786+ if include and not _path_in_glob_list(path, include):
787+ return True
788+ if exclude and _path_in_glob_list(path, exclude):
789+ return True
790+ return False
791+
792+
793+def dir_grep(tree, path, relpath, opts, revno, path_prefix):
794+ # setup relpath to open files relative to cwd
795+ rpath = relpath
796+ if relpath:
797+ rpath = osutils.pathjoin('..',relpath)
798+
799+ from_dir = osutils.pathjoin(relpath, path)
800+ if opts.from_root:
801+ # start searching recursively from root
802+ from_dir=None
803+ recursive=True
804+
805+ to_grep = []
806+ to_grep_append = to_grep.append
807+ # GZ 2010-06-05: The cache dict used to be recycled every call to dir_grep
808+ # and hits manually refilled. Could do this again if it was
809+ # for a good reason, otherwise cache might want purging.
810+ outputter = opts.outputter
811+ for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
812+ from_dir=from_dir, recursive=opts.recursive):
813+
814+ if _skip_file(opts.include, opts.exclude, fp):
815+ continue
816+
817+ if fc == 'V' and fkind == 'file':
818+ if revno != None:
819+ # If old result is valid, print results immediately.
820+ # Otherwise, add file info to to_grep so that the
821+ # loop later will get chunks and grep them
822+ cache_id = tree.get_file_revision(fid)
823+ if cache_id in outputter.cache:
824+ # GZ 2010-06-05: Not really sure caching and re-outputting
825+ # the old path is really the right thing,
826+ # but it's what the old code seemed to do
827+ outputter.write_cached_lines(cache_id, revno)
828+ else:
829+ to_grep_append((fid, (fp, fid)))
830+ else:
831+ # we are grepping working tree.
832+ if from_dir is None:
833+ from_dir = '.'
834+
835+ path_for_file = osutils.pathjoin(tree.basedir, from_dir, fp)
836+ if opts.files_with_matches or opts.files_without_match:
837+ # Optimize for wtree list-only as we don't need to read the
838+ # entire file
839+ file = open(path_for_file, 'r', buffering=4096)
840+ _file_grep_list_only_wtree(file, fp, opts, path_prefix)
841+ else:
842+ file_text = open(path_for_file, 'r').read()
843+ _file_grep(file_text, fp, opts, revno, path_prefix)
844+
845+ if revno != None: # grep versioned files
846+ for (path, fid), chunks in tree.iter_files_bytes(to_grep):
847+ path = _make_display_path(relpath, path)
848+ _file_grep(chunks[0], path, opts, revno, path_prefix,
849+ tree.get_file_revision(fid, path))
850+
851+
852+def _make_display_path(relpath, path):
853+ """Return path string relative to user cwd.
854+
855+ Take tree's 'relpath' and user supplied 'path', and return path
856+ that can be displayed to the user.
857+ """
858+ if relpath:
859+ # update path so to display it w.r.t cwd
860+ # handle windows slash separator
861+ path = osutils.normpath(osutils.pathjoin(relpath, path))
862+ path = path.replace('\\', '/')
863+ path = path.replace(relpath + '/', '', 1)
864+ return path
865+
866+
867+def versioned_file_grep(tree, id, relpath, path, opts, revno, path_prefix = None):
868+ """Create a file object for the specified id and pass it on to _file_grep.
869+ """
870+
871+ path = _make_display_path(relpath, path)
872+ file_text = tree.get_file_text(id)
873+ _file_grep(file_text, path, opts, revno, path_prefix)
874+
875+
876+def _path_in_glob_list(path, glob_list):
877+ for glob in glob_list:
878+ if fnmatch(path, glob):
879+ return True
880+ return False
881+
882+
883+def _file_grep_list_only_wtree(file, path, opts, path_prefix=None):
884+ # test and skip binary files
885+ if '\x00' in file.read(1024):
886+ if opts.verbose:
887+ trace.warning("Binary file '%s' skipped." % path)
888+ return
889+
890+ file.seek(0) # search from beginning
891+
892+ found = False
893+ if opts.fixed_string:
894+ pattern = opts.pattern.encode(_user_encoding, 'replace')
895+ for line in file:
896+ if pattern in line:
897+ found = True
898+ break
899+ else: # not fixed_string
900+ for line in file:
901+ if opts.patternc.search(line):
902+ found = True
903+ break
904+
905+ if (opts.files_with_matches and found) or \
906+ (opts.files_without_match and not found):
907+ if path_prefix and path_prefix != '.':
908+ # user has passed a dir arg, show that as result prefix
909+ path = osutils.pathjoin(path_prefix, path)
910+ opts.outputter.get_writer(path, None, None)()
911+
912+
913+class _Outputter(object):
914+ """Precalculate formatting based on options given
915+
916+ The idea here is to do this work only once per run, and finally return a
917+ function that will do the minimum amount possible for each match.
918+ """
919+ def __init__(self, opts, use_cache=False):
920+ self.outf = opts.outf
921+ if use_cache:
922+ # self.cache is used to cache results for dir grep based on fid.
923+ # If the fid is does not change between results, it means that
924+ # the result will be the same apart from revno. In such a case
925+ # we avoid getting file chunks from repo and grepping. The result
926+ # is just printed by replacing old revno with new one.
927+ self.cache = {}
928+ else:
929+ self.cache = None
930+ no_line = opts.files_with_matches or opts.files_without_match
931+
932+ if opts.show_color:
933+ pat = opts.pattern.encode(_user_encoding, 'replace')
934+ if no_line:
935+ self.get_writer = self._get_writer_plain
936+ elif opts.fixed_string:
937+ self._old = pat
938+ self._new = color_string(pat, FG.BOLD_RED)
939+ self.get_writer = self._get_writer_fixed_highlighted
940+ else:
941+ flags = opts.patternc.flags
942+ self._sub = re.compile(pat.join(("((?:",")+)")), flags).sub
943+ self._highlight = color_string("\\1", FG.BOLD_RED)
944+ self.get_writer = self._get_writer_regexp_highlighted
945+ path_start = FG.MAGENTA
946+ path_end = FG.NONE
947+ sep = color_string(':', FG.BOLD_CYAN)
948+ rev_sep = color_string('~', FG.BOLD_YELLOW)
949+ else:
950+ self.get_writer = self._get_writer_plain
951+ path_start = path_end = ""
952+ sep = ":"
953+ rev_sep = "~"
954+
955+ parts = [path_start, "%(path)s"]
956+ if opts.print_revno:
957+ parts.extend([rev_sep, "%(revno)s"])
958+ self._format_initial = "".join(parts)
959+ parts = []
960+ if no_line:
961+ if not opts.print_revno:
962+ parts.append(path_end)
963+ else:
964+ if opts.line_number:
965+ parts.extend([sep, "%(lineno)s"])
966+ parts.extend([sep, "%(line)s"])
967+ parts.append(opts.eol_marker)
968+ self._format_perline = "".join(parts)
969+
970+ def _get_writer_plain(self, path, revno, cache_id):
971+ """Get function for writing uncoloured output"""
972+ per_line = self._format_perline
973+ start = self._format_initial % {"path":path, "revno":revno}
974+ write = self.outf.write
975+ if self.cache is not None and cache_id is not None:
976+ result_list = []
977+ self.cache[cache_id] = path, result_list
978+ add_to_cache = result_list.append
979+ def _line_cache_and_writer(**kwargs):
980+ """Write formatted line and cache arguments"""
981+ end = per_line % kwargs
982+ add_to_cache(end)
983+ write(start + end)
984+ return _line_cache_and_writer
985+ def _line_writer(**kwargs):
986+ """Write formatted line from arguments given by underlying opts"""
987+ write(start + per_line % kwargs)
988+ return _line_writer
989+
990+ def write_cached_lines(self, cache_id, revno):
991+ """Write cached results out again for new revision"""
992+ cached_path, cached_matches = self.cache[cache_id]
993+ start = self._format_initial % {"path":cached_path, "revno":revno}
994+ write = self.outf.write
995+ for end in cached_matches:
996+ write(start + end)
997+
998+ def _get_writer_regexp_highlighted(self, path, revno, cache_id):
999+ """Get function for writing output with regexp match highlighted"""
1000+ _line_writer = self._get_writer_plain(path, revno, cache_id)
1001+ sub, highlight = self._sub, self._highlight
1002+ def _line_writer_regexp_highlighted(line, **kwargs):
1003+ """Write formatted line with matched pattern highlighted"""
1004+ return _line_writer(line=sub(highlight, line), **kwargs)
1005+ return _line_writer_regexp_highlighted
1006+
1007+ def _get_writer_fixed_highlighted(self, path, revno, cache_id):
1008+ """Get function for writing output with search string highlighted"""
1009+ _line_writer = self._get_writer_plain(path, revno, cache_id)
1010+ old, new = self._old, self._new
1011+ def _line_writer_fixed_highlighted(line, **kwargs):
1012+ """Write formatted line with string searched for highlighted"""
1013+ return _line_writer(line=line.replace(old, new), **kwargs)
1014+ return _line_writer_fixed_highlighted
1015+
1016+
1017+def _file_grep(file_text, path, opts, revno, path_prefix=None, cache_id=None):
1018+ # test and skip binary files
1019+ if '\x00' in file_text[:1024]:
1020+ if opts.verbose:
1021+ trace.warning("Binary file '%s' skipped." % path)
1022+ return
1023+
1024+ if path_prefix and path_prefix != '.':
1025+ # user has passed a dir arg, show that as result prefix
1026+ path = osutils.pathjoin(path_prefix, path)
1027+
1028+ # GZ 2010-06-07: There's no actual guarentee the file contents will be in
1029+ # the user encoding, but we have to guess something and it
1030+ # is a reasonable default without a better mechanism.
1031+ file_encoding = _user_encoding
1032+ pattern = opts.pattern.encode(_user_encoding, 'replace')
1033+
1034+ writeline = opts.outputter.get_writer(path, revno, cache_id)
1035+
1036+ if opts.files_with_matches or opts.files_without_match:
1037+ if opts.fixed_string:
1038+ if sys.platform > (2, 5):
1039+ found = pattern in file_text
1040+ else:
1041+ for line in file_text.splitlines():
1042+ if pattern in line:
1043+ found = True
1044+ break
1045+ else:
1046+ found = False
1047+ else:
1048+ search = opts.patternc.search
1049+ if "$" not in pattern:
1050+ found = search(file_text) is not None
1051+ else:
1052+ for line in file_text.splitlines():
1053+ if search(line):
1054+ found = True
1055+ break
1056+ else:
1057+ found = False
1058+ if (opts.files_with_matches and found) or \
1059+ (opts.files_without_match and not found):
1060+ writeline()
1061+ elif opts.fixed_string:
1062+ # Fast path for no match, search through the entire file at once rather
1063+ # than a line at a time. However, we don't want this without Python 2.5
1064+ # as the quick string search algorithm wasn't implemented till then:
1065+ # <http://effbot.org/zone/stringlib.htm>
1066+ if sys.version_info > (2, 5):
1067+ i = file_text.find(pattern)
1068+ if i == -1:
1069+ return
1070+ b = file_text.rfind("\n", 0, i) + 1
1071+ if opts.line_number:
1072+ start = file_text.count("\n", 0, b) + 1
1073+ file_text = file_text[b:]
1074+ else:
1075+ start = 1
1076+ if opts.line_number:
1077+ for index, line in enumerate(file_text.splitlines()):
1078+ if pattern in line:
1079+ line = line.decode(file_encoding, 'replace')
1080+ writeline(lineno=index+start, line=line)
1081+ else:
1082+ for line in file_text.splitlines():
1083+ if pattern in line:
1084+ line = line.decode(file_encoding, 'replace')
1085+ writeline(line=line)
1086+ else:
1087+ # Fast path on no match, the re module avoids bad behaviour in most
1088+ # standard cases, but perhaps could try and detect backtracking
1089+ # patterns here and avoid whole text search in those cases
1090+ search = opts.patternc.search
1091+ if "$" not in pattern:
1092+ # GZ 2010-06-05: Grr, re.MULTILINE can't save us when searching
1093+ # through revisions as bazaar returns binary mode
1094+ # and trailing \r breaks $ as line ending match
1095+ m = search(file_text)
1096+ if m is None:
1097+ return
1098+ b = file_text.rfind("\n", 0, m.start()) + 1
1099+ if opts.line_number:
1100+ start = file_text.count("\n", 0, b) + 1
1101+ file_text = file_text[b:]
1102+ else:
1103+ start = 1
1104+ if opts.line_number:
1105+ for index, line in enumerate(file_text.splitlines()):
1106+ if search(line):
1107+ line = line.decode(file_encoding, 'replace')
1108+ writeline(lineno=index+start, line=line)
1109+ else:
1110+ for line in file_text.splitlines():
1111+ if search(line):
1112+ line = line.decode(file_encoding, 'replace')
1113+ writeline(line=line)
1114+
1115
1116=== added file 'bzrlib/plugins/grep/test_grep.py'
1117--- bzrlib/plugins/grep/test_grep.py 1970-01-01 00:00:00 +0000
1118+++ bzrlib/plugins/grep/test_grep.py 2012-08-03 11:45:28 +0000
1119@@ -0,0 +1,2254 @@
1120+# Copyright (C) 2010 Canonical Ltd
1121+#
1122+# This program is free software; you can redistribute it and/or modify
1123+# it under the terms of the GNU General Public License as published by
1124+# the Free Software Foundation; either version 2 of the License, or
1125+# (at your option) any later version.
1126+#
1127+# This program is distributed in the hope that it will be useful,
1128+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1129+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1130+# GNU General Public License for more details.
1131+#
1132+# You should have received a copy of the GNU General Public License
1133+# along with this program; if not, write to the Free Software
1134+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1135+
1136+from __future__ import absolute_import
1137+
1138+import os
1139+import re
1140+import unicodedata as ud
1141+
1142+from bzrlib import tests, osutils
1143+from bzrlib.termcolor import color_string, FG
1144+
1145+from bzrlib.tests.features import (
1146+ ColorFeature,
1147+ UnicodeFilenameFeature,
1148+ )
1149+
1150+# NOTE: As bzr-grep optimizes PATTERN search to -F/--fixed-string
1151+# for patterns that are not alphanumeric+whitespace, we test grep
1152+# specfically with patterns that have special characters so that
1153+# regex path is tested. alphanumeric patterns test the -F path.
1154+
1155+class GrepTestBase(tests.TestCaseWithTransport):
1156+ """Base class for testing grep.
1157+
1158+ Provides support methods for creating directory and file revisions.
1159+ """
1160+ _reflags = re.MULTILINE|re.DOTALL
1161+
1162+ def _mk_file(self, path, line_prefix, total_lines, versioned):
1163+ text=''
1164+ for i in range(total_lines):
1165+ text += line_prefix + str(i+1) + "\n"
1166+
1167+ open(path, 'w').write(text)
1168+ if versioned:
1169+ self.run_bzr(['add', path])
1170+ self.run_bzr(['ci', '-m', '"' + path + '"'])
1171+
1172+ def _update_file(self, path, text, checkin=True):
1173+ """append text to file 'path' and check it in"""
1174+ open(path, 'a').write(text)
1175+ if checkin:
1176+ self.run_bzr(['ci', path, '-m', '"' + path + '"'])
1177+
1178+ def _mk_unknown_file(self, path, line_prefix='line', total_lines=10):
1179+ self._mk_file(path, line_prefix, total_lines, versioned=False)
1180+
1181+ def _mk_versioned_file(self, path, line_prefix='line', total_lines=10):
1182+ self._mk_file(path, line_prefix, total_lines, versioned=True)
1183+
1184+ def _mk_dir(self, path, versioned):
1185+ os.mkdir(path)
1186+ if versioned:
1187+ self.run_bzr(['add', path])
1188+ self.run_bzr(['ci', '-m', '"' + path + '"'])
1189+
1190+ def _mk_unknown_dir(self, path):
1191+ self._mk_dir(path, versioned=False)
1192+
1193+ def _mk_versioned_dir(self, path):
1194+ self._mk_dir(path, versioned=True)
1195+
1196+
1197+class TestGrep(GrepTestBase):
1198+ """Core functional tests for grep."""
1199+
1200+ def test_basic_unknown_file(self):
1201+ """Search for pattern in specfic file.
1202+
1203+ If specified file is unknown, grep it anyway."""
1204+ wd = 'foobar0'
1205+ self.make_branch_and_tree(wd)
1206+ os.chdir(wd)
1207+ self._mk_unknown_file('file0.txt')
1208+
1209+ out, err = self.run_bzr(['grep', 'line1', 'file0.txt'])
1210+ self.assertContainsRe(out, "file0.txt:line1", flags=TestGrep._reflags)
1211+ self.assertEqual(len(out.splitlines()), 2) # finds line1 and line10
1212+
1213+ out, err = self.run_bzr(['grep', 'line\d+', 'file0.txt'])
1214+ self.assertContainsRe(out, "file0.txt:line1", flags=TestGrep._reflags)
1215+ self.assertEqual(len(out.splitlines()), 10)
1216+
1217+ # unknown file is not grepped unless explicitely specified
1218+ out, err = self.run_bzr(['grep', 'line1'])
1219+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
1220+ self.assertEqual(len(out.splitlines()), 0)
1221+
1222+ # unknown file is not grepped unless explicitely specified
1223+ out, err = self.run_bzr(['grep', 'line1$'])
1224+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
1225+ self.assertEqual(len(out.splitlines()), 0)
1226+
1227+ def test_ver_basic_file(self):
1228+ """(versioned) Search for pattern in specfic file.
1229+ """
1230+ wd = 'foobar0'
1231+ self.make_branch_and_tree(wd)
1232+ os.chdir(wd)
1233+ self._mk_versioned_file('file0.txt')
1234+
1235+ out, err = self.run_bzr(['grep', '-r', '1', 'line1', 'file0.txt'])
1236+ self.assertContainsRe(out, "file0.txt~1:line1", flags=TestGrep._reflags)
1237+ self.assertEqual(len(out.splitlines()), 2) # finds line1 and line10
1238+
1239+ out, err = self.run_bzr(['grep', '-r', '1', 'line[0-9]$', 'file0.txt'])
1240+ self.assertContainsRe(out, "file0.txt~1:line1", flags=TestGrep._reflags)
1241+ self.assertEqual(len(out.splitlines()), 9)
1242+
1243+ # finds all the lines
1244+ out, err = self.run_bzr(['grep', '-r', '1', 'line[0-9]', 'file0.txt'])
1245+ self.assertContainsRe(out, "file0.txt~1:line1", flags=TestGrep._reflags)
1246+ self.assertEqual(len(out.splitlines()), 10)
1247+
1248+ def test_wtree_basic_file(self):
1249+ """(wtree) Search for pattern in specfic file.
1250+ """
1251+ wd = 'foobar0'
1252+ self.make_branch_and_tree(wd)
1253+ os.chdir(wd)
1254+ self._mk_versioned_file('file0.txt')
1255+ self._update_file('file0.txt', 'ABC\n', checkin=False)
1256+
1257+ out, err = self.run_bzr(['grep', 'ABC', 'file0.txt'])
1258+ self.assertContainsRe(out, "file0.txt:ABC", flags=TestGrep._reflags)
1259+ self.assertEqual(len(out.splitlines()), 1)
1260+
1261+ out, err = self.run_bzr(['grep', '[A-Z]{3}', 'file0.txt'])
1262+ self.assertContainsRe(out, "file0.txt:ABC", flags=TestGrep._reflags)
1263+ self.assertEqual(len(out.splitlines()), 1)
1264+
1265+ out, err = self.run_bzr(['grep', '-r', 'last:1', 'ABC', 'file0.txt'])
1266+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
1267+ self.assertEqual(len(out.splitlines()), 0)
1268+
1269+ out, err = self.run_bzr(['grep', '-r', 'last:1', '[A-Z]{3}', 'file0.txt'])
1270+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
1271+ self.assertEqual(len(out.splitlines()), 0)
1272+
1273+ def test_ver_basic_include(self):
1274+ """(versioned) Ensure that -I flag is respected.
1275+ """
1276+ wd = 'foobar0'
1277+ self.make_branch_and_tree(wd)
1278+ os.chdir(wd)
1279+ self._mk_versioned_file('file0.aa')
1280+ self._mk_versioned_file('file0.bb')
1281+ self._mk_versioned_file('file0.cc')
1282+
1283+ out, err = self.run_bzr(['grep', '-r', 'last:1',
1284+ '--include', '*.aa', '--include', '*.bb', 'line1'])
1285+ self.assertContainsRe(out, "file0.aa~.:line1", flags=TestGrep._reflags)
1286+ self.assertContainsRe(out, "file0.bb~.:line1", flags=TestGrep._reflags)
1287+ self.assertNotContainsRe(out, "file0.cc", flags=TestGrep._reflags)
1288+ # two lines each (line1, line10) from file0.aa and file0.bb
1289+ self.assertEqual(len(out.splitlines()), 4)
1290+
1291+ out, err = self.run_bzr(['grep', '-r', 'last:1',
1292+ '--include', '*.aa', '--include', '*.bb', 'line1$'])
1293+ self.assertContainsRe(out, "file0.aa~.:line1", flags=TestGrep._reflags)
1294+ self.assertContainsRe(out, "file0.bb~.:line1", flags=TestGrep._reflags)
1295+ self.assertNotContainsRe(out, "file0.cc", flags=TestGrep._reflags)
1296+ # one lines each (line1) from file0.aa and file0.bb
1297+ self.assertEqual(len(out.splitlines()), 2)
1298+
1299+ out, err = self.run_bzr(['grep', '-r', 'last:1',
1300+ '-I', '*.aa', '-I', '*.bb', 'line1'])
1301+ self.assertContainsRe(out, "file0.aa~.:line1", flags=TestGrep._reflags)
1302+ self.assertContainsRe(out, "file0.bb~.:line1", flags=TestGrep._reflags)
1303+ self.assertNotContainsRe(out, "file0.cc", flags=TestGrep._reflags)
1304+ # two lines each (line1, line10) from file0.aa and file0.bb
1305+ self.assertEqual(len(out.splitlines()), 4)
1306+
1307+ out, err = self.run_bzr(['grep', '-r', 'last:1',
1308+ '-I', '*.aa', '-I', '*.bb', 'line1$'])
1309+ self.assertContainsRe(out, "file0.aa~.:line1", flags=TestGrep._reflags)
1310+ self.assertContainsRe(out, "file0.bb~.:line1", flags=TestGrep._reflags)
1311+ self.assertNotContainsRe(out, "file0.cc", flags=TestGrep._reflags)
1312+ # one lines each (line1) from file0.aa and file0.bb
1313+ self.assertEqual(len(out.splitlines()), 2)
1314+
1315+ def test_wtree_basic_include(self):
1316+ """(wtree) Ensure that --include flag is respected.
1317+ """
1318+ wd = 'foobar0'
1319+ self.make_branch_and_tree(wd)
1320+ os.chdir(wd)
1321+ self._mk_versioned_file('file0.aa')
1322+ self._mk_versioned_file('file0.bb')
1323+ self._mk_versioned_file('file0.cc')
1324+
1325+ out, err = self.run_bzr(['grep', '--include', '*.aa',
1326+ '--include', '*.bb', 'line1'])
1327+ self.assertContainsRe(out, "file0.aa:line1", flags=TestGrep._reflags)
1328+ self.assertContainsRe(out, "file0.bb:line1", flags=TestGrep._reflags)
1329+ self.assertNotContainsRe(out, "file0.cc", flags=TestGrep._reflags)
1330+ # two lines each (line1, line10) from file0.aa and file0.bb
1331+ self.assertEqual(len(out.splitlines()), 4)
1332+
1333+ out, err = self.run_bzr(['grep', '--include', '*.aa',
1334+ '--include', '*.bb', 'line1$'])
1335+ self.assertContainsRe(out, "file0.aa:line1", flags=TestGrep._reflags)
1336+ self.assertContainsRe(out, "file0.bb:line1", flags=TestGrep._reflags)
1337+ self.assertNotContainsRe(out, "file0.cc", flags=TestGrep._reflags)
1338+ # one line each (line1) from file0.aa and file0.bb
1339+ self.assertEqual(len(out.splitlines()), 2)
1340+
1341+ def test_ver_basic_exclude(self):
1342+ """(versioned) Ensure that --exclude flag is respected.
1343+ """
1344+ wd = 'foobar0'
1345+ self.make_branch_and_tree(wd)
1346+ os.chdir(wd)
1347+ self._mk_versioned_file('file0.aa')
1348+ self._mk_versioned_file('file0.bb')
1349+ self._mk_versioned_file('file0.cc')
1350+
1351+ out, err = self.run_bzr(['grep', '-r', 'last:1',
1352+ '--exclude', '*.cc', 'line1'])
1353+ self.assertContainsRe(out, "file0.aa~.:line1$", flags=TestGrep._reflags)
1354+ self.assertContainsRe(out, "file0.bb~.:line1$", flags=TestGrep._reflags)
1355+ self.assertContainsRe(out, "file0.aa~.:line10", flags=TestGrep._reflags)
1356+ self.assertContainsRe(out, "file0.bb~.:line10", flags=TestGrep._reflags)
1357+ self.assertNotContainsRe(out, "file0.cc", flags=TestGrep._reflags)
1358+ # two lines each (line1, line10) from file0.aa and file0.bb
1359+ self.assertEqual(len(out.splitlines()), 4)
1360+
1361+ out, err = self.run_bzr(['grep', '-r', 'last:1',
1362+ '--exclude', '*.cc', 'line1$'])
1363+ self.assertContainsRe(out, "file0.aa~.:line1", flags=TestGrep._reflags)
1364+ self.assertContainsRe(out, "file0.bb~.:line1", flags=TestGrep._reflags)
1365+ self.assertNotContainsRe(out, "file0.cc", flags=TestGrep._reflags)
1366+ # one line each (line1) from file0.aa and file0.bb
1367+ self.assertEqual(len(out.splitlines()), 2)
1368+
1369+ out, err = self.run_bzr(['grep', '-r', 'last:1',
1370+ '-X', '*.cc', 'line1'])
1371+ self.assertContainsRe(out, "file0.aa~.:line1", flags=TestGrep._reflags)
1372+ self.assertContainsRe(out, "file0.bb~.:line1", flags=TestGrep._reflags)
1373+ self.assertNotContainsRe(out, "file0.cc", flags=TestGrep._reflags)
1374+ # two lines each (line1, line10) from file0.aa and file0.bb
1375+ self.assertEqual(len(out.splitlines()), 4)
1376+
1377+ def test_wtree_basic_exclude(self):
1378+ """(wtree) Ensure that --exclude flag is respected.
1379+ """
1380+ wd = 'foobar0'
1381+ self.make_branch_and_tree(wd)
1382+ os.chdir(wd)
1383+ self._mk_versioned_file('file0.aa')
1384+ self._mk_versioned_file('file0.bb')
1385+ self._mk_versioned_file('file0.cc')
1386+
1387+ out, err = self.run_bzr(['grep', '--exclude', '*.cc', 'line1'])
1388+ self.assertContainsRe(out, "file0.aa:line1", flags=TestGrep._reflags)
1389+ self.assertContainsRe(out, "file0.bb:line1", flags=TestGrep._reflags)
1390+ self.assertNotContainsRe(out, "file0.cc", flags=TestGrep._reflags)
1391+ # two lines each (line1, line10) from file0.aa and file0.bb
1392+ self.assertEqual(len(out.splitlines()), 4)
1393+
1394+ out, err = self.run_bzr(['grep', '--exclude', '*.cc', 'lin.1$'])
1395+ self.assertContainsRe(out, "file0.aa:line1", flags=TestGrep._reflags)
1396+ self.assertContainsRe(out, "file0.bb:line1", flags=TestGrep._reflags)
1397+ self.assertNotContainsRe(out, "file0.cc", flags=TestGrep._reflags)
1398+ # one line each (line1) from file0.aa and file0.bb
1399+ self.assertEqual(len(out.splitlines()), 2)
1400+
1401+ def test_ver_multiple_files(self):
1402+ """(versioned) Search for pattern in multiple files.
1403+ """
1404+ wd = 'foobar0'
1405+ self.make_branch_and_tree(wd)
1406+ os.chdir(wd)
1407+ self._mk_versioned_file('file0.txt', total_lines=2)
1408+ self._mk_versioned_file('file1.txt', total_lines=2)
1409+ self._mk_versioned_file('file2.txt', total_lines=2)
1410+
1411+ out, err = self.run_bzr(['grep', '-r', 'last:1', 'line[1-2]$'])
1412+ self.assertContainsRe(out, "file0.txt~.:line1", flags=TestGrep._reflags)
1413+ self.assertContainsRe(out, "file0.txt~.:line2", flags=TestGrep._reflags)
1414+ self.assertContainsRe(out, "file1.txt~.:line1", flags=TestGrep._reflags)
1415+ self.assertContainsRe(out, "file1.txt~.:line2", flags=TestGrep._reflags)
1416+ self.assertContainsRe(out, "file2.txt~.:line1", flags=TestGrep._reflags)
1417+ self.assertContainsRe(out, "file2.txt~.:line2", flags=TestGrep._reflags)
1418+ self.assertEqual(len(out.splitlines()), 6)
1419+
1420+ out, err = self.run_bzr(['grep', '-r', 'last:1', 'line'])
1421+ self.assertContainsRe(out, "file0.txt~.:line1", flags=TestGrep._reflags)
1422+ self.assertContainsRe(out, "file0.txt~.:line2", flags=TestGrep._reflags)
1423+ self.assertContainsRe(out, "file1.txt~.:line1", flags=TestGrep._reflags)
1424+ self.assertContainsRe(out, "file1.txt~.:line2", flags=TestGrep._reflags)
1425+ self.assertContainsRe(out, "file2.txt~.:line1", flags=TestGrep._reflags)
1426+ self.assertContainsRe(out, "file2.txt~.:line2", flags=TestGrep._reflags)
1427+ self.assertEqual(len(out.splitlines()), 6)
1428+
1429+ def test_multiple_wtree_files(self):
1430+ """(wtree) Search for pattern in multiple files in working tree.
1431+ """
1432+ wd = 'foobar0'
1433+ self.make_branch_and_tree(wd)
1434+ os.chdir(wd)
1435+ self._mk_versioned_file('file0.txt', total_lines=2)
1436+ self._mk_versioned_file('file1.txt', total_lines=2)
1437+ self._mk_versioned_file('file2.txt', total_lines=2)
1438+ self._update_file('file0.txt', 'HELLO\n', checkin=False)
1439+ self._update_file('file1.txt', 'HELLO\n', checkin=True)
1440+ self._update_file('file2.txt', 'HELLO\n', checkin=False)
1441+
1442+ out, err = self.run_bzr(['grep', 'HELLO',
1443+ 'file0.txt', 'file1.txt', 'file2.txt'])
1444+
1445+ self.assertContainsRe(out, "file0.txt:HELLO", flags=TestGrep._reflags)
1446+ self.assertContainsRe(out, "file1.txt:HELLO", flags=TestGrep._reflags)
1447+ self.assertContainsRe(out, "file2.txt:HELLO", flags=TestGrep._reflags)
1448+ self.assertEqual(len(out.splitlines()), 3)
1449+
1450+ out, err = self.run_bzr(['grep', 'HELLO', '-r', 'last:1',
1451+ 'file0.txt', 'file1.txt', 'file2.txt'])
1452+
1453+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
1454+ self.assertContainsRe(out, "file1.txt~.:HELLO", flags=TestGrep._reflags)
1455+ self.assertNotContainsRe(out, "file2.txt", flags=TestGrep._reflags)
1456+ self.assertEqual(len(out.splitlines()), 1)
1457+
1458+ out, err = self.run_bzr(['grep', 'HE..O',
1459+ 'file0.txt', 'file1.txt', 'file2.txt'])
1460+
1461+ self.assertContainsRe(out, "file0.txt:HELLO", flags=TestGrep._reflags)
1462+ self.assertContainsRe(out, "file1.txt:HELLO", flags=TestGrep._reflags)
1463+ self.assertContainsRe(out, "file2.txt:HELLO", flags=TestGrep._reflags)
1464+ self.assertEqual(len(out.splitlines()), 3)
1465+
1466+ out, err = self.run_bzr(['grep', 'HE..O', '-r', 'last:1',
1467+ 'file0.txt', 'file1.txt', 'file2.txt'])
1468+
1469+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
1470+ self.assertContainsRe(out, "file1.txt~.:HELLO", flags=TestGrep._reflags)
1471+ self.assertNotContainsRe(out, "file2.txt", flags=TestGrep._reflags)
1472+ self.assertEqual(len(out.splitlines()), 1)
1473+
1474+ def test_ver_null_option(self):
1475+ """(versioned) --null option should use NUL instead of newline.
1476+ """
1477+ wd = 'foobar0'
1478+ self.make_branch_and_tree(wd)
1479+ os.chdir(wd)
1480+ self._mk_versioned_file('file0.txt', total_lines=3)
1481+
1482+ nref = ud.normalize(u'NFC', u"file0.txt~1:line1\0file0.txt~1:line2\0file0.txt~1:line3\0")
1483+
1484+ out, err = self.run_bzr(['grep', '-r', 'last:1', '--null', 'line[1-3]'])
1485+ nout = ud.normalize(u'NFC', out.decode('utf-8', 'ignore'))
1486+ self.assertEqual(nout, nref)
1487+ self.assertEqual(len(out.splitlines()), 1)
1488+
1489+ out, err = self.run_bzr(['grep', '-r', 'last:1', '-Z', 'line[1-3]'])
1490+ nout = ud.normalize(u'NFC', out.decode('utf-8', 'ignore'))
1491+ self.assertEqual(nout, nref)
1492+ self.assertEqual(len(out.splitlines()), 1)
1493+
1494+ out, err = self.run_bzr(['grep', '-r', 'last:1', '--null', 'line'])
1495+ nout = ud.normalize(u'NFC', out.decode('utf-8', 'ignore'))
1496+ self.assertEqual(nout, nref)
1497+ self.assertEqual(len(out.splitlines()), 1)
1498+
1499+ def test_wtree_null_option(self):
1500+ """(wtree) --null option should use NUL instead of newline.
1501+ """
1502+ wd = 'foobar0'
1503+ self.make_branch_and_tree(wd)
1504+ os.chdir(wd)
1505+ self._mk_versioned_file('file0.txt', total_lines=3)
1506+
1507+ out, err = self.run_bzr(['grep', '--null', 'line[1-3]'])
1508+ self.assertEqual(out, "file0.txt:line1\0file0.txt:line2\0file0.txt:line3\0")
1509+ self.assertEqual(len(out.splitlines()), 1)
1510+
1511+ out, err = self.run_bzr(['grep', '-Z', 'line[1-3]'])
1512+ self.assertEqual(out, "file0.txt:line1\0file0.txt:line2\0file0.txt:line3\0")
1513+ self.assertEqual(len(out.splitlines()), 1)
1514+
1515+ out, err = self.run_bzr(['grep', '-Z', 'line'])
1516+ self.assertEqual(out, "file0.txt:line1\0file0.txt:line2\0file0.txt:line3\0")
1517+ self.assertEqual(len(out.splitlines()), 1)
1518+
1519+ def test_versioned_file_in_dir_no_recursive(self):
1520+ """(versioned) Should not recurse with --no-recursive"""
1521+ wd = 'foobar0'
1522+ self.make_branch_and_tree(wd)
1523+ os.chdir(wd)
1524+ self._mk_versioned_file('fileX.txt', line_prefix='lin')
1525+ self._mk_versioned_dir('dir0')
1526+ self._mk_versioned_file('dir0/file0.txt')
1527+
1528+ out, err = self.run_bzr(['grep', '-r', 'last:1', '--no-recursive', 'line1'])
1529+ self.assertNotContainsRe(out, "file0.txt~.:line1", flags=TestGrep._reflags)
1530+ self.assertEqual(len(out.splitlines()), 0)
1531+
1532+ out, err = self.run_bzr(['grep', '-r', 'last:1', '--no-recursive', 'line1$'])
1533+ self.assertNotContainsRe(out, "file0.txt~.:line1", flags=TestGrep._reflags)
1534+ self.assertEqual(len(out.splitlines()), 0)
1535+
1536+ def test_wtree_file_in_dir_no_recursive(self):
1537+ """(wtree) Should not recurse with --no-recursive"""
1538+ wd = 'foobar0'
1539+ self.make_branch_and_tree(wd)
1540+ os.chdir(wd)
1541+ self._mk_versioned_file('fileX.txt', line_prefix='lin')
1542+ self._mk_versioned_dir('dir0')
1543+ self._mk_versioned_file('dir0/file0.txt')
1544+
1545+ out, err = self.run_bzr(['grep', '--no-recursive', 'line1'])
1546+ self.assertNotContainsRe(out, "file0.txt:line1", flags=TestGrep._reflags)
1547+ self.assertEqual(len(out.splitlines()), 0)
1548+
1549+ out, err = self.run_bzr(['grep', '--no-recursive', 'lin.1'])
1550+ self.assertNotContainsRe(out, "file0.txt:line1", flags=TestGrep._reflags)
1551+ self.assertEqual(len(out.splitlines()), 0)
1552+
1553+ def test_versioned_file_in_dir_recurse(self):
1554+ """(versioned) Should recurse by default.
1555+ """
1556+ wd = 'foobar0'
1557+ self.make_branch_and_tree(wd)
1558+ os.chdir(wd)
1559+ self._mk_versioned_dir('dir0')
1560+ self._mk_versioned_file('dir0/file0.txt')
1561+
1562+ out, err = self.run_bzr(['grep', '-r', '-1', '.i.e1'])
1563+ self.assertContainsRe(out, "^dir0/file0.txt~.:line1", flags=TestGrep._reflags)
1564+ # find line1 and line10
1565+ self.assertEqual(len(out.splitlines()), 2)
1566+
1567+ out, err = self.run_bzr(['grep', '-r', '-1', 'line1'])
1568+ self.assertContainsRe(out, "^dir0/file0.txt~.:line1", flags=TestGrep._reflags)
1569+ # find line1 and line10
1570+ self.assertEqual(len(out.splitlines()), 2)
1571+
1572+ def test_wtree_file_in_dir_recurse(self):
1573+ """(wtree) Should recurse by default.
1574+ """
1575+ wd = 'foobar0'
1576+ self.make_branch_and_tree(wd)
1577+ os.chdir(wd)
1578+ self._mk_versioned_dir('dir0')
1579+ self._mk_versioned_file('dir0/file0.txt')
1580+
1581+ out, err = self.run_bzr(['grep', 'line1'])
1582+ self.assertContainsRe(out, "^dir0/file0.txt:line1", flags=TestGrep._reflags)
1583+ # find line1 and line10
1584+ self.assertEqual(len(out.splitlines()), 2)
1585+
1586+ out, err = self.run_bzr(['grep', 'lin.1'])
1587+ self.assertContainsRe(out, "^dir0/file0.txt:line1", flags=TestGrep._reflags)
1588+ # find line1 and line10
1589+ self.assertEqual(len(out.splitlines()), 2)
1590+
1591+ def test_versioned_file_within_dir(self):
1592+ """(versioned) Search for pattern while in nested dir.
1593+ """
1594+ wd = 'foobar0'
1595+ self.make_branch_and_tree(wd)
1596+ os.chdir(wd)
1597+ self._mk_versioned_dir('dir0')
1598+ self._mk_versioned_file('dir0/file0.txt')
1599+ os.chdir('dir0')
1600+
1601+ out, err = self.run_bzr(['grep', '-r', 'last:1', 'line1'])
1602+ self.assertContainsRe(out, "^file0.txt~.:line1", flags=TestGrep._reflags)
1603+ # finds line1 and line10
1604+ self.assertEqual(len(out.splitlines()), 2)
1605+
1606+ out, err = self.run_bzr(['grep', '-r', 'last:1', '.i.e1'])
1607+ self.assertContainsRe(out, "^file0.txt~.:line1", flags=TestGrep._reflags)
1608+ # finds line1 and line10
1609+ self.assertEqual(len(out.splitlines()), 2)
1610+
1611+ def test_versioned_include_file_within_dir(self):
1612+ """(versioned) Ensure --include is respected with file within dir.
1613+ """
1614+ wd = 'foobar0'
1615+ self.make_branch_and_tree(wd)
1616+ os.chdir(wd)
1617+ self._mk_versioned_dir('dir0') # revno 1
1618+ self._mk_versioned_file('dir0/file0.txt') # revno 2
1619+ self._mk_versioned_file('dir0/file1.aa') # revno 3
1620+ self._update_file('dir0/file1.aa', 'hello\n') # revno 4
1621+ self._update_file('dir0/file0.txt', 'hello\n') # revno 5
1622+ os.chdir('dir0')
1623+
1624+ out, err = self.run_bzr(['grep', '-r', 'last:1',
1625+ '--include', '*.aa', 'line1'])
1626+ self.assertContainsRe(out, "^file1.aa~5:line1$", flags=TestGrep._reflags)
1627+ self.assertContainsRe(out, "^file1.aa~5:line10$", flags=TestGrep._reflags)
1628+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
1629+ # finds line1 and line10
1630+ self.assertEqual(len(out.splitlines()), 2)
1631+
1632+ out, err = self.run_bzr(['grep', '-r', 'last:2..last:1',
1633+ '--include', '*.aa', 'line1'])
1634+ self.assertContainsRe(out, "^file1.aa~4:line1$", flags=TestGrep._reflags)
1635+ self.assertContainsRe(out, "^file1.aa~4:line10$", flags=TestGrep._reflags)
1636+ self.assertContainsRe(out, "^file1.aa~5:line1$", flags=TestGrep._reflags)
1637+ self.assertContainsRe(out, "^file1.aa~5:line10$", flags=TestGrep._reflags)
1638+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
1639+ # finds line1 and line10 over two revisions
1640+ self.assertEqual(len(out.splitlines()), 4)
1641+
1642+ out, err = self.run_bzr(['grep', '-r', 'last:1',
1643+ '--include', '*.aa', 'lin.1'])
1644+ self.assertContainsRe(out, "^file1.aa~5:line1$", flags=TestGrep._reflags)
1645+ self.assertContainsRe(out, "^file1.aa~5:line10$", flags=TestGrep._reflags)
1646+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
1647+ # finds line1 and line10
1648+ self.assertEqual(len(out.splitlines()), 2)
1649+
1650+ out, err = self.run_bzr(['grep', '-r', 'last:3..last:1',
1651+ '--include', '*.aa', 'lin.1'])
1652+ self.assertContainsRe(out, "^file1.aa~3:line1$", flags=TestGrep._reflags)
1653+ self.assertContainsRe(out, "^file1.aa~4:line1$", flags=TestGrep._reflags)
1654+ self.assertContainsRe(out, "^file1.aa~5:line1$", flags=TestGrep._reflags)
1655+ self.assertContainsRe(out, "^file1.aa~3:line10$", flags=TestGrep._reflags)
1656+ self.assertContainsRe(out, "^file1.aa~4:line10$", flags=TestGrep._reflags)
1657+ self.assertContainsRe(out, "^file1.aa~5:line10$", flags=TestGrep._reflags)
1658+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
1659+ # finds line1 and line10 over 3 revisions
1660+ self.assertEqual(len(out.splitlines()), 6)
1661+
1662+ def test_versioned_exclude_file_within_dir(self):
1663+ """(versioned) Ensure --exclude is respected with file within dir.
1664+ """
1665+ wd = 'foobar0'
1666+ self.make_branch_and_tree(wd)
1667+ os.chdir(wd)
1668+ self._mk_versioned_dir('dir0')
1669+ self._mk_versioned_file('dir0/file0.txt')
1670+ self._mk_versioned_file('dir0/file1.aa')
1671+ os.chdir('dir0')
1672+
1673+ out, err = self.run_bzr(['grep', '-r', 'last:1',
1674+ '--exclude', '*.txt', 'line1'])
1675+ self.assertContainsRe(out, "^file1.aa~.:line1", flags=TestGrep._reflags)
1676+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
1677+ # finds line1 and line10
1678+ self.assertEqual(len(out.splitlines()), 2)
1679+
1680+ out, err = self.run_bzr(['grep', '-r', 'last:1',
1681+ '--exclude', '*.txt', 'l[a-z]ne1'])
1682+ self.assertContainsRe(out, "^file1.aa~.:line1", flags=TestGrep._reflags)
1683+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
1684+ # finds line1 and line10
1685+ self.assertEqual(len(out.splitlines()), 2)
1686+
1687+ def test_wtree_file_within_dir(self):
1688+ """(wtree) Search for pattern while in nested dir.
1689+ """
1690+ wd = 'foobar0'
1691+ self.make_branch_and_tree(wd)
1692+ os.chdir(wd)
1693+ self._mk_versioned_dir('dir0')
1694+ self._mk_versioned_file('dir0/file0.txt')
1695+ os.chdir('dir0')
1696+
1697+ out, err = self.run_bzr(['grep', 'line1'])
1698+ self.assertContainsRe(out, "^file0.txt:line1", flags=TestGrep._reflags)
1699+ # finds line1 and line10
1700+ self.assertEqual(len(out.splitlines()), 2)
1701+
1702+ out, err = self.run_bzr(['grep', 'l[aeiou]ne1'])
1703+ self.assertContainsRe(out, "^file0.txt:line1", flags=TestGrep._reflags)
1704+ # finds line1 and line10
1705+ self.assertEqual(len(out.splitlines()), 2)
1706+
1707+ def test_wtree_include_file_within_dir(self):
1708+ """(wtree) Ensure --include is respected with file within dir.
1709+ """
1710+ wd = 'foobar0'
1711+ self.make_branch_and_tree(wd)
1712+ os.chdir(wd)
1713+ self._mk_versioned_dir('dir0')
1714+ self._mk_versioned_file('dir0/file0.txt')
1715+ self._mk_versioned_file('dir0/file1.aa')
1716+ os.chdir('dir0')
1717+
1718+ out, err = self.run_bzr(['grep', '--include', '*.aa', 'line1'])
1719+ self.assertContainsRe(out, "^file1.aa:line1", flags=TestGrep._reflags)
1720+ # finds line1 and line10
1721+ self.assertEqual(len(out.splitlines()), 2)
1722+
1723+ out, err = self.run_bzr(['grep', '--include', '*.aa', 'l[ixn]ne1'])
1724+ self.assertContainsRe(out, "^file1.aa:line1", flags=TestGrep._reflags)
1725+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
1726+ # finds line1 and line10
1727+ self.assertEqual(len(out.splitlines()), 2)
1728+
1729+ def test_wtree_exclude_file_within_dir(self):
1730+ """(wtree) Ensure --exclude is respected with file within dir.
1731+ """
1732+ wd = 'foobar0'
1733+ self.make_branch_and_tree(wd)
1734+ os.chdir(wd)
1735+ self._mk_versioned_dir('dir0')
1736+ self._mk_versioned_file('dir0/file0.txt')
1737+ self._mk_versioned_file('dir0/file1.aa')
1738+ os.chdir('dir0')
1739+
1740+ out, err = self.run_bzr(['grep', '--exclude', '*.txt', 'li.e1'])
1741+ self.assertContainsRe(out, "^file1.aa:line1$", flags=TestGrep._reflags)
1742+ self.assertContainsRe(out, "^file1.aa:line10$", flags=TestGrep._reflags)
1743+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
1744+ # finds line1 and line10
1745+ self.assertEqual(len(out.splitlines()), 2)
1746+
1747+ out, err = self.run_bzr(['grep', '--exclude', '*.txt', 'line1'])
1748+ self.assertContainsRe(out, "^file1.aa:line1$", flags=TestGrep._reflags)
1749+ self.assertContainsRe(out, "^file1.aa:line10$", flags=TestGrep._reflags)
1750+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
1751+ # finds line1 and line10
1752+ self.assertEqual(len(out.splitlines()), 2)
1753+
1754+ def test_versioned_include_from_outside_dir(self):
1755+ """(versioned) Ensure --include is respected during recursive search.
1756+ """
1757+ wd = 'foobar0'
1758+ self.make_branch_and_tree(wd)
1759+ os.chdir(wd)
1760+
1761+ self._mk_versioned_dir('dir0')
1762+ self._mk_versioned_file('dir0/file0.aa')
1763+
1764+ self._mk_versioned_dir('dir1')
1765+ self._mk_versioned_file('dir1/file1.bb')
1766+
1767+ self._mk_versioned_dir('dir2')
1768+ self._mk_versioned_file('dir2/file2.cc')
1769+
1770+ out, err = self.run_bzr(['grep', '-r', 'last:1',
1771+ '--include', '*.aa', '--include', '*.bb', 'l..e1'])
1772+ self.assertContainsRe(out, "^dir0/file0.aa~.:line1$", flags=TestGrep._reflags)
1773+ self.assertContainsRe(out, "^dir1/file1.bb~.:line1$", flags=TestGrep._reflags)
1774+ self.assertContainsRe(out, "^dir0/file0.aa~.:line10$", flags=TestGrep._reflags)
1775+ self.assertContainsRe(out, "^dir1/file1.bb~.:line10$", flags=TestGrep._reflags)
1776+ self.assertNotContainsRe(out, "file1.cc", flags=TestGrep._reflags)
1777+ # finds line1 and line10
1778+ self.assertEqual(len(out.splitlines()), 4)
1779+
1780+ out, err = self.run_bzr(['grep', '-r', 'last:1',
1781+ '--include', '*.aa', '--include', '*.bb', 'line1'])
1782+ self.assertContainsRe(out, "^dir0/file0.aa~.:line1$", flags=TestGrep._reflags)
1783+ self.assertContainsRe(out, "^dir1/file1.bb~.:line1$", flags=TestGrep._reflags)
1784+ self.assertContainsRe(out, "^dir0/file0.aa~.:line10$", flags=TestGrep._reflags)
1785+ self.assertContainsRe(out, "^dir1/file1.bb~.:line10$", flags=TestGrep._reflags)
1786+ self.assertNotContainsRe(out, "file1.cc", flags=TestGrep._reflags)
1787+ # finds line1 and line10
1788+ self.assertEqual(len(out.splitlines()), 4)
1789+
1790+ def test_wtree_include_from_outside_dir(self):
1791+ """(wtree) Ensure --include is respected during recursive search.
1792+ """
1793+ wd = 'foobar0'
1794+ self.make_branch_and_tree(wd)
1795+ os.chdir(wd)
1796+
1797+ self._mk_versioned_dir('dir0')
1798+ self._mk_versioned_file('dir0/file0.aa')
1799+
1800+ self._mk_versioned_dir('dir1')
1801+ self._mk_versioned_file('dir1/file1.bb')
1802+
1803+ self._mk_versioned_dir('dir2')
1804+ self._mk_versioned_file('dir2/file2.cc')
1805+
1806+ out, err = self.run_bzr(['grep', '--include', '*.aa',
1807+ '--include', '*.bb', 'l.n.1'])
1808+ self.assertContainsRe(out, "^dir0/file0.aa:line1$", flags=TestGrep._reflags)
1809+ self.assertContainsRe(out, "^dir1/file1.bb:line1$", flags=TestGrep._reflags)
1810+ self.assertContainsRe(out, "^dir0/file0.aa:line10$", flags=TestGrep._reflags)
1811+ self.assertContainsRe(out, "^dir1/file1.bb:line10$", flags=TestGrep._reflags)
1812+ self.assertNotContainsRe(out, "file1.cc", flags=TestGrep._reflags)
1813+ # finds line1 and line10
1814+ self.assertEqual(len(out.splitlines()), 4)
1815+
1816+ out, err = self.run_bzr(['grep', '--include', '*.aa',
1817+ '--include', '*.bb', 'line1'])
1818+ self.assertContainsRe(out, "^dir0/file0.aa:line1$", flags=TestGrep._reflags)
1819+ self.assertContainsRe(out, "^dir1/file1.bb:line1$", flags=TestGrep._reflags)
1820+ self.assertContainsRe(out, "^dir0/file0.aa:line10$", flags=TestGrep._reflags)
1821+ self.assertContainsRe(out, "^dir1/file1.bb:line10$", flags=TestGrep._reflags)
1822+ self.assertNotContainsRe(out, "file1.cc", flags=TestGrep._reflags)
1823+ # finds line1 and line10
1824+ self.assertEqual(len(out.splitlines()), 4)
1825+
1826+ def test_versioned_exclude_from_outside_dir(self):
1827+ """(versioned) Ensure --exclude is respected during recursive search.
1828+ """
1829+ wd = 'foobar0'
1830+ self.make_branch_and_tree(wd)
1831+ os.chdir(wd)
1832+
1833+ self._mk_versioned_dir('dir0')
1834+ self._mk_versioned_file('dir0/file0.aa')
1835+
1836+ self._mk_versioned_dir('dir1')
1837+ self._mk_versioned_file('dir1/file1.bb')
1838+
1839+ self._mk_versioned_dir('dir2')
1840+ self._mk_versioned_file('dir2/file2.cc')
1841+
1842+ out, err = self.run_bzr(['grep', '-r', 'last:1',
1843+ '--exclude', '*.cc', 'l..e1'])
1844+ self.assertContainsRe(out, "^dir0/file0.aa~.:line1", flags=TestGrep._reflags)
1845+ self.assertContainsRe(out, "^dir1/file1.bb~.:line1", flags=TestGrep._reflags)
1846+ self.assertNotContainsRe(out, "file1.cc", flags=TestGrep._reflags)
1847+
1848+ out, err = self.run_bzr(['grep', '-r', 'last:1',
1849+ '--exclude', '*.cc', 'line1'])
1850+ self.assertContainsRe(out, "^dir0/file0.aa~.:line1", flags=TestGrep._reflags)
1851+ self.assertContainsRe(out, "^dir1/file1.bb~.:line1", flags=TestGrep._reflags)
1852+ self.assertNotContainsRe(out, "file1.cc", flags=TestGrep._reflags)
1853+
1854+ def test_wtree_exclude_from_outside_dir(self):
1855+ """(wtree) Ensure --exclude is respected during recursive search.
1856+ """
1857+ wd = 'foobar0'
1858+ self.make_branch_and_tree(wd)
1859+ os.chdir(wd)
1860+
1861+ self._mk_versioned_dir('dir0')
1862+ self._mk_versioned_file('dir0/file0.aa')
1863+
1864+ self._mk_versioned_dir('dir1')
1865+ self._mk_versioned_file('dir1/file1.bb')
1866+
1867+ self._mk_versioned_dir('dir2')
1868+ self._mk_versioned_file('dir2/file2.cc')
1869+
1870+ out, err = self.run_bzr(['grep', '--exclude', '*.cc', 'l[hijk]ne1'])
1871+ self.assertContainsRe(out, "^dir0/file0.aa:line1", flags=TestGrep._reflags)
1872+ self.assertContainsRe(out, "^dir1/file1.bb:line1", flags=TestGrep._reflags)
1873+ self.assertNotContainsRe(out, "file1.cc", flags=TestGrep._reflags)
1874+
1875+ out, err = self.run_bzr(['grep', '--exclude', '*.cc', 'line1'])
1876+ self.assertContainsRe(out, "^dir0/file0.aa:line1", flags=TestGrep._reflags)
1877+ self.assertContainsRe(out, "^dir1/file1.bb:line1", flags=TestGrep._reflags)
1878+ self.assertNotContainsRe(out, "file1.cc", flags=TestGrep._reflags)
1879+
1880+ def test_workingtree_files_from_outside_dir(self):
1881+ """(wtree) Grep for pattern with dirs passed as argument.
1882+ """
1883+ wd = 'foobar0'
1884+ self.make_branch_and_tree(wd)
1885+ os.chdir(wd)
1886+
1887+ self._mk_versioned_dir('dir0')
1888+ self._mk_versioned_file('dir0/file0.txt')
1889+
1890+ self._mk_versioned_dir('dir1')
1891+ self._mk_versioned_file('dir1/file1.txt')
1892+
1893+ out, err = self.run_bzr(['grep', 'l[aeiou]ne1', 'dir0', 'dir1'])
1894+ self.assertContainsRe(out, "^dir0/file0.txt:line1", flags=TestGrep._reflags)
1895+ self.assertContainsRe(out, "^dir1/file1.txt:line1", flags=TestGrep._reflags)
1896+
1897+ out, err = self.run_bzr(['grep', 'line1', 'dir0', 'dir1'])
1898+ self.assertContainsRe(out, "^dir0/file0.txt:line1", flags=TestGrep._reflags)
1899+ self.assertContainsRe(out, "^dir1/file1.txt:line1", flags=TestGrep._reflags)
1900+
1901+ def test_versioned_files_from_outside_dir(self):
1902+ """(versioned) Grep for pattern with dirs passed as argument.
1903+ """
1904+ wd = 'foobar0'
1905+ self.make_branch_and_tree(wd)
1906+ os.chdir(wd)
1907+
1908+ self._mk_versioned_dir('dir0')
1909+ self._mk_versioned_file('dir0/file0.txt')
1910+
1911+ self._mk_versioned_dir('dir1')
1912+ self._mk_versioned_file('dir1/file1.txt')
1913+
1914+ out, err = self.run_bzr(['grep', '-r', 'last:1', '.ine1', 'dir0', 'dir1'])
1915+ self.assertContainsRe(out, "^dir0/file0.txt~.:line1", flags=TestGrep._reflags)
1916+ self.assertContainsRe(out, "^dir1/file1.txt~.:line1", flags=TestGrep._reflags)
1917+
1918+ out, err = self.run_bzr(['grep', '-r', 'last:1', 'line1', 'dir0', 'dir1'])
1919+ self.assertContainsRe(out, "^dir0/file0.txt~.:line1", flags=TestGrep._reflags)
1920+ self.assertContainsRe(out, "^dir1/file1.txt~.:line1", flags=TestGrep._reflags)
1921+
1922+ def test_wtree_files_from_outside_dir(self):
1923+ """(wtree) Grep for pattern with dirs passed as argument.
1924+ """
1925+ wd = 'foobar0'
1926+ self.make_branch_and_tree(wd)
1927+ os.chdir(wd)
1928+
1929+ self._mk_versioned_dir('dir0')
1930+ self._mk_versioned_file('dir0/file0.txt')
1931+
1932+ self._mk_versioned_dir('dir1')
1933+ self._mk_versioned_file('dir1/file1.txt')
1934+
1935+ out, err = self.run_bzr(['grep', 'li.e1', 'dir0', 'dir1'])
1936+ self.assertContainsRe(out, "^dir0/file0.txt:line1", flags=TestGrep._reflags)
1937+ self.assertContainsRe(out, "^dir1/file1.txt:line1", flags=TestGrep._reflags)
1938+
1939+ out, err = self.run_bzr(['grep', 'line1', 'dir0', 'dir1'])
1940+ self.assertContainsRe(out, "^dir0/file0.txt:line1", flags=TestGrep._reflags)
1941+ self.assertContainsRe(out, "^dir1/file1.txt:line1", flags=TestGrep._reflags)
1942+
1943+ def test_versioned_files_from_outside_two_dirs(self):
1944+ """(versioned) Grep for pattern with two levels of nested dir.
1945+ """
1946+ wd = 'foobar0'
1947+ self.make_branch_and_tree(wd)
1948+ os.chdir(wd)
1949+
1950+ self._mk_versioned_dir('dir0')
1951+ self._mk_versioned_file('dir0/file0.txt')
1952+
1953+ self._mk_versioned_dir('dir1')
1954+ self._mk_versioned_file('dir1/file1.txt')
1955+
1956+ self._mk_versioned_dir('dir0/dir00')
1957+ self._mk_versioned_file('dir0/dir00/file0.txt')
1958+
1959+ out, err = self.run_bzr(['grep', '-r', 'last:1', 'l.ne1', 'dir0/dir00'])
1960+ self.assertContainsRe(out, "^dir0/dir00/file0.txt~.:line1", flags=TestGrep._reflags)
1961+
1962+ out, err = self.run_bzr(['grep', '-r', 'last:1', 'l.ne1'])
1963+ self.assertContainsRe(out, "^dir0/dir00/file0.txt~.:line1", flags=TestGrep._reflags)
1964+
1965+ out, err = self.run_bzr(['grep', '-r', 'last:1', 'line1', 'dir0/dir00'])
1966+ self.assertContainsRe(out, "^dir0/dir00/file0.txt~.:line1", flags=TestGrep._reflags)
1967+
1968+ out, err = self.run_bzr(['grep', '-r', 'last:1', 'line1'])
1969+ self.assertContainsRe(out, "^dir0/dir00/file0.txt~.:line1", flags=TestGrep._reflags)
1970+
1971+ def test_wtree_files_from_outside_two_dirs(self):
1972+ """(wtree) Grep for pattern with two levels of nested dir.
1973+ """
1974+ wd = 'foobar0'
1975+ self.make_branch_and_tree(wd)
1976+ os.chdir(wd)
1977+
1978+ self._mk_versioned_dir('dir0')
1979+ self._mk_versioned_file('dir0/file0.txt')
1980+
1981+ self._mk_versioned_dir('dir1')
1982+ self._mk_versioned_file('dir1/file1.txt')
1983+
1984+ self._mk_versioned_dir('dir0/dir00')
1985+ self._mk_versioned_file('dir0/dir00/file0.txt')
1986+
1987+ out, err = self.run_bzr(['grep', 'lin.1', 'dir0/dir00'])
1988+ self.assertContainsRe(out, "^dir0/dir00/file0.txt:line1", flags=TestGrep._reflags)
1989+
1990+ out, err = self.run_bzr(['grep', 'li.e1'])
1991+ self.assertContainsRe(out, "^dir0/dir00/file0.txt:line1", flags=TestGrep._reflags)
1992+
1993+ out, err = self.run_bzr(['grep', 'line1', 'dir0/dir00'])
1994+ self.assertContainsRe(out, "^dir0/dir00/file0.txt:line1", flags=TestGrep._reflags)
1995+
1996+ out, err = self.run_bzr(['grep', 'line1'])
1997+ self.assertContainsRe(out, "^dir0/dir00/file0.txt:line1", flags=TestGrep._reflags)
1998+
1999+ def test_versioned_file_within_dir_two_levels(self):
2000+ """(versioned) Search for pattern while in nested dir (two levels).
2001+ """
2002+ wd = 'foobar0'
2003+ self.make_branch_and_tree(wd)
2004+ os.chdir(wd)
2005+ self._mk_versioned_dir('dir0')
2006+ self._mk_versioned_dir('dir0/dir1')
2007+ self._mk_versioned_file('dir0/dir1/file0.txt')
2008+ os.chdir('dir0')
2009+
2010+ out, err = self.run_bzr(['grep', '-r', 'last:1', '.ine1'])
2011+ self.assertContainsRe(out, "^dir1/file0.txt~.:line1", flags=TestGrep._reflags)
2012+
2013+ out, err = self.run_bzr(['grep', '-r', 'last:1', '--from-root', 'l.ne1'])
2014+ self.assertContainsRe(out, "^dir0/dir1/file0.txt~.:line1", flags=TestGrep._reflags)
2015+
2016+ out, err = self.run_bzr(['grep', '-r', 'last:1', '--no-recursive', 'line1'])
2017+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
2018+
2019+ out, err = self.run_bzr(['grep', '-r', 'last:1', 'lin.1'])
2020+ self.assertContainsRe(out, "^dir1/file0.txt~.:line1", flags=TestGrep._reflags)
2021+
2022+ out, err = self.run_bzr(['grep', '-r', 'last:1', '--from-root', 'line1'])
2023+ self.assertContainsRe(out, "^dir0/dir1/file0.txt~.:line1", flags=TestGrep._reflags)
2024+
2025+ out, err = self.run_bzr(['grep', '-r', 'last:1', '--no-recursive', 'line1'])
2026+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
2027+ self.assertEqual(len(out.splitlines()), 0)
2028+
2029+ def test_wtree_file_within_dir_two_levels(self):
2030+ """(wtree) Search for pattern while in nested dir (two levels).
2031+ """
2032+ wd = 'foobar0'
2033+ self.make_branch_and_tree(wd)
2034+ os.chdir(wd)
2035+ self._mk_versioned_dir('dir0')
2036+ self._mk_versioned_dir('dir0/dir1')
2037+ self._mk_versioned_file('dir0/dir1/file0.txt')
2038+ os.chdir('dir0')
2039+
2040+ out, err = self.run_bzr(['grep', 'l[hij]ne1'])
2041+ self.assertContainsRe(out, "^dir1/file0.txt:line1", flags=TestGrep._reflags)
2042+
2043+ out, err = self.run_bzr(['grep', '--from-root', 'l.ne1'])
2044+ self.assertContainsRe(out, "^dir0/dir1/file0.txt:line1", flags=TestGrep._reflags)
2045+
2046+ out, err = self.run_bzr(['grep', '--no-recursive', 'lin.1'])
2047+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
2048+
2049+ out, err = self.run_bzr(['grep', 'line1'])
2050+ self.assertContainsRe(out, "^dir1/file0.txt:line1", flags=TestGrep._reflags)
2051+
2052+ out, err = self.run_bzr(['grep', '--from-root', 'line1'])
2053+ self.assertContainsRe(out, "^dir0/dir1/file0.txt:line1", flags=TestGrep._reflags)
2054+
2055+ out, err = self.run_bzr(['grep', '--no-recursive', 'line1'])
2056+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
2057+
2058+ def test_versioned_ignore_case_no_match(self):
2059+ """(versioned) Match fails without --ignore-case.
2060+ """
2061+ wd = 'foobar0'
2062+ self.make_branch_and_tree(wd)
2063+ os.chdir(wd)
2064+ self._mk_versioned_file('file0.txt')
2065+
2066+ out, err = self.run_bzr(['grep', '-r', 'last:1', 'LinE1', 'file0.txt'])
2067+ self.assertNotContainsRe(out, "file0.txt~.:line1", flags=TestGrep._reflags)
2068+
2069+ out, err = self.run_bzr(['grep', '-r', 'last:1', 'Li.E1', 'file0.txt'])
2070+ self.assertNotContainsRe(out, "file0.txt~.:line1", flags=TestGrep._reflags)
2071+
2072+ def test_wtree_ignore_case_no_match(self):
2073+ """(wtree) Match fails without --ignore-case.
2074+ """
2075+ wd = 'foobar0'
2076+ self.make_branch_and_tree(wd)
2077+ os.chdir(wd)
2078+ self._mk_versioned_file('file0.txt')
2079+
2080+ out, err = self.run_bzr(['grep', 'LinE1', 'file0.txt'])
2081+ self.assertNotContainsRe(out, "file0.txt:line1", flags=TestGrep._reflags)
2082+
2083+ out, err = self.run_bzr(['grep', '.inE1', 'file0.txt'])
2084+ self.assertNotContainsRe(out, "file0.txt:line1", flags=TestGrep._reflags)
2085+
2086+ def test_versioned_ignore_case_match(self):
2087+ """(versioned) Match fails without --ignore-case.
2088+ """
2089+ wd = 'foobar0'
2090+ self.make_branch_and_tree(wd)
2091+ os.chdir(wd)
2092+ self._mk_versioned_file('file0.txt')
2093+
2094+ out, err = self.run_bzr(['grep', '-r', 'last:1',
2095+ '-i', 'Li.E1', 'file0.txt'])
2096+ self.assertContainsRe(out, "file0.txt~.:line1", flags=TestGrep._reflags)
2097+
2098+ out, err = self.run_bzr(['grep', '-r', 'last:1',
2099+ '-i', 'LinE1', 'file0.txt'])
2100+ self.assertContainsRe(out, "file0.txt~.:line1", flags=TestGrep._reflags)
2101+
2102+ out, err = self.run_bzr(['grep', '-r', 'last:1',
2103+ '--ignore-case', 'LinE1', 'file0.txt'])
2104+ self.assertContainsRe(out, "^file0.txt~.:line1", flags=TestGrep._reflags)
2105+
2106+ def test_wtree_ignore_case_match(self):
2107+ """(wtree) Match fails without --ignore-case.
2108+ """
2109+ wd = 'foobar0'
2110+ self.make_branch_and_tree(wd)
2111+ os.chdir(wd)
2112+ self._mk_versioned_file('file0.txt')
2113+
2114+ out, err = self.run_bzr(['grep', '-i', 'LinE1', 'file0.txt'])
2115+ self.assertContainsRe(out, "file0.txt:line1", flags=TestGrep._reflags)
2116+
2117+ out, err = self.run_bzr(['grep', '--ignore-case', 'LinE1', 'file0.txt'])
2118+ self.assertContainsRe(out, "^file0.txt:line1", flags=TestGrep._reflags)
2119+
2120+ out, err = self.run_bzr(['grep', '--ignore-case', 'Li.E1', 'file0.txt'])
2121+ self.assertContainsRe(out, "^file0.txt:line1", flags=TestGrep._reflags)
2122+
2123+ def test_versioned_from_root_fail(self):
2124+ """(versioned) Match should fail without --from-root.
2125+ """
2126+ wd = 'foobar0'
2127+ self.make_branch_and_tree(wd)
2128+ os.chdir(wd)
2129+ self._mk_versioned_file('file0.txt')
2130+ self._mk_versioned_dir('dir0')
2131+ os.chdir('dir0')
2132+
2133+ out, err = self.run_bzr(['grep', '-r', 'last:1', 'li.e1'])
2134+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
2135+
2136+ out, err = self.run_bzr(['grep', '-r', 'last:1', 'line1'])
2137+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
2138+
2139+ def test_wtree_from_root_fail(self):
2140+ """(wtree) Match should fail without --from-root.
2141+ """
2142+ wd = 'foobar0'
2143+ self.make_branch_and_tree(wd)
2144+ os.chdir(wd)
2145+ self._mk_versioned_file('file0.txt')
2146+ self._mk_versioned_dir('dir0')
2147+ os.chdir('dir0')
2148+
2149+ out, err = self.run_bzr(['grep', 'line1'])
2150+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
2151+
2152+ out, err = self.run_bzr(['grep', 'li.e1'])
2153+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
2154+
2155+ def test_versioned_from_root_pass(self):
2156+ """(versioned) Match pass with --from-root.
2157+ """
2158+ wd = 'foobar0'
2159+ self.make_branch_and_tree(wd)
2160+ os.chdir(wd)
2161+ self._mk_versioned_file('file0.txt')
2162+ self._mk_versioned_dir('dir0')
2163+ os.chdir('dir0')
2164+
2165+ out, err = self.run_bzr(['grep', '-r', 'last:1',
2166+ '--from-root', 'l.ne1'])
2167+ self.assertContainsRe(out, "file0.txt~.:line1", flags=TestGrep._reflags)
2168+
2169+ out, err = self.run_bzr(['grep', '-r', 'last:1',
2170+ '--from-root', 'line1'])
2171+ self.assertContainsRe(out, "file0.txt~.:line1", flags=TestGrep._reflags)
2172+
2173+ def test_wtree_from_root_pass(self):
2174+ """(wtree) Match pass with --from-root.
2175+ """
2176+ wd = 'foobar0'
2177+ self.make_branch_and_tree(wd)
2178+ os.chdir(wd)
2179+ self._mk_versioned_file('file0.txt')
2180+ self._mk_versioned_dir('dir0')
2181+ os.chdir('dir0')
2182+
2183+ out, err = self.run_bzr(['grep', '--from-root', 'lin.1'])
2184+ self.assertContainsRe(out, "file0.txt:line1", flags=TestGrep._reflags)
2185+
2186+ out, err = self.run_bzr(['grep', '--from-root', 'line1'])
2187+ self.assertContainsRe(out, "file0.txt:line1", flags=TestGrep._reflags)
2188+
2189+ def test_versioned_with_line_number(self):
2190+ """(versioned) Search for pattern with --line-number.
2191+ """
2192+ wd = 'foobar0'
2193+ self.make_branch_and_tree(wd)
2194+ os.chdir(wd)
2195+ self._mk_versioned_file('file0.txt')
2196+
2197+ out, err = self.run_bzr(['grep', '-r', 'last:1',
2198+ '--line-number', 'li.e3', 'file0.txt'])
2199+ self.assertContainsRe(out, "file0.txt~.:3:line3", flags=TestGrep._reflags)
2200+
2201+ out, err = self.run_bzr(['grep', '-r', 'last:1',
2202+ '--line-number', 'line3', 'file0.txt'])
2203+ self.assertContainsRe(out, "file0.txt~.:3:line3", flags=TestGrep._reflags)
2204+
2205+ out, err = self.run_bzr(['grep', '-r', 'last:1',
2206+ '-n', 'line1', 'file0.txt'])
2207+ self.assertContainsRe(out, "file0.txt~.:1:line1", flags=TestGrep._reflags)
2208+
2209+ out, err = self.run_bzr(['grep', '-n', 'line[0-9]', 'file0.txt'])
2210+ self.assertContainsRe(out, "file0.txt:3:line3", flags=TestGrep._reflags)
2211+
2212+ def test_wtree_with_line_number(self):
2213+ """(wtree) Search for pattern with --line-number.
2214+ """
2215+ wd = 'foobar0'
2216+ self.make_branch_and_tree(wd)
2217+ os.chdir(wd)
2218+ self._mk_versioned_file('file0.txt')
2219+
2220+ out, err = self.run_bzr(['grep', '--line-number', 'line3', 'file0.txt'])
2221+ self.assertContainsRe(out, "file0.txt:3:line3", flags=TestGrep._reflags)
2222+
2223+ out, err = self.run_bzr(['grep', '-n', 'line1', 'file0.txt'])
2224+ self.assertContainsRe(out, "file0.txt:1:line1", flags=TestGrep._reflags)
2225+
2226+ out, err = self.run_bzr(['grep', '-n', '[hjkl]ine1', 'file0.txt'])
2227+ self.assertContainsRe(out, "file0.txt:1:line1", flags=TestGrep._reflags)
2228+
2229+ out, err = self.run_bzr(['grep', '-n', 'line[0-9]', 'file0.txt'])
2230+ self.assertContainsRe(out, "file0.txt:3:line3", flags=TestGrep._reflags)
2231+
2232+ def test_revno_basic_history_grep_file(self):
2233+ """Search for pattern in specific revision number in a file.
2234+ """
2235+ wd = 'foobar0'
2236+ fname = 'file0.txt'
2237+ self.make_branch_and_tree(wd)
2238+ os.chdir(wd)
2239+ self._mk_versioned_file(fname, total_lines=0)
2240+ self._update_file(fname, text="v2 text\n")
2241+ self._update_file(fname, text="v3 text\n")
2242+ self._update_file(fname, text="v4 text\n")
2243+
2244+ # rev 2 should not have text 'v3'
2245+ out, err = self.run_bzr(['grep', '-r', '2', 'v3', fname])
2246+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
2247+
2248+ # rev 3 should not have text 'v3'
2249+ out, err = self.run_bzr(['grep', '-r', '3', 'v3', fname])
2250+ self.assertContainsRe(out, "file0.txt~3:v3.*", flags=TestGrep._reflags)
2251+
2252+ # rev 3 should not have text 'v3' with line number
2253+ out, err = self.run_bzr(['grep', '-r', '3', '-n', 'v3', fname])
2254+ self.assertContainsRe(out, "file0.txt~3:2:v3.*", flags=TestGrep._reflags)
2255+
2256+ # rev 2 should not have text 'v3'
2257+ out, err = self.run_bzr(['grep', '-r', '2', '[tuv]3', fname])
2258+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
2259+
2260+ # rev 3 should not have text 'v3'
2261+ out, err = self.run_bzr(['grep', '-r', '3', '[tuv]3', fname])
2262+ self.assertContainsRe(out, "file0.txt~3:v3.*", flags=TestGrep._reflags)
2263+
2264+ # rev 3 should not have text 'v3' with line number
2265+ out, err = self.run_bzr(['grep', '-r', '3', '-n', '[tuv]3', fname])
2266+ self.assertContainsRe(out, "file0.txt~3:2:v3.*", flags=TestGrep._reflags)
2267+
2268+ def test_revno_basic_history_grep_full(self):
2269+ """Search for pattern in specific revision number in a file.
2270+ """
2271+ wd = 'foobar0'
2272+ fname = 'file0.txt'
2273+ self.make_branch_and_tree(wd)
2274+ os.chdir(wd)
2275+ self._mk_versioned_file(fname, total_lines=0) # rev1
2276+ self._mk_versioned_file('file1.txt') # rev2
2277+ self._update_file(fname, text="v3 text\n") # rev3
2278+ self._update_file(fname, text="v4 text\n") # rev4
2279+ self._update_file(fname, text="v5 text\n") # rev5
2280+
2281+ # rev 2 should not have text 'v3'
2282+ out, err = self.run_bzr(['grep', '-r', '2', 'v3'])
2283+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
2284+
2285+ # rev 3 should not have text 'v3'
2286+ out, err = self.run_bzr(['grep', '-r', '3', 'v3'])
2287+ self.assertContainsRe(out, "file0.txt~3:v3", flags=TestGrep._reflags)
2288+
2289+ # rev 3 should not have text 'v3' with line number
2290+ out, err = self.run_bzr(['grep', '-r', '3', '-n', 'v3'])
2291+ self.assertContainsRe(out, "file0.txt~3:1:v3", flags=TestGrep._reflags)
2292+
2293+ # rev 2 should not have text 'v3'
2294+ out, err = self.run_bzr(['grep', '-r', '2', '[tuv]3'])
2295+ self.assertNotContainsRe(out, "file0.txt", flags=TestGrep._reflags)
2296+
2297+ # rev 3 should not have text 'v3'
2298+ out, err = self.run_bzr(['grep', '-r', '3', '[tuv]3'])
2299+ self.assertContainsRe(out, "file0.txt~3:v3", flags=TestGrep._reflags)
2300+
2301+ # rev 3 should not have text 'v3' with line number
2302+ out, err = self.run_bzr(['grep', '-r', '3', '-n', '[tuv]3'])
2303+ self.assertContainsRe(out, "file0.txt~3:1:v3", flags=TestGrep._reflags)
2304+
2305+ def test_revno_versioned_file_in_dir(self):
2306+ """Grep specific version of file withing dir.
2307+ """
2308+ wd = 'foobar0'
2309+ self.make_branch_and_tree(wd)
2310+ os.chdir(wd)
2311+ self._mk_versioned_dir('dir0') # rev1
2312+ self._mk_versioned_file('dir0/file0.txt') # rev2
2313+ self._update_file('dir0/file0.txt', "v3 text\n") # rev3
2314+ self._update_file('dir0/file0.txt', "v4 text\n") # rev4
2315+ self._update_file('dir0/file0.txt', "v5 text\n") # rev5
2316+
2317+ # v4 should not be present in revno 3
2318+ out, err = self.run_bzr(['grep', '-r', 'last:3', 'v4'])
2319+ self.assertNotContainsRe(out, "^dir0/file0.txt", flags=TestGrep._reflags)
2320+
2321+ # v4 should be present in revno 4
2322+ out, err = self.run_bzr(['grep', '-r', 'last:2', 'v4'])
2323+ self.assertContainsRe(out, "^dir0/file0.txt~4:v4", flags=TestGrep._reflags)
2324+
2325+ # v4 should not be present in revno 3
2326+ out, err = self.run_bzr(['grep', '-r', 'last:3', '[tuv]4'])
2327+ self.assertNotContainsRe(out, "^dir0/file0.txt", flags=TestGrep._reflags)
2328+
2329+ # v4 should be present in revno 4
2330+ out, err = self.run_bzr(['grep', '-r', 'last:2', '[tuv]4'])
2331+ self.assertContainsRe(out, "^dir0/file0.txt~4:v4", flags=TestGrep._reflags)
2332+
2333+ def test_revno_range_basic_history_grep(self):
2334+ """Search for pattern in revision range for file.
2335+ """
2336+ wd = 'foobar0'
2337+ fname = 'file0.txt'
2338+ self.make_branch_and_tree(wd)
2339+ os.chdir(wd)
2340+ self._mk_versioned_file(fname, total_lines=0) # rev1
2341+ self._mk_versioned_file('file1.txt') # rev2
2342+ self._update_file(fname, text="v3 text\n") # rev3
2343+ self._update_file(fname, text="v4 text\n") # rev4
2344+ self._update_file(fname, text="v5 text\n") # rev5
2345+ self._update_file(fname, text="v6 text\n") # rev6
2346+
2347+ out, err = self.run_bzr(['grep', '-r', '1..', 'v3'])
2348+ self.assertContainsRe(out, "file0.txt~3:v3", flags=TestGrep._reflags)
2349+ self.assertContainsRe(out, "file0.txt~4:v3", flags=TestGrep._reflags)
2350+ self.assertContainsRe(out, "file0.txt~5:v3", flags=TestGrep._reflags)
2351+ self.assertContainsRe(out, "file0.txt~6:v3", flags=TestGrep._reflags)
2352+ self.assertEqual(len(out.splitlines()), 4)
2353+
2354+ out, err = self.run_bzr(['grep', '-r', '..1', 'v3'])
2355+ # searching only rev1 gives nothing
2356+ self.assertEqual(len(out.splitlines()), 0)
2357+
2358+ out, err = self.run_bzr(['grep', '-r', '..6', 'v3'])
2359+ self.assertContainsRe(out, "file0.txt~3:v3", flags=TestGrep._reflags)
2360+ self.assertContainsRe(out, "file0.txt~4:v3", flags=TestGrep._reflags)
2361+ self.assertContainsRe(out, "file0.txt~5:v3", flags=TestGrep._reflags)
2362+ self.assertContainsRe(out, "file0.txt~6:v3", flags=TestGrep._reflags)
2363+ self.assertEqual(len(out.splitlines()), 4)
2364+
2365+ out, err = self.run_bzr(['grep', '-r', '..', 'v3'])
2366+ self.assertContainsRe(out, "file0.txt~3:v3", flags=TestGrep._reflags)
2367+ self.assertContainsRe(out, "file0.txt~4:v3", flags=TestGrep._reflags)
2368+ self.assertContainsRe(out, "file0.txt~5:v3", flags=TestGrep._reflags)
2369+ self.assertContainsRe(out, "file0.txt~6:v3", flags=TestGrep._reflags)
2370+ self.assertEqual(len(out.splitlines()), 4)
2371+
2372+ out, err = self.run_bzr(['grep', '-r', '1..5', 'v3'])
2373+ self.assertContainsRe(out, "file0.txt~3:v3", flags=TestGrep._reflags)
2374+ self.assertContainsRe(out, "file0.txt~4:v3", flags=TestGrep._reflags)
2375+ self.assertContainsRe(out, "file0.txt~5:v3", flags=TestGrep._reflags)
2376+ self.assertNotContainsRe(out, "file0.txt~6:v3", flags=TestGrep._reflags)
2377+ self.assertEqual(len(out.splitlines()), 3)
2378+
2379+ out, err = self.run_bzr(['grep', '-r', '5..1', 'v3'])
2380+ self.assertContainsRe(out, "file0.txt~3:v3", flags=TestGrep._reflags)
2381+ self.assertContainsRe(out, "file0.txt~4:v3", flags=TestGrep._reflags)
2382+ self.assertContainsRe(out, "file0.txt~5:v3", flags=TestGrep._reflags)
2383+ self.assertNotContainsRe(out, "file0.txt~6:v3", flags=TestGrep._reflags)
2384+ self.assertEqual(len(out.splitlines()), 3)
2385+
2386+ out, err = self.run_bzr(['grep', '-r', '1..', '[tuv]3'])
2387+ self.assertContainsRe(out, "file0.txt~3:v3", flags=TestGrep._reflags)
2388+ self.assertContainsRe(out, "file0.txt~4:v3", flags=TestGrep._reflags)
2389+ self.assertContainsRe(out, "file0.txt~5:v3", flags=TestGrep._reflags)
2390+ self.assertContainsRe(out, "file0.txt~6:v3", flags=TestGrep._reflags)
2391+ self.assertEqual(len(out.splitlines()), 4)
2392+
2393+ out, err = self.run_bzr(['grep', '-r', '1..5', '[tuv]3'])
2394+ self.assertContainsRe(out, "file0.txt~3:v3", flags=TestGrep._reflags)
2395+ self.assertContainsRe(out, "file0.txt~4:v3", flags=TestGrep._reflags)
2396+ self.assertContainsRe(out, "file0.txt~5:v3", flags=TestGrep._reflags)
2397+ self.assertNotContainsRe(out, "file0.txt~6:v3", flags=TestGrep._reflags)
2398+ self.assertEqual(len(out.splitlines()), 3)
2399+
2400+ out, err = self.run_bzr(['grep', '-r', '5..1', '[tuv]3'])
2401+ self.assertContainsRe(out, "file0.txt~3:v3", flags=TestGrep._reflags)
2402+ self.assertContainsRe(out, "file0.txt~4:v3", flags=TestGrep._reflags)
2403+ self.assertContainsRe(out, "file0.txt~5:v3", flags=TestGrep._reflags)
2404+ self.assertNotContainsRe(out, "file0.txt~6:v3", flags=TestGrep._reflags)
2405+ self.assertEqual(len(out.splitlines()), 3)
2406+
2407+ def test_revno_range_versioned_file_in_dir(self):
2408+ """Grep rev-range for pattern for file withing a dir.
2409+ """
2410+ wd = 'foobar0'
2411+ self.make_branch_and_tree(wd)
2412+ os.chdir(wd)
2413+ self._mk_versioned_dir('dir0') # rev1
2414+ self._mk_versioned_file('dir0/file0.txt') # rev2
2415+ self._update_file('dir0/file0.txt', "v3 text\n") # rev3
2416+ self._update_file('dir0/file0.txt', "v4 text\n") # rev4
2417+ self._update_file('dir0/file0.txt', "v5 text\n") # rev5
2418+ self._update_file('dir0/file0.txt', "v6 text\n") # rev6
2419+
2420+ out, err = self.run_bzr(['grep', '-r', '2..5', 'v3'])
2421+ self.assertContainsRe(out, "^dir0/file0.txt~3:v3", flags=TestGrep._reflags)
2422+ self.assertContainsRe(out, "^dir0/file0.txt~4:v3", flags=TestGrep._reflags)
2423+ self.assertContainsRe(out, "^dir0/file0.txt~5:v3", flags=TestGrep._reflags)
2424+ self.assertNotContainsRe(out, "^dir0/file0.txt~6:v3", flags=TestGrep._reflags)
2425+ self.assertEqual(len(out.splitlines()), 3)
2426+
2427+ out, err = self.run_bzr(['grep', '-r', '2..5', '[tuv]3'])
2428+ self.assertContainsRe(out, "^dir0/file0.txt~3:v3", flags=TestGrep._reflags)
2429+ self.assertContainsRe(out, "^dir0/file0.txt~4:v3", flags=TestGrep._reflags)
2430+ self.assertContainsRe(out, "^dir0/file0.txt~5:v3", flags=TestGrep._reflags)
2431+ self.assertNotContainsRe(out, "^dir0/file0.txt~6:v3", flags=TestGrep._reflags)
2432+ self.assertEqual(len(out.splitlines()), 3)
2433+
2434+ def test_revno_range_versioned_file_from_outside_dir(self):
2435+ """Grep rev-range for pattern from outside dir.
2436+ """
2437+ wd = 'foobar0'
2438+ self.make_branch_and_tree(wd)
2439+ os.chdir(wd)
2440+ self._mk_versioned_dir('dir0') # rev1
2441+ self._mk_versioned_file('dir0/file0.txt') # rev2
2442+ self._update_file('dir0/file0.txt', "v3 text\n") # rev3
2443+ self._update_file('dir0/file0.txt', "v4 text\n") # rev4
2444+ self._update_file('dir0/file0.txt', "v5 text\n") # rev5
2445+ self._update_file('dir0/file0.txt', "v6 text\n") # rev6
2446+
2447+ out, err = self.run_bzr(['grep', '-r', '2..5', 'v3', 'dir0'])
2448+ self.assertContainsRe(out, "^dir0/file0.txt~3:v3", flags=TestGrep._reflags)
2449+ self.assertContainsRe(out, "^dir0/file0.txt~4:v3", flags=TestGrep._reflags)
2450+ self.assertContainsRe(out, "^dir0/file0.txt~5:v3", flags=TestGrep._reflags)
2451+ self.assertNotContainsRe(out, "^dir0/file0.txt~6:v3", flags=TestGrep._reflags)
2452+
2453+ out, err = self.run_bzr(['grep', '-r', '2..5', '[tuv]3', 'dir0'])
2454+ self.assertContainsRe(out, "^dir0/file0.txt~3:v3", flags=TestGrep._reflags)
2455+ self.assertContainsRe(out, "^dir0/file0.txt~4:v3", flags=TestGrep._reflags)
2456+ self.assertContainsRe(out, "^dir0/file0.txt~5:v3", flags=TestGrep._reflags)
2457+ self.assertNotContainsRe(out, "^dir0/file0.txt~6:v3", flags=TestGrep._reflags)
2458+
2459+ def test_levels(self):
2460+ """--levels=0 should show findings from merged revision.
2461+ """
2462+ wd0 = 'foobar0'
2463+ wd1 = 'foobar1'
2464+
2465+ self.make_branch_and_tree(wd0)
2466+ os.chdir(wd0)
2467+ self._mk_versioned_file('file0.txt')
2468+ os.chdir('..')
2469+
2470+ out, err = self.run_bzr(['branch', wd0, wd1])
2471+ os.chdir(wd1)
2472+ self._mk_versioned_file('file1.txt')
2473+ os.chdir(osutils.pathjoin('..', wd0))
2474+
2475+ out, err = self.run_bzr(['merge', osutils.pathjoin('..', wd1)])
2476+ out, err = self.run_bzr(['ci', '-m', 'merged'])
2477+
2478+ out, err = self.run_bzr(['grep', 'line1'])
2479+ self.assertContainsRe(out, "file0.txt:line1", flags=TestGrep._reflags)
2480+ self.assertContainsRe(out, "file1.txt:line1", flags=TestGrep._reflags)
2481+
2482+ # levels should be ignored by wtree grep
2483+ out, err = self.run_bzr(['grep', '--levels=0', 'line1'])
2484+ self.assertContainsRe(out, "^file0.txt:line1$", flags=TestGrep._reflags)
2485+ self.assertContainsRe(out, "^file1.txt:line1$", flags=TestGrep._reflags)
2486+ self.assertContainsRe(out, "^file0.txt:line10$", flags=TestGrep._reflags)
2487+ self.assertContainsRe(out, "^file1.txt:line10$", flags=TestGrep._reflags)
2488+ self.assertEqual(len(out.splitlines()), 4)
2489+
2490+ out, err = self.run_bzr(['grep', '-r', 'last:1..', '--levels=0', 'line1'])
2491+ self.assertContainsRe(out, "^file0.txt~2:line1$", flags=TestGrep._reflags)
2492+ self.assertContainsRe(out, "^file1.txt~2:line1$", flags=TestGrep._reflags)
2493+ self.assertContainsRe(out, "^file0.txt~1.1.1:line1$", flags=TestGrep._reflags)
2494+ self.assertContainsRe(out, "^file1.txt~1.1.1:line1$", flags=TestGrep._reflags)
2495+ self.assertContainsRe(out, "^file0.txt~2:line10$", flags=TestGrep._reflags)
2496+ self.assertContainsRe(out, "^file1.txt~2:line10$", flags=TestGrep._reflags)
2497+ self.assertContainsRe(out, "^file0.txt~1.1.1:line10$", flags=TestGrep._reflags)
2498+ self.assertContainsRe(out, "^file1.txt~1.1.1:line10$", flags=TestGrep._reflags)
2499+ self.assertEqual(len(out.splitlines()), 8)
2500+
2501+ out, err = self.run_bzr(['grep', '-r', '-1..', '-n', '--levels=0', 'line1'])
2502+ self.assertContainsRe(out, "^file0.txt~2:1:line1$", flags=TestGrep._reflags)
2503+ self.assertContainsRe(out, "^file1.txt~2:1:line1$", flags=TestGrep._reflags)
2504+ self.assertContainsRe(out, "^file0.txt~1.1.1:1:line1$", flags=TestGrep._reflags)
2505+ self.assertContainsRe(out, "^file1.txt~1.1.1:1:line1$", flags=TestGrep._reflags)
2506+ self.assertContainsRe(out, "^file0.txt~2:10:line10$", flags=TestGrep._reflags)
2507+ self.assertContainsRe(out, "^file1.txt~2:10:line10$", flags=TestGrep._reflags)
2508+ self.assertContainsRe(out, "^file0.txt~1.1.1:10:line10$", flags=TestGrep._reflags)
2509+ self.assertContainsRe(out, "^file1.txt~1.1.1:10:line10$", flags=TestGrep._reflags)
2510+ self.assertEqual(len(out.splitlines()), 8)
2511+
2512+ # levels should be ignored by wtree grep
2513+ out, err = self.run_bzr(['grep', '--levels=0', 'l.ne1'])
2514+ self.assertContainsRe(out, "^file0.txt:line1$", flags=TestGrep._reflags)
2515+ self.assertContainsRe(out, "^file1.txt:line1$", flags=TestGrep._reflags)
2516+ self.assertContainsRe(out, "^file0.txt:line10$", flags=TestGrep._reflags)
2517+ self.assertContainsRe(out, "^file1.txt:line10$", flags=TestGrep._reflags)
2518+ self.assertEqual(len(out.splitlines()), 4)
2519+
2520+ out, err = self.run_bzr(['grep', '-r', 'last:1..', '--levels=0', 'lin.1'])
2521+ self.assertContainsRe(out, "^file0.txt~2:line1$", flags=TestGrep._reflags)
2522+ self.assertContainsRe(out, "^file1.txt~2:line1$", flags=TestGrep._reflags)
2523+ self.assertContainsRe(out, "^file0.txt~1.1.1:line1$", flags=TestGrep._reflags)
2524+ self.assertContainsRe(out, "^file1.txt~1.1.1:line1$", flags=TestGrep._reflags)
2525+ self.assertContainsRe(out, "^file0.txt~2:line10$", flags=TestGrep._reflags)
2526+ self.assertContainsRe(out, "^file1.txt~2:line10$", flags=TestGrep._reflags)
2527+ self.assertContainsRe(out, "^file0.txt~1.1.1:line10$", flags=TestGrep._reflags)
2528+ self.assertContainsRe(out, "^file1.txt~1.1.1:line10$", flags=TestGrep._reflags)
2529+ self.assertEqual(len(out.splitlines()), 8)
2530+
2531+ out, err = self.run_bzr(['grep', '-r', '-1..', '-n', '--levels=0', '.ine1'])
2532+ self.assertContainsRe(out, "file0.txt~2:1:line1", flags=TestGrep._reflags)
2533+ self.assertContainsRe(out, "file1.txt~2:1:line1", flags=TestGrep._reflags)
2534+ self.assertContainsRe(out, "file0.txt~1.1.1:1:line1", flags=TestGrep._reflags)
2535+ self.assertContainsRe(out, "file1.txt~1.1.1:1:line1", flags=TestGrep._reflags)
2536+
2537+ def test_dotted_rev_grep(self):
2538+ """Grep in dotted revs
2539+ """
2540+ wd0 = 'foobar0'
2541+ wd1 = 'foobar1'
2542+
2543+ self.make_branch_and_tree(wd0)
2544+ os.chdir(wd0)
2545+ self._mk_versioned_file('file0.txt')
2546+ os.chdir('..')
2547+
2548+ out, err = self.run_bzr(['branch', wd0, wd1])
2549+ os.chdir(wd1)
2550+ self._mk_versioned_file('file1.txt') # revno 1.1.1
2551+ self._update_file('file1.txt', "text 0\n") # revno 1.1.2
2552+ self._update_file('file1.txt', "text 1\n") # revno 1.1.3
2553+ self._update_file('file1.txt', "text 2\n") # revno 1.1.4
2554+ os.chdir(osutils.pathjoin('..', wd0))
2555+
2556+ out, err = self.run_bzr(['merge', osutils.pathjoin('..', wd1)])
2557+ out, err = self.run_bzr(['ci', '-m', 'merged'])
2558+
2559+ out, err = self.run_bzr(['grep', '-r', '1.1.1..1.1.4', 'text'])
2560+ self.assertContainsRe(out, "file1.txt~1.1.2:text 0", flags=TestGrep._reflags)
2561+ self.assertContainsRe(out, "file1.txt~1.1.3:text 1", flags=TestGrep._reflags)
2562+ self.assertContainsRe(out, "file1.txt~1.1.3:text 1", flags=TestGrep._reflags)
2563+ self.assertContainsRe(out, "file1.txt~1.1.4:text 0", flags=TestGrep._reflags)
2564+ self.assertContainsRe(out, "file1.txt~1.1.4:text 1", flags=TestGrep._reflags)
2565+ self.assertContainsRe(out, "file1.txt~1.1.4:text 2", flags=TestGrep._reflags)
2566+ self.assertEqual(len(out.splitlines()), 6)
2567+
2568+ def test_versioned_binary_file_grep(self):
2569+ """(versioned) Grep for pattern in binary file.
2570+ """
2571+ wd = 'foobar0'
2572+ self.make_branch_and_tree(wd)
2573+ os.chdir(wd)
2574+ self._mk_versioned_file('file.txt')
2575+ self._mk_versioned_file('file0.bin')
2576+ self._update_file('file0.bin', "\x00lineNN\x00\n")
2577+
2578+ # note: set --verbose/-v flag to get the skip message.
2579+ out, err = self.run_bzr(['grep', '-v', '-r', 'last:1',
2580+ 'lineNN', 'file0.bin'])
2581+ self.assertNotContainsRe(out, "file0.bin", flags=TestGrep._reflags)
2582+ self.assertContainsRe(err, "Binary file.*file0.bin.*skipped", flags=TestGrep._reflags)
2583+ self.assertEqual(len(out.splitlines()), 0)
2584+ self.assertEqual(len(err.splitlines()), 1)
2585+
2586+ out, err = self.run_bzr(['grep', '-v', '-r', 'last:1',
2587+ 'line.N', 'file0.bin'])
2588+ self.assertNotContainsRe(out, "file0.bin", flags=TestGrep._reflags)
2589+ self.assertContainsRe(err, "Binary file.*file0.bin.*skipped", flags=TestGrep._reflags)
2590+ self.assertEqual(len(out.splitlines()), 0)
2591+ self.assertEqual(len(err.splitlines()), 1)
2592+
2593+ def test_wtree_binary_file_grep(self):
2594+ """(wtree) Grep for pattern in binary file.
2595+ """
2596+ wd = 'foobar0'
2597+ self.make_branch_and_tree(wd)
2598+ os.chdir(wd)
2599+ self._mk_versioned_file('file0.bin')
2600+ self._update_file('file0.bin', "\x00lineNN\x00\n")
2601+
2602+ # note: set --verbose/-v flag to get the skip message.
2603+ out, err = self.run_bzr(['grep', '-v', 'lineNN', 'file0.bin'])
2604+ self.assertNotContainsRe(out, "file0.bin:line1", flags=TestGrep._reflags)
2605+ self.assertContainsRe(err, "Binary file.*file0.bin.*skipped", flags=TestGrep._reflags)
2606+
2607+ # binary warning should not be shown without --verbose
2608+ out, err = self.run_bzr(['grep', 'lineNN', 'file0.bin'])
2609+ self.assertNotContainsRe(out, "file0.bin:line1", flags=TestGrep._reflags)
2610+ self.assertNotContainsRe(err, "Binary file", flags=TestGrep._reflags)
2611+
2612+ def test_revspec(self):
2613+ """Ensure various revspecs work
2614+ """
2615+ wd = 'foobar0'
2616+ self.make_branch_and_tree(wd)
2617+ os.chdir(wd)
2618+ self._mk_versioned_dir('dir0') # rev1
2619+ self._mk_versioned_file('dir0/file0.txt') # rev2
2620+ self._update_file('dir0/file0.txt', "v3 text\n") # rev3
2621+ self._update_file('dir0/file0.txt', "v4 text\n") # rev4
2622+ self._update_file('dir0/file0.txt', "v5 text\n") # rev5
2623+
2624+ out, err = self.run_bzr(['grep', '-r', 'revno:1..2', 'v3'])
2625+ self.assertNotContainsRe(out, "file0", flags=TestGrep._reflags)
2626+ self.assertEqual(len(out.splitlines()), 0)
2627+
2628+ out, err = self.run_bzr(['grep', '-r', 'revno:4..', 'v4'])
2629+ self.assertContainsRe(out, "^dir0/file0.txt", flags=TestGrep._reflags)
2630+ self.assertEqual(len(out.splitlines()), 2) # find v4 in rev4 and rev5
2631+
2632+ out, err = self.run_bzr(['grep', '-r', '..revno:3', 'v4'])
2633+ self.assertNotContainsRe(out, "file0", flags=TestGrep._reflags)
2634+ self.assertEqual(len(out.splitlines()), 0)
2635+
2636+ out, err = self.run_bzr(['grep', '-r', '..revno:3', 'v3'])
2637+ self.assertContainsRe(out, "^dir0/file0.txt", flags=TestGrep._reflags)
2638+ self.assertEqual(len(out.splitlines()), 1)
2639+
2640+ def test_wtree_files_with_matches(self):
2641+ """(wtree) Ensure --files-with-matches, -l works
2642+ """
2643+ wd = 'foobar0'
2644+ self.make_branch_and_tree(wd)
2645+ os.chdir(wd)
2646+
2647+ self._mk_versioned_file('file0.txt', total_lines=2)
2648+ self._mk_versioned_file('file1.txt', total_lines=2)
2649+ self._mk_versioned_dir('dir0')
2650+ self._mk_versioned_file('dir0/file00.txt', total_lines=2)
2651+ self._mk_versioned_file('dir0/file01.txt', total_lines=2)
2652+
2653+ self._update_file('file0.txt', 'HELLO\n', checkin=False)
2654+ self._update_file('dir0/file00.txt', 'HELLO\n', checkin=False)
2655+
2656+ # fixed-string
2657+ out, err = self.run_bzr(['grep', '--files-with-matches', 'HELLO'])
2658+
2659+ self.assertContainsRe(out, "^file0.txt$", flags=TestGrep._reflags)
2660+ self.assertContainsRe(out, "^dir0/file00.txt$", flags=TestGrep._reflags)
2661+ self.assertEqual(len(out.splitlines()), 2)
2662+
2663+ # regex
2664+ out, err = self.run_bzr(['grep', '--files-with-matches', 'HE.LO'])
2665+
2666+ self.assertContainsRe(out, "^file0.txt$", flags=TestGrep._reflags)
2667+ self.assertContainsRe(out, "^dir0/file00.txt$", flags=TestGrep._reflags)
2668+ self.assertEqual(len(out.splitlines()), 2)
2669+
2670+ # fixed-string
2671+ out, err = self.run_bzr(['grep', '-l', 'HELLO'])
2672+
2673+ self.assertContainsRe(out, "^file0.txt$", flags=TestGrep._reflags)
2674+ self.assertContainsRe(out, "^dir0/file00.txt$", flags=TestGrep._reflags)
2675+ self.assertEqual(len(out.splitlines()), 2)
2676+
2677+ # regex
2678+ out, err = self.run_bzr(['grep', '-l', 'HE.LO'])
2679+
2680+ self.assertContainsRe(out, "^file0.txt$", flags=TestGrep._reflags)
2681+ self.assertContainsRe(out, "^dir0/file00.txt$", flags=TestGrep._reflags)
2682+ self.assertEqual(len(out.splitlines()), 2)
2683+
2684+ # fixed-string
2685+ out, err = self.run_bzr(['grep', '-l', 'HELLO', 'dir0', 'file1.txt'])
2686+
2687+ self.assertContainsRe(out, "^dir0/file00.txt$", flags=TestGrep._reflags)
2688+ self.assertEqual(len(out.splitlines()), 1)
2689+
2690+ # regex
2691+ out, err = self.run_bzr(['grep', '-l', '.ELLO', 'dir0', 'file1.txt'])
2692+
2693+ self.assertContainsRe(out, "^dir0/file00.txt$", flags=TestGrep._reflags)
2694+ self.assertEqual(len(out.splitlines()), 1)
2695+
2696+ # fixed-string
2697+ out, err = self.run_bzr(['grep', '-l', 'HELLO', 'file0.txt'])
2698+
2699+ self.assertContainsRe(out, "^file0.txt$", flags=TestGrep._reflags)
2700+ self.assertEqual(len(out.splitlines()), 1)
2701+
2702+ # regex
2703+ out, err = self.run_bzr(['grep', '-l', '.ELLO', 'file0.txt'])
2704+
2705+ self.assertContainsRe(out, "^file0.txt$", flags=TestGrep._reflags)
2706+ self.assertEqual(len(out.splitlines()), 1)
2707+
2708+ # fixed-string
2709+ out, err = self.run_bzr(['grep', '--no-recursive', '-l', 'HELLO'])
2710+
2711+ self.assertContainsRe(out, "^file0.txt$", flags=TestGrep._reflags)
2712+ self.assertEqual(len(out.splitlines()), 1)
2713+
2714+ # regex
2715+ out, err = self.run_bzr(['grep', '--no-recursive', '-l', '.ELLO'])
2716+
2717+ self.assertContainsRe(out, "^file0.txt$", flags=TestGrep._reflags)
2718+ self.assertEqual(len(out.splitlines()), 1)
2719+
2720+ def test_ver_files_with_matches(self):
2721+ """(ver) Ensure --files-with-matches, -l works
2722+ """
2723+ wd = 'foobar0'
2724+ self.make_branch_and_tree(wd)
2725+ os.chdir(wd)
2726+
2727+ self._mk_versioned_file('file0.txt', total_lines=2) # rev 1
2728+ self._mk_versioned_file('file1.txt', total_lines=2) # rev 2
2729+ self._mk_versioned_dir('dir0') # rev 3
2730+ self._mk_versioned_file('dir0/file00.txt', total_lines=2) # rev 4
2731+ self._mk_versioned_file('dir0/file01.txt', total_lines=2) # rev 5
2732+
2733+ self._update_file('file0.txt', 'HELLO\n') # rev 6
2734+ self._update_file('dir0/file00.txt', 'HELLO\n') # rev 7
2735+
2736+ # fixed-string
2737+ out, err = self.run_bzr(['grep', '-r', '-1', '--files-with-matches',
2738+ 'HELLO'])
2739+
2740+ self.assertContainsRe(out, "^file0.txt~7$", flags=TestGrep._reflags)
2741+ self.assertContainsRe(out, "^dir0/file00.txt~7$", flags=TestGrep._reflags)
2742+ self.assertEqual(len(out.splitlines()), 2)
2743+
2744+ # regex
2745+ out, err = self.run_bzr(['grep', '-r', '-1', '--files-with-matches',
2746+ 'H.LLO'])
2747+
2748+ self.assertContainsRe(out, "^file0.txt~7$", flags=TestGrep._reflags)
2749+ self.assertContainsRe(out, "^dir0/file00.txt~7$", flags=TestGrep._reflags)
2750+ self.assertEqual(len(out.splitlines()), 2)
2751+
2752+ # fixed-string
2753+ out, err = self.run_bzr(['grep', '-r', '6..7', '--files-with-matches',
2754+ 'HELLO'])
2755+
2756+ self.assertContainsRe(out, "^file0.txt~6$", flags=TestGrep._reflags)
2757+ self.assertContainsRe(out, "^file0.txt~7$", flags=TestGrep._reflags)
2758+ self.assertContainsRe(out, "^dir0/file00.txt~7$", flags=TestGrep._reflags)
2759+ self.assertEqual(len(out.splitlines()), 3)
2760+
2761+ # regex
2762+ out, err = self.run_bzr(['grep', '-r', '6..7', '--files-with-matches',
2763+ 'H.LLO'])
2764+
2765+ self.assertContainsRe(out, "^file0.txt~6$", flags=TestGrep._reflags)
2766+ self.assertContainsRe(out, "^file0.txt~7$", flags=TestGrep._reflags)
2767+ self.assertContainsRe(out, "^dir0/file00.txt~7$", flags=TestGrep._reflags)
2768+ self.assertEqual(len(out.splitlines()), 3)
2769+
2770+ # fixed-string
2771+ out, err = self.run_bzr(['grep', '-r', '-1', '-l', 'HELLO'])
2772+
2773+ self.assertContainsRe(out, "^file0.txt~7$", flags=TestGrep._reflags)
2774+ self.assertContainsRe(out, "^dir0/file00.txt~7$", flags=TestGrep._reflags)
2775+ self.assertEqual(len(out.splitlines()), 2)
2776+
2777+ # regex
2778+ out, err = self.run_bzr(['grep', '-r', '-1', '-l', 'H.LLO'])
2779+
2780+ self.assertContainsRe(out, "^file0.txt~7$", flags=TestGrep._reflags)
2781+ self.assertContainsRe(out, "^dir0/file00.txt~7$", flags=TestGrep._reflags)
2782+ self.assertEqual(len(out.splitlines()), 2)
2783+
2784+ # fixed-string
2785+ out, err = self.run_bzr(['grep', '-l', 'HELLO', '-r', '-1',
2786+ 'dir0', 'file1.txt'])
2787+
2788+ self.assertContainsRe(out, "^dir0/file00.txt~7$", flags=TestGrep._reflags)
2789+ self.assertEqual(len(out.splitlines()), 1)
2790+
2791+ # regex
2792+ out, err = self.run_bzr(['grep', '-l', 'H.LLO', '-r', '-1',
2793+ 'dir0', 'file1.txt'])
2794+
2795+ self.assertContainsRe(out, "^dir0/file00.txt~7$", flags=TestGrep._reflags)
2796+ self.assertEqual(len(out.splitlines()), 1)
2797+
2798+ # fixed-string
2799+ out, err = self.run_bzr(['grep', '-l', 'HELLO',
2800+ '-r', '-2', 'file0.txt'])
2801+
2802+ self.assertContainsRe(out, "^file0.txt~6$", flags=TestGrep._reflags)
2803+ self.assertEqual(len(out.splitlines()), 1)
2804+
2805+ # regex
2806+ out, err = self.run_bzr(['grep', '-l', 'HE.LO',
2807+ '-r', '-2', 'file0.txt'])
2808+
2809+ self.assertContainsRe(out, "^file0.txt~6$", flags=TestGrep._reflags)
2810+ self.assertEqual(len(out.splitlines()), 1)
2811+
2812+ # fixed-string
2813+ out, err = self.run_bzr(['grep', '--no-recursive', '-r', '-1',
2814+ '-l', 'HELLO'])
2815+
2816+ self.assertContainsRe(out, "^file0.txt~7$", flags=TestGrep._reflags)
2817+ self.assertEqual(len(out.splitlines()), 1)
2818+
2819+ # regex
2820+ out, err = self.run_bzr(['grep', '--no-recursive', '-r', '-1',
2821+ '-l', '.ELLO'])
2822+
2823+ self.assertContainsRe(out, "^file0.txt~7$", flags=TestGrep._reflags)
2824+ self.assertEqual(len(out.splitlines()), 1)
2825+
2826+ def test_wtree_files_without_matches(self):
2827+ """(wtree) Ensure --files-without-match, -L works
2828+ """
2829+ wd = 'foobar0'
2830+ self.make_branch_and_tree(wd)
2831+ os.chdir(wd)
2832+
2833+ self._mk_versioned_file('file0.txt', total_lines=2)
2834+ self._mk_versioned_file('file1.txt', total_lines=2)
2835+ self._mk_versioned_dir('dir0')
2836+ self._mk_versioned_file('dir0/file00.txt', total_lines=2)
2837+ self._mk_versioned_file('dir0/file01.txt', total_lines=2)
2838+
2839+ self._update_file('file0.txt', 'HELLO\n', checkin=False)
2840+ self._update_file('dir0/file00.txt', 'HELLO\n', checkin=False)
2841+
2842+ # fixed-string
2843+ out, err = self.run_bzr(['grep', '--files-without-match', 'HELLO'])
2844+
2845+ self.assertContainsRe(out, "^file1.txt$", flags=TestGrep._reflags)
2846+ self.assertContainsRe(out, "^dir0/file01.txt$", flags=TestGrep._reflags)
2847+ self.assertEqual(len(out.splitlines()), 2)
2848+
2849+ # regex
2850+ out, err = self.run_bzr(['grep', '--files-without-match', 'HE.LO'])
2851+
2852+ self.assertContainsRe(out, "^file1.txt$", flags=TestGrep._reflags)
2853+ self.assertContainsRe(out, "^dir0/file01.txt$", flags=TestGrep._reflags)
2854+ self.assertEqual(len(out.splitlines()), 2)
2855+
2856+ # fixed-string
2857+ out, err = self.run_bzr(['grep', '-L', 'HELLO'])
2858+
2859+ self.assertContainsRe(out, "^file1.txt$", flags=TestGrep._reflags)
2860+ self.assertContainsRe(out, "^dir0/file01.txt$", flags=TestGrep._reflags)
2861+ self.assertEqual(len(out.splitlines()), 2)
2862+
2863+ # regex
2864+ out, err = self.run_bzr(['grep', '-L', 'HE.LO'])
2865+
2866+ self.assertContainsRe(out, "^file1.txt$", flags=TestGrep._reflags)
2867+ self.assertContainsRe(out, "^dir0/file01.txt$", flags=TestGrep._reflags)
2868+ self.assertEqual(len(out.splitlines()), 2)
2869+
2870+ # fixed-string
2871+ out, err = self.run_bzr(['grep', '-L', 'HELLO', 'dir0', 'file1.txt'])
2872+
2873+ self.assertContainsRe(out, "^file1.txt$", flags=TestGrep._reflags)
2874+ self.assertContainsRe(out, "^dir0/file01.txt$", flags=TestGrep._reflags)
2875+ self.assertEqual(len(out.splitlines()), 2)
2876+
2877+ # regex
2878+ out, err = self.run_bzr(['grep', '-L', '.ELLO', 'dir0', 'file1.txt'])
2879+
2880+ self.assertContainsRe(out, "^file1.txt$", flags=TestGrep._reflags)
2881+ self.assertContainsRe(out, "^dir0/file01.txt$", flags=TestGrep._reflags)
2882+ self.assertEqual(len(out.splitlines()), 2)
2883+
2884+ # fixed-string
2885+ out, err = self.run_bzr(['grep', '-L', 'HELLO', 'file1.txt'])
2886+
2887+ self.assertContainsRe(out, "^file1.txt$", flags=TestGrep._reflags)
2888+ self.assertEqual(len(out.splitlines()), 1)
2889+
2890+ # regex
2891+ out, err = self.run_bzr(['grep', '-L', '.ELLO', 'file1.txt'])
2892+
2893+ self.assertContainsRe(out, "^file1.txt$", flags=TestGrep._reflags)
2894+ self.assertEqual(len(out.splitlines()), 1)
2895+
2896+ # fixed-string
2897+ out, err = self.run_bzr(['grep', '--no-recursive', '-L', 'HELLO'])
2898+
2899+ self.assertContainsRe(out, "^file1.txt$", flags=TestGrep._reflags)
2900+ self.assertEqual(len(out.splitlines()), 1)
2901+
2902+ # regex
2903+ out, err = self.run_bzr(['grep', '--no-recursive', '-L', '.ELLO'])
2904+
2905+ self.assertContainsRe(out, "^file1.txt$", flags=TestGrep._reflags)
2906+ self.assertEqual(len(out.splitlines()), 1)
2907+
2908+ def test_ver_files_without_matches(self):
2909+ """(ver) Ensure --files-without-match, -L works
2910+ """
2911+ wd = 'foobar0'
2912+ self.make_branch_and_tree(wd)
2913+ os.chdir(wd)
2914+
2915+ self._mk_versioned_file('file0.txt', total_lines=2) # rev 1
2916+ self._mk_versioned_file('file1.txt', total_lines=2) # rev 2
2917+ self._mk_versioned_dir('dir0') # rev 3
2918+ self._mk_versioned_file('dir0/file00.txt', total_lines=2) # rev 4
2919+ self._mk_versioned_file('dir0/file01.txt', total_lines=2) # rev 5
2920+
2921+ self._update_file('file0.txt', 'HELLO\n') # rev 6
2922+ self._update_file('dir0/file00.txt', 'HELLO\n') # rev 7
2923+
2924+ # fixed-string
2925+ out, err = self.run_bzr(['grep', '-r', '-1', '--files-without-match',
2926+ 'HELLO'])
2927+
2928+ self.assertContainsRe(out, "^file1.txt~7$", flags=TestGrep._reflags)
2929+ self.assertContainsRe(out, "^dir0/file01.txt~7$", flags=TestGrep._reflags)
2930+ self.assertEqual(len(out.splitlines()), 2)
2931+
2932+ # regex
2933+ out, err = self.run_bzr(['grep', '-r', '-1', '--files-without-match',
2934+ 'H.LLO'])
2935+
2936+ self.assertContainsRe(out, "^file1.txt~7$", flags=TestGrep._reflags)
2937+ self.assertContainsRe(out, "^dir0/file01.txt~7$", flags=TestGrep._reflags)
2938+ self.assertEqual(len(out.splitlines()), 2)
2939+
2940+ # fixed-string
2941+ out, err = self.run_bzr(['grep', '-r', '6..7', '--files-without-match',
2942+ 'HELLO'])
2943+
2944+ self.assertContainsRe(out, "^file1.txt~6$", flags=TestGrep._reflags)
2945+ self.assertContainsRe(out, "^dir0/file00.txt~6$", flags=TestGrep._reflags)
2946+ self.assertContainsRe(out, "^dir0/file01.txt~6$", flags=TestGrep._reflags)
2947+ self.assertContainsRe(out, "^file1.txt~7$", flags=TestGrep._reflags)
2948+ self.assertContainsRe(out, "^dir0/file01.txt~7$", flags=TestGrep._reflags)
2949+ self.assertEqual(len(out.splitlines()), 5)
2950+
2951+ # regex
2952+ out, err = self.run_bzr(['grep', '-r', '6..7', '--files-without-match',
2953+ 'H.LLO'])
2954+
2955+ self.assertContainsRe(out, "^file1.txt~6$", flags=TestGrep._reflags)
2956+ self.assertContainsRe(out, "^dir0/file00.txt~6$", flags=TestGrep._reflags)
2957+ self.assertContainsRe(out, "^dir0/file01.txt~6$", flags=TestGrep._reflags)
2958+ self.assertContainsRe(out, "^file1.txt~7$", flags=TestGrep._reflags)
2959+ self.assertContainsRe(out, "^dir0/file01.txt~7$", flags=TestGrep._reflags)
2960+ self.assertEqual(len(out.splitlines()), 5)
2961+
2962+ # fixed-string
2963+ out, err = self.run_bzr(['grep', '-r', '-1', '-L', 'HELLO'])
2964+
2965+ self.assertContainsRe(out, "^file1.txt~7$", flags=TestGrep._reflags)
2966+ self.assertContainsRe(out, "^dir0/file01.txt~7$", flags=TestGrep._reflags)
2967+ self.assertEqual(len(out.splitlines()), 2)
2968+
2969+ # regex
2970+ out, err = self.run_bzr(['grep', '-r', '-1', '-L', 'H.LLO'])
2971+
2972+ self.assertContainsRe(out, "^file1.txt~7$", flags=TestGrep._reflags)
2973+ self.assertContainsRe(out, "^dir0/file01.txt~7$", flags=TestGrep._reflags)
2974+ self.assertEqual(len(out.splitlines()), 2)
2975+
2976+ # fixed-string
2977+ out, err = self.run_bzr(['grep', '-L', 'HELLO', '-r', '-1',
2978+ 'dir0', 'file1.txt'])
2979+
2980+ self.assertContainsRe(out, "^file1.txt~7$", flags=TestGrep._reflags)
2981+ self.assertContainsRe(out, "^dir0/file01.txt~7$", flags=TestGrep._reflags)
2982+ self.assertEqual(len(out.splitlines()), 2)
2983+
2984+ # regex
2985+ out, err = self.run_bzr(['grep', '-L', 'H.LLO', '-r', '-1',
2986+ 'dir0', 'file1.txt'])
2987+
2988+ self.assertContainsRe(out, "^file1.txt~7$", flags=TestGrep._reflags)
2989+ self.assertContainsRe(out, "^dir0/file01.txt~7$", flags=TestGrep._reflags)
2990+ self.assertEqual(len(out.splitlines()), 2)
2991+
2992+ # fixed-string
2993+ out, err = self.run_bzr(['grep', '-L', 'HELLO',
2994+ '-r', '-2', 'file1.txt'])
2995+
2996+ self.assertContainsRe(out, "^file1.txt~6$", flags=TestGrep._reflags)
2997+ self.assertEqual(len(out.splitlines()), 1)
2998+
2999+ # regex
3000+ out, err = self.run_bzr(['grep', '-L', 'HE.LO',
3001+ '-r', '-2', 'file1.txt'])
3002+
3003+ self.assertContainsRe(out, "^file1.txt~6$", flags=TestGrep._reflags)
3004+ self.assertEqual(len(out.splitlines()), 1)
3005+
3006+ # fixed-string
3007+ out, err = self.run_bzr(['grep', '--no-recursive', '-r', '-1',
3008+ '-L', 'HELLO'])
3009+
3010+ self.assertContainsRe(out, "^file1.txt~7$", flags=TestGrep._reflags)
3011+ self.assertEqual(len(out.splitlines()), 1)
3012+
3013+ # regex
3014+ out, err = self.run_bzr(['grep', '--no-recursive', '-r', '-1',
3015+ '-L', '.ELLO'])
3016+
3017+ self.assertContainsRe(out, "^file1.txt~7$", flags=TestGrep._reflags)
3018+ self.assertEqual(len(out.splitlines()), 1)
3019+
3020+ def test_no_tree(self):
3021+ """Ensure grep works without working tree.
3022+ """
3023+ wd0 = 'foobar0'
3024+ wd1 = 'foobar1'
3025+ self.make_branch_and_tree(wd0)
3026+ os.chdir(wd0)
3027+ self._mk_versioned_file('file0.txt')
3028+ os.chdir('..')
3029+ out, err = self.run_bzr(['branch', '--no-tree', wd0, wd1])
3030+ os.chdir(wd1)
3031+
3032+ out, err = self.run_bzr(['grep', 'line1'], 3)
3033+ self.assertContainsRe(err, "Cannot search working tree", flags=TestGrep._reflags)
3034+ self.assertEqual(out, '')
3035+
3036+ out, err = self.run_bzr(['grep', '-r', '1', 'line1'])
3037+ self.assertContainsRe(out, "file0.txt~1:line1", flags=TestGrep._reflags)
3038+ self.assertEqual(len(out.splitlines()), 2) # finds line1 and line10
3039+
3040+
3041+class TestNonAscii(GrepTestBase):
3042+ """Tests for non-ascii filenames and file contents"""
3043+
3044+ _test_needs_features = [UnicodeFilenameFeature]
3045+
3046+ def test_unicode_only_file(self):
3047+ """Test filename and contents that requires a unicode encoding"""
3048+ tree = self.make_branch_and_tree(".")
3049+ contents = [u"\u1234"]
3050+ self.build_tree(contents)
3051+ tree.add(contents)
3052+ tree.commit("Initial commit")
3053+ as_utf8 = u"\u1234".encode("UTF-8")
3054+
3055+ # GZ 2010-06-07: Note we can't actually grep for \u1234 as the pattern
3056+ # is mangled according to the user encoding.
3057+ streams = self.run_bzr(["grep", "--files-with-matches",
3058+ u"contents"], encoding="UTF-8")
3059+ self.assertEqual(streams, (as_utf8 + "\n", ""))
3060+
3061+ streams = self.run_bzr(["grep", "-r", "1", "--files-with-matches",
3062+ u"contents"], encoding="UTF-8")
3063+ self.assertEqual(streams, (as_utf8 + "~1\n", ""))
3064+
3065+ fileencoding = osutils.get_user_encoding()
3066+ as_mangled = as_utf8.decode(fileencoding, "replace").encode("UTF-8")
3067+
3068+ streams = self.run_bzr(["grep", "-n",
3069+ u"contents"], encoding="UTF-8")
3070+ self.assertEqual(streams, ("%s:1:contents of %s\n" %
3071+ (as_utf8, as_mangled), ""))
3072+
3073+ streams = self.run_bzr(["grep", "-n", "-r", "1",
3074+ u"contents"], encoding="UTF-8")
3075+ self.assertEqual(streams, ("%s~1:1:contents of %s\n" %
3076+ (as_utf8, as_mangled), ""))
3077+
3078+
3079+class TestColorGrep(GrepTestBase):
3080+ """Tests for the --color option."""
3081+
3082+ # GZ 2010-06-05: Does this really require the feature? Nothing prints.
3083+ _test_needs_features = [ColorFeature]
3084+
3085+ _rev_sep = color_string('~', fg=FG.BOLD_YELLOW)
3086+ _sep = color_string(':', fg=FG.BOLD_CYAN)
3087+
3088+ def test_color_option(self):
3089+ """Ensure options for color are valid.
3090+ """
3091+ out, err = self.run_bzr(['grep', '--color', 'foo', 'bar'], 3)
3092+ self.assertEqual(out, '')
3093+ self.assertContainsRe(err, 'Valid values for --color are', flags=TestGrep._reflags)
3094+
3095+ def test_ver_matching_files(self):
3096+ """(versioned) Search for matches or no matches only"""
3097+ tree = self.make_branch_and_tree(".")
3098+ contents = ["d/", "d/aaa", "bbb"]
3099+ self.build_tree(contents)
3100+ tree.add(contents)
3101+ tree.commit("Initial commit")
3102+
3103+ # GZ 2010-06-05: Maybe modify the working tree here
3104+
3105+ streams = self.run_bzr(["grep", "--color", "always", "-r", "1",
3106+ "--files-with-matches", "aaa"])
3107+ self.assertEqual(streams, ("".join([
3108+ FG.MAGENTA, "d/aaa", self._rev_sep, "1", "\n"
3109+ ]), ""))
3110+
3111+ streams = self.run_bzr(["grep", "--color", "always", "-r", "1",
3112+ "--files-without-match", "aaa"])
3113+ self.assertEqual(streams, ("".join([
3114+ FG.MAGENTA, "bbb", self._rev_sep, "1", "\n"
3115+ ]), ""))
3116+
3117+ def test_wtree_matching_files(self):
3118+ """(wtree) Search for matches or no matches only"""
3119+ tree = self.make_branch_and_tree(".")
3120+ contents = ["d/", "d/aaa", "bbb"]
3121+ self.build_tree(contents)
3122+ tree.add(contents)
3123+ tree.commit("Initial commit")
3124+
3125+ # GZ 2010-06-05: Maybe modify the working tree here
3126+
3127+ streams = self.run_bzr(["grep", "--color", "always",
3128+ "--files-with-matches", "aaa"])
3129+ self.assertEqual(streams, ("".join([
3130+ FG.MAGENTA, "d/aaa", FG.NONE, "\n"
3131+ ]), ""))
3132+
3133+ streams = self.run_bzr(["grep", "--color", "always",
3134+ "--files-without-match", "aaa"])
3135+ self.assertEqual(streams, ("".join([
3136+ FG.MAGENTA, "bbb", FG.NONE, "\n"
3137+ ]), ""))
3138+
3139+ def test_ver_basic_file(self):
3140+ """(versioned) Search for pattern in specfic file.
3141+ """
3142+ wd = 'foobar0'
3143+ self.make_branch_and_tree(wd)
3144+ os.chdir(wd)
3145+ lp = 'foo is foobar'
3146+ self._mk_versioned_file('file0.txt', line_prefix=lp, total_lines=1)
3147+
3148+ # prepare colored result
3149+ foo = color_string('foo', fg=FG.BOLD_RED)
3150+ res = (FG.MAGENTA + 'file0.txt'
3151+ + self._rev_sep + '1' + self._sep
3152+ + foo + ' is ' + foo + 'bar1' + '\n')
3153+ txt_res = 'file0.txt~1:foo is foobar1\n'
3154+
3155+ nres = (FG.MAGENTA + 'file0.txt'
3156+ + self._rev_sep + '1' + self._sep + '1' + self._sep
3157+ + foo + ' is ' + foo + 'bar1' + '\n')
3158+
3159+ out, err = self.run_bzr(['grep', '--color',
3160+ 'always', '-r', '1', 'foo'])
3161+ self.assertEqual(out, res)
3162+ self.assertEqual(len(out.splitlines()), 1)
3163+
3164+ # auto should produce plain text result
3165+ # as stdout is redireched here.
3166+ out, err = self.run_bzr(['grep', '--color',
3167+ 'auto', '-r', '1', 'foo'])
3168+ self.assertEqual(out, txt_res)
3169+ self.assertEqual(len(out.splitlines()), 1)
3170+
3171+ out, err = self.run_bzr(['grep', '-i', '--color',
3172+ 'always', '-r', '1', 'FOO'])
3173+ self.assertEqual(out, res)
3174+ self.assertEqual(len(out.splitlines()), 1)
3175+
3176+ out, err = self.run_bzr(['grep', '--color',
3177+ 'always', '-r', '1', 'f.o'])
3178+ self.assertEqual(out, res)
3179+ self.assertEqual(len(out.splitlines()), 1)
3180+
3181+ out, err = self.run_bzr(['grep', '-i', '--color',
3182+ 'always', '-r', '1', 'F.O'])
3183+ self.assertEqual(out, res)
3184+ self.assertEqual(len(out.splitlines()), 1)
3185+
3186+ out, err = self.run_bzr(['grep', '-n', '--color',
3187+ 'always', '-r', '1', 'foo'])
3188+ self.assertEqual(out, nres)
3189+ self.assertEqual(len(out.splitlines()), 1)
3190+
3191+ out, err = self.run_bzr(['grep', '-n', '-i', '--color',
3192+ 'always', '-r', '1', 'FOO'])
3193+ self.assertEqual(out, nres)
3194+ self.assertEqual(len(out.splitlines()), 1)
3195+
3196+ out, err = self.run_bzr(['grep', '-n', '--color',
3197+ 'always', '-r', '1', 'f.o'])
3198+ self.assertEqual(out, nres)
3199+ self.assertEqual(len(out.splitlines()), 1)
3200+
3201+ out, err = self.run_bzr(['grep', '-n', '-i', '--color',
3202+ 'always', '-r', '1', 'F.O'])
3203+ self.assertEqual(out, nres)
3204+ self.assertEqual(len(out.splitlines()), 1)
3205+
3206+ def test_wtree_basic_file(self):
3207+ """(wtree) Search for pattern in specfic file.
3208+ """
3209+ wd = 'foobar0'
3210+ self.make_branch_and_tree(wd)
3211+ os.chdir(wd)
3212+ lp = 'foo is foobar'
3213+ self._mk_versioned_file('file0.txt', line_prefix=lp, total_lines=1)
3214+
3215+ # prepare colored result
3216+ foo = color_string('foo', fg=FG.BOLD_RED)
3217+ res = (FG.MAGENTA + 'file0.txt'
3218+ + self._sep + foo + ' is ' + foo + 'bar1' + '\n')
3219+
3220+ nres = (FG.MAGENTA + 'file0.txt'
3221+ + self._sep + '1' + self._sep
3222+ + foo + ' is ' + foo + 'bar1' + '\n')
3223+
3224+ out, err = self.run_bzr(['grep', '--color',
3225+ 'always', 'foo'])
3226+ self.assertEqual(out, res)
3227+ self.assertEqual(len(out.splitlines()), 1)
3228+
3229+ out, err = self.run_bzr(['grep', '-i', '--color',
3230+ 'always', 'FOO'])
3231+ self.assertEqual(out, res)
3232+ self.assertEqual(len(out.splitlines()), 1)
3233+
3234+ out, err = self.run_bzr(['grep', '--color',
3235+ 'always', 'f.o'])
3236+ self.assertEqual(out, res)
3237+ self.assertEqual(len(out.splitlines()), 1)
3238+
3239+ out, err = self.run_bzr(['grep', '-i', '--color',
3240+ 'always', 'F.O'])
3241+ self.assertEqual(out, res)
3242+ self.assertEqual(len(out.splitlines()), 1)
3243+
3244+ out, err = self.run_bzr(['grep', '-n', '--color',
3245+ 'always', 'foo'])
3246+ self.assertEqual(out, nres)
3247+ self.assertEqual(len(out.splitlines()), 1)
3248+
3249+ out, err = self.run_bzr(['grep', '-n', '-i', '--color',
3250+ 'always', 'FOO'])
3251+ self.assertEqual(out, nres)
3252+ self.assertEqual(len(out.splitlines()), 1)
3253+
3254+ out, err = self.run_bzr(['grep', '-n', '--color',
3255+ 'always', 'f.o'])
3256+ self.assertEqual(out, nres)
3257+ self.assertEqual(len(out.splitlines()), 1)
3258+
3259+ out, err = self.run_bzr(['grep', '-n', '-i', '--color',
3260+ 'always', 'F.O'])
3261+ self.assertEqual(out, nres)
3262+ self.assertEqual(len(out.splitlines()), 1)
3263+
3264+
3265+# copied from bzrlib.tests.blackbox.test_diff
3266+def subst_dates(string):
3267+ """Replace date strings with constant values."""
3268+ return re.sub(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [-\+]\d{4}',
3269+ 'YYYY-MM-DD HH:MM:SS +ZZZZ', string)
3270+
3271+
3272+class TestGrepDiff(tests.TestCaseWithTransport):
3273+
3274+ def make_example_branch(self):
3275+ tree = self.make_branch_and_tree('.')
3276+ self.build_tree_contents([
3277+ ('hello', 'foo\n'),
3278+ ('goodbye', 'baz\n')])
3279+ tree.add(['hello'])
3280+ tree.commit('setup')
3281+ tree.add(['goodbye'])
3282+ tree.commit('setup')
3283+ return tree
3284+
3285+ def test_grep_diff_basic(self):
3286+ """grep -p basic test."""
3287+ tree = self.make_example_branch()
3288+ self.build_tree_contents([('hello', 'hello world!\n')])
3289+ tree.commit('updated hello')
3290+ out, err = self.run_bzr(['grep', '-p', 'hello'])
3291+ self.assertEquals(err, '')
3292+ self.assertEqualDiff(subst_dates(out), '''\
3293+=== revno:3 ===
3294+ === modified file 'hello'
3295+ --- hello YYYY-MM-DD HH:MM:SS +ZZZZ
3296+ +++ hello YYYY-MM-DD HH:MM:SS +ZZZZ
3297+ +hello world!
3298+=== revno:1 ===
3299+ === added file 'hello'
3300+ --- hello YYYY-MM-DD HH:MM:SS +ZZZZ
3301+ +++ hello YYYY-MM-DD HH:MM:SS +ZZZZ
3302+''')
3303+
3304+ def test_grep_diff_revision(self):
3305+ """grep -p specific revision."""
3306+ tree = self.make_example_branch()
3307+ self.build_tree_contents([('hello', 'hello world!\n')])
3308+ tree.commit('updated hello')
3309+ out, err = self.run_bzr(['grep', '-p', '-r', '3', 'hello'])
3310+ self.assertEquals(err, '')
3311+ self.assertEqualDiff(subst_dates(out), '''\
3312+=== revno:3 ===
3313+ === modified file 'hello'
3314+ --- hello YYYY-MM-DD HH:MM:SS +ZZZZ
3315+ +++ hello YYYY-MM-DD HH:MM:SS +ZZZZ
3316+ +hello world!
3317+''')
3318+
3319+ def test_grep_diff_revision_range(self):
3320+ """grep -p revision range."""
3321+ tree = self.make_example_branch()
3322+ self.build_tree_contents([('hello', 'hello world!1\n')]) # rev 3
3323+ tree.commit('rev3')
3324+ self.build_tree_contents([('blah', 'hello world!2\n')]) # rev 4
3325+ tree.add('blah')
3326+ tree.commit('rev4')
3327+ open('hello', 'a').write('hello world!3\n')
3328+ #self.build_tree_contents([('hello', 'hello world!3\n')]) # rev 5
3329+ tree.commit('rev5')
3330+ out, err = self.run_bzr(['grep', '-p', '-r', '2..5', 'hello'])
3331+ self.assertEquals(err, '')
3332+ self.assertEqualDiff(subst_dates(out), '''\
3333+=== revno:5 ===
3334+ === modified file 'hello'
3335+ --- hello YYYY-MM-DD HH:MM:SS +ZZZZ
3336+ +++ hello YYYY-MM-DD HH:MM:SS +ZZZZ
3337+ +hello world!3
3338+=== revno:4 ===
3339+ === added file 'blah'
3340+ +hello world!2
3341+=== revno:3 ===
3342+ === modified file 'hello'
3343+ --- hello YYYY-MM-DD HH:MM:SS +ZZZZ
3344+ +++ hello YYYY-MM-DD HH:MM:SS +ZZZZ
3345+ +hello world!1
3346+''')
3347+
3348+ def test_grep_diff_color(self):
3349+ """grep -p color test."""
3350+ tree = self.make_example_branch()
3351+ self.build_tree_contents([('hello', 'hello world!\n')])
3352+ tree.commit('updated hello')
3353+ out, err = self.run_bzr(['grep', '--diff', '-r', '3',
3354+ '--color', 'always', 'hello'])
3355+ self.assertEquals(err, '')
3356+ revno = color_string('=== revno:3 ===', fg=FG.BOLD_BLUE) + '\n'
3357+ filename = color_string(" === modified file 'hello'", fg=FG.BOLD_MAGENTA) + '\n'
3358+ redhello = color_string('hello', fg=FG.BOLD_RED)
3359+ diffstr = '''\
3360+ --- hello YYYY-MM-DD HH:MM:SS +ZZZZ
3361+ +++ hello YYYY-MM-DD HH:MM:SS +ZZZZ
3362+ +hello world!
3363+'''
3364+ diffstr = diffstr.replace('hello', redhello)
3365+ self.assertEqualDiff(subst_dates(out), revno + filename + diffstr)
3366+
3367+ def test_grep_norevs(self):
3368+ """grep -p with zero revisions."""
3369+ out, err = self.run_bzr(['init'])
3370+ out, err = self.run_bzr(['grep', '--diff', 'foo'], 3)
3371+ self.assertEquals(out, '')
3372+ self.assertContainsRe(err, "ERROR:.*revision.* does not exist in branch")
3373+
3374
3375=== added file 'bzrlib/termcolor.py'
3376--- bzrlib/termcolor.py 1970-01-01 00:00:00 +0000
3377+++ bzrlib/termcolor.py 2012-08-03 11:45:28 +0000
3378@@ -0,0 +1,78 @@
3379+# Copyright (C) 2010 Canonical Ltd
3380+#
3381+# This program is free software; you can redistribute it and/or modify
3382+# it under the terms of the GNU General Public License as published by
3383+# the Free Software Foundation; either version 2 of the License, or
3384+# (at your option) any later version.
3385+#
3386+# This program is distributed in the hope that it will be useful,
3387+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3388+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3389+# GNU General Public License for more details.
3390+#
3391+# You should have received a copy of the GNU General Public License
3392+# along with this program; if not, write to the Free Software
3393+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
3394+
3395+from __future__ import absolute_import
3396+
3397+import os
3398+import sys
3399+
3400+
3401+class FG(object):
3402+ """Unix terminal foreground color codes (16-color)."""
3403+ RED = '\033[31m'
3404+ GREEN = '\033[32m'
3405+ YELLOW = '\033[33m'
3406+ BLUE = '\033[34m'
3407+ MAGENTA = '\033[35m'
3408+ CYAN = '\033[36m'
3409+ WHITE = '\033[37m'
3410+
3411+ # Bold Foreground
3412+ BOLD_RED = '\033[1;31m'
3413+ BOLD_GREEN = '\033[1;32m'
3414+ BOLD_YELLOW = '\033[1;33m'
3415+ BOLD_BLUE = '\033[1;34m'
3416+ BOLD_MAGENTA = '\033[1;35m'
3417+ BOLD_CYAN = '\033[1;36m'
3418+ BOLD_WHITE = '\033[1;37m'
3419+
3420+ NONE = '\033[0m'
3421+
3422+
3423+class BG(object):
3424+ """Unix terminal background color codes (16-color)."""
3425+ BLACK = '\033[40m'
3426+ RED = '\033[41m'
3427+ GREEN = '\033[42m'
3428+ YELLOW = '\033[43m'
3429+ BLUE = '\033[44m'
3430+ MAGENTA = '\033[45m'
3431+ CYAN = '\033[46m'
3432+ WHITE = '\033[47m'
3433+
3434+ NONE = '\033[0m'
3435+
3436+
3437+def color_string(s, fg, bg=''):
3438+ return fg + bg + s + FG.NONE
3439+
3440+
3441+def re_color_string(compiled_pattern, s, fg):
3442+ return compiled_pattern.sub(fg + r'\1' + FG.NONE, s)
3443+
3444+
3445+def allow_color():
3446+ if os.name != 'posix':
3447+ return False
3448+ if not sys.stdout.isatty():
3449+ return False
3450+ try:
3451+ import curses
3452+ curses.setupterm()
3453+ return curses.tigetnum('colors') > 2
3454+ except curses.error:
3455+ return False
3456+
3457
3458=== modified file 'bzrlib/tests/features.py'
3459--- bzrlib/tests/features.py 2012-03-09 16:48:55 +0000
3460+++ bzrlib/tests/features.py 2012-08-03 11:45:28 +0000
3461@@ -492,3 +492,15 @@
3462
3463
3464 win32_feature = Win32Feature()
3465+
3466+
3467+class _ColorFeature(Feature):
3468+
3469+ def _probe(self):
3470+ from bzrlib.termcolor import allow_color
3471+ return allow_color()
3472+
3473+ def feature_name(self):
3474+ return "Terminal supports color."
3475+
3476+ColorFeature = _ColorFeature()
3477
3478=== modified file 'doc/en/release-notes/bzr-2.6.txt'
3479--- doc/en/release-notes/bzr-2.6.txt 2012-07-28 20:20:38 +0000
3480+++ doc/en/release-notes/bzr-2.6.txt 2012-08-03 11:45:28 +0000
3481@@ -102,6 +102,8 @@
3482 * New option ``--overwrite-tags`` for ``bzr pull`` and ``bzr push``.
3483 (Jelmer Vernooij, #681792)
3484
3485+* The 'grep' plugin is now shipped with bzr. (Jelmer Vernooij)
3486+
3487 Improvements
3488 ************
3489