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

Proposed by Jelmer Vernooij
Status: Merged
Approved by: Jelmer Vernooij
Approved revision: no longer in the source branch.
Merged at revision: 6555
Proposed branch: lp:~jelmer/bzr/merge-grep
Merge into: lp:bzr
Diff against target: 3488 lines (+3435/-0)
9 files modified
bzrlib/_termcolor.py (+78/-0)
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/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) Approve
Review via email: mp+118067@code.launchpad.net

Commit message

Merge the grep plugin into the core.

Description of the change

Merge the grep plugin into core.

Now updated for tests to pass, and with the ColorFeature moved into bzrlib.tests.features.

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

Tests now pass.

Idle query, why don't we use bzr join for things like this?

Don't like moving the colour stuff to bzrlib.termcolor, it's not the interface I think we'll want to be supporting in core long term. Can stick it back under grep or rename to _termcolor so it's clear we don't want other plugins basing output off this interface.

Looks good apart from that.

review: Approve
Revision history for this message
Jelmer Vernooij (jelmer) wrote :

This was merged using bzr join.

I've renamed termcolor to _termcolor.

Thanks for the review!

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

> This was merged using bzr join.

Well I obviously fail at using blame then...

Preview Diff

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