Merge lp:~parthm/bzr/503670-grep-builtin into lp:bzr

Proposed by Parth Malwankar
Status: Rejected
Rejected by: Parth Malwankar
Proposed branch: lp:~parthm/bzr/503670-grep-builtin
Merge into: lp:bzr
Diff against target: 747 lines (+676/-1)
5 files modified
NEWS (+3/-0)
bzrlib/builtins.py (+134/-1)
bzrlib/grep.py (+103/-0)
bzrlib/tests/blackbox/__init__.py (+1/-0)
bzrlib/tests/blackbox/test_grep.py (+435/-0)
To merge this branch: bzr merge lp:~parthm/bzr/503670-grep-builtin
Reviewer Review Type Date Requested Status
Vincent Ladeuil Needs Fixing
Review via email: mp+20420@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Parth Malwankar (parthm) wrote :

=== Fixes #503670 ===
Ports the lp:bzr-grep (https://launchpad.net/bzr-grep) plugin to be a builtin.

[bzrlib]% ../bzr help grep
Purpose: Print lines matching PATTERN for specified files and revisions.
Usage: bzr grep PATTERN [PATH...]

Options:
  --from-root Search for pattern starting from the root of the
                        branch. (implies --recursive)
  -Z, --null Write an ascii NUL (\0) separator between output lines
                        rather than a newline.
  -v, --verbose Display more information.
  -R, --recursive Recurse into subdirectories.
  -h, --help Show help message.
  -q, --quiet Only display errors and warnings.
  -i, --ignore-case ignore case distinctions while matching.
  --levels=N Number of levels to display - 0 for all, 1 for
                        collapsed (default).
  --usage Show usage message and options.
  -n, --line-number show 1-based line number.
  -r ARG, --revision=ARG
                        See "help revisionspec" for details.

Description:
  This command searches the specified files and revisions for a given pattern.
  The pattern is specified as a Python regular expressions[1].
  If the file name is not specified the file revisions in the current directory
  are searched. If the revision number is not specified, the latest revision is
  searched.

  Note that this command is different from POSIX grep in that it searches the
  revisions of the branch and not the working copy. Unversioned files and
  uncommitted changes are not seen.

  When searching a pattern, the output is shown in the 'filepath:string' format.
  If a revision is explicitly searched, the output is shown as 'filepath~N:string',
  where N is the revision number.

  [1] http://docs.python.org/library/re.html#regular-expression-syntax

[bzrlib]%

Revision history for this message
Parth Malwankar (parthm) wrote :
lp:~parthm/bzr/503670-grep-builtin updated
5069. By Parth Malwankar

levels=0 now shows revision number

5070. By Parth Malwankar

added test for --levels=0

Revision history for this message
Vincent Ladeuil (vila) wrote :

I'd rather see that as a true plugin in bzrlib/plugins itself
(if only to avoid yet another import(s) in builtins.py).

Trimming the cmd_grep.run() method can be good too and will allow
whitebox testing, let me know if you need help there.

review: Needs Fixing
Revision history for this message
Parth Malwankar (parthm) wrote :

This can be rejected as lp:bzr-grep has been added to "Bazaar VCS and Tools"
based on irc discussion between vila, lifeless and parthm.

A separate mp will be raised to review lp:bzr-grep.

Unmerged revisions

5070. By Parth Malwankar

added test for --levels=0

5069. By Parth Malwankar

levels=0 now shows revision number

5068. By Parth Malwankar

updated NEWS

5067. By Parth Malwankar

ported lp:bzr-grep to be a builtin.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'NEWS'
--- NEWS 2010-03-02 10:21:39 +0000
+++ NEWS 2010-03-02 12:44:17 +0000
@@ -34,6 +34,9 @@
34New Features34New Features
35************35************
3636
37* bzr now has a builtin command ``grep`` which can be used to search for files
38 and revisions containing a pattern. (Parth Malwankar, #503670)
39
37* If the Apport crash-reporting tool is available, bzr crashes are now40* If the Apport crash-reporting tool is available, bzr crashes are now
38 stored into the ``/var/crash`` apport spool directory, and the user is41 stored into the ``/var/crash`` apport spool directory, and the user is
39 invited to report them to the developers from there, either42 invited to report them to the developers from there, either
4043
=== modified file 'bzrlib/builtins.py'
--- bzrlib/builtins.py 2010-03-01 16:18:43 +0000
+++ bzrlib/builtins.py 2010-03-02 12:44:17 +0000
@@ -22,6 +22,7 @@
22lazy_import(globals(), """22lazy_import(globals(), """
23import codecs23import codecs
24import cStringIO24import cStringIO
25import re
25import sys26import sys
26import time27import time
2728
@@ -36,6 +37,7 @@
36 config,37 config,
37 errors,38 errors,
38 globbing,39 globbing,
40 grep,
39 hooks,41 hooks,
40 log,42 log,
41 merge as _mod_merge,43 merge as _mod_merge,
@@ -47,6 +49,7 @@
47 static_tuple,49 static_tuple,
48 symbol_versioning,50 symbol_versioning,
49 timestamp,51 timestamp,
52 trace,
50 transport,53 transport,
51 ui,54 ui,
52 urlutils,55 urlutils,
@@ -55,7 +58,7 @@
55from bzrlib.branch import Branch58from bzrlib.branch import Branch
56from bzrlib.conflicts import ConflictList59from bzrlib.conflicts import ConflictList
57from bzrlib.transport import memory60from bzrlib.transport import memory
58from bzrlib.revisionspec import RevisionSpec, RevisionInfo61from bzrlib.revisionspec import RevisionSpec, RevisionInfo, RevisionSpec_revid
59from bzrlib.smtp_connection import SMTPConnection62from bzrlib.smtp_connection import SMTPConnection
60from bzrlib.workingtree import WorkingTree63from bzrlib.workingtree import WorkingTree
61""")64""")
@@ -5917,6 +5920,136 @@
5917 for path, location in sorted(ref_list):5920 for path, location in sorted(ref_list):
5918 self.outf.write('%s %s\n' % (path, location))5921 self.outf.write('%s %s\n' % (path, location))
59195922
5923class cmd_grep(Command):
5924 """Print lines matching PATTERN for specified files and revisions.
5925
5926 This command searches the specified files and revisions for a given pattern.
5927 The pattern is specified as a Python regular expressions[1].
5928 If the file name is not specified the file revisions in the current directory
5929 are searched. If the revision number is not specified, the latest revision is
5930 searched.
5931
5932 Note that this command is different from POSIX grep in that it searches the
5933 revisions of the branch and not the working copy. Unversioned files and
5934 uncommitted changes are not seen.
5935
5936 When searching a pattern, the output is shown in the 'filepath:string' format.
5937 If a revision is explicitly searched, the output is shown as 'filepath~N:string',
5938 where N is the revision number.
5939
5940 [1] http://docs.python.org/library/re.html#regular-expression-syntax
5941 """
5942
5943 takes_args = ['pattern', 'path*']
5944 takes_options = [
5945 'verbose',
5946 'revision',
5947 Option('line-number', short_name='n',
5948 help='show 1-based line number.'),
5949 Option('ignore-case', short_name='i',
5950 help='ignore case distinctions while matching.'),
5951 Option('recursive', short_name='R',
5952 help='Recurse into subdirectories.'),
5953 Option('from-root',
5954 help='Search for pattern starting from the root of the branch. '
5955 '(implies --recursive)'),
5956 Option('null', short_name='Z',
5957 help='Write an ascii NUL (\\0) separator '
5958 'between output lines rather than a newline.'),
5959 Option('levels',
5960 help='Number of levels to display - 0 for all, 1 for collapsed (default).',
5961 argname='N',
5962 type=_parse_levels),
5963 ]
5964
5965
5966 @display_command
5967 def run(self, verbose=False, ignore_case=False, recursive=False, from_root=False,
5968 null=False, levels=None, line_number=False, path_list=None, revision=None, pattern=None):
5969
5970 if levels==None:
5971 levels=1
5972
5973 if path_list == None:
5974 path_list = ['.']
5975 else:
5976 if from_root:
5977 raise errors.BzrCommandError('cannot specify both --from-root and PATH.')
5978
5979 print_revno = False
5980 if levels==0:
5981 # if multiple levels are checked user should be able to
5982 # know which version is being shown
5983 print_revno=True
5984
5985 if revision == None:
5986 # grep on latest revision by default
5987 revision = [RevisionSpec.from_string("last:1")]
5988 else:
5989 print_revno = True # used to print revno in output.
5990
5991 start_rev = revision[0]
5992 end_rev = revision[0]
5993 if len(revision) == 2:
5994 end_rev = revision[1]
5995
5996 eol_marker = '\n'
5997 if null:
5998 eol_marker = '\0'
5999
6000 re_flags = 0
6001 if ignore_case:
6002 re_flags = re.IGNORECASE
6003 patternc = grep.compile_pattern(pattern, re_flags)
6004
6005 wt, relpath = WorkingTree.open_containing('.')
6006
6007 start_revid = start_rev.as_revision_id(wt.branch)
6008 end_revid = end_rev.as_revision_id(wt.branch)
6009
6010 given_revs = log._graph_view_revisions(wt.branch, start_revid, end_revid)
6011
6012 # edge case: we have a repo created with 'bzr init' and it has no
6013 # revisions (revno: 0)
6014 try:
6015 given_revs = list(given_revs)
6016 except errors.NoSuchRevision, e:
6017 raise errors.BzrCommandError('No revisions found for grep.')
6018
6019 for revid, revno, merge_depth in given_revs:
6020 if levels == 1 and merge_depth != 0:
6021 # with level=1 show only top level
6022 continue
6023
6024 wt.lock_read()
6025 rev = RevisionSpec_revid.from_string("revid:"+revid)
6026 try:
6027 for path in path_list:
6028 tree = rev.as_tree(wt.branch)
6029 path_for_id = osutils.pathjoin(relpath, path)
6030 id = tree.path2id(path_for_id)
6031 if not id:
6032 self._skip_file(path)
6033 continue
6034
6035 if osutils.isdir(path):
6036 path_prefix = path
6037 grep.dir_grep(tree, path, relpath, recursive, line_number,
6038 patternc, from_root, eol_marker, revno, print_revno,
6039 self.outf, path_prefix)
6040 else:
6041 tree.lock_read()
6042 try:
6043 grep.file_grep(tree, id, '.', path, patternc, eol_marker,
6044 line_number, revno, print_revno, self.outf)
6045 finally:
6046 tree.unlock()
6047 finally:
6048 wt.unlock()
6049
6050 def _skip_file(self, path):
6051 trace.warning("warning: skipped unknown file '%s'." % path)
6052
59206053
5921# these get imported and then picked up by the scan for cmd_*6054# these get imported and then picked up by the scan for cmd_*
5922# TODO: Some more consistent way to split command definitions across files;6055# TODO: Some more consistent way to split command definitions across files;
59236056
=== added file 'bzrlib/grep.py'
--- bzrlib/grep.py 1970-01-01 00:00:00 +0000
+++ bzrlib/grep.py 2010-03-02 12:44:17 +0000
@@ -0,0 +1,103 @@
1# Copyright (C) 2010 Canonical Ltd
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16"""bzr grep"""
17
18
19from bzrlib.lazy_import import lazy_import
20lazy_import(globals(), """
21import os
22import re
23
24from bzrlib import (
25 osutils,
26 errors,
27 lazy_regex,
28 )
29""")
30
31def compile_pattern(pattern, flags=0):
32 patternc = None
33 try:
34 # use python's re.compile as we need to catch re.error in case of bad pattern
35 lazy_regex.reset_compile()
36 patternc = re.compile(pattern, flags)
37 except re.error, e:
38 raise errors.BzrError("Invalid pattern: '%s'" % pattern)
39 return patternc
40
41def dir_grep(tree, path, relpath, recursive, line_number, compiled_pattern,
42 from_root, eol_marker, revno, print_revno, outf, path_prefix):
43 # setup relpath to open files relative to cwd
44 rpath = relpath
45 if relpath:
46 rpath = osutils.pathjoin('..',relpath)
47
48 tree.lock_read()
49 try:
50 from_dir = osutils.pathjoin(relpath, path)
51 if from_root:
52 # start searching recursively from root
53 from_dir=None
54 recursive=True
55
56 for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
57 from_dir=from_dir, recursive=recursive):
58 if fc == 'V' and fkind == 'file':
59 file_grep(tree, fid, rpath, fp, compiled_pattern,
60 eol_marker, line_number, revno, print_revno, outf, path_prefix)
61 finally:
62 tree.unlock()
63
64
65def file_grep(tree, id, relpath, path, patternc, eol_marker,
66 line_number, revno, print_revno, outf, path_prefix = None):
67
68 if relpath:
69 path = osutils.normpath(osutils.pathjoin(relpath, path))
70 path = path.replace('\\', '/')
71 path = path.replace(relpath + '/', '', 1)
72
73 revfmt = ''
74 if print_revno:
75 revfmt = "~%s"
76
77 if path_prefix and path_prefix != '.':
78 # user has passed a dir arg, show that as result prefix
79 path = osutils.pathjoin(path_prefix, path)
80
81 fmt_with_n = path + revfmt + ":%d:%s" + eol_marker
82 fmt_without_n = path + revfmt + ":%s" + eol_marker
83
84 index = 1
85 for line in tree.get_file_lines(id):
86 res = patternc.search(line)
87 if res:
88 if line_number:
89 if print_revno:
90 out = (revno, index, line.strip())
91 else:
92 out = (index, line.strip())
93 outf.write(fmt_with_n % out)
94 else:
95 if print_revno:
96 out = (revno, line.strip())
97 else:
98 out = (line.strip(),)
99 outf.write(fmt_without_n % out)
100
101 index += 1
102
103
0104
=== modified file 'bzrlib/tests/blackbox/__init__.py'
--- bzrlib/tests/blackbox/__init__.py 2009-04-29 20:31:34 +0000
+++ bzrlib/tests/blackbox/__init__.py 2010-03-02 12:44:17 +0000
@@ -60,6 +60,7 @@
60 'bzrlib.tests.blackbox.test_filesystem_cicp',60 'bzrlib.tests.blackbox.test_filesystem_cicp',
61 'bzrlib.tests.blackbox.test_filtered_view_ops',61 'bzrlib.tests.blackbox.test_filtered_view_ops',
62 'bzrlib.tests.blackbox.test_find_merge_base',62 'bzrlib.tests.blackbox.test_find_merge_base',
63 'bzrlib.tests.blackbox.test_grep',
63 'bzrlib.tests.blackbox.test_help',64 'bzrlib.tests.blackbox.test_help',
64 'bzrlib.tests.blackbox.test_hooks',65 'bzrlib.tests.blackbox.test_hooks',
65 'bzrlib.tests.blackbox.test_ignore',66 'bzrlib.tests.blackbox.test_ignore',
6667
=== added file 'bzrlib/tests/blackbox/test_grep.py'
--- bzrlib/tests/blackbox/test_grep.py 1970-01-01 00:00:00 +0000
+++ bzrlib/tests/blackbox/test_grep.py 2010-03-02 12:44:17 +0000
@@ -0,0 +1,435 @@
1# Copyright (C) 2010 Canonical Ltd
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
17import os
18import re
19
20from bzrlib import tests, osutils
21
22class TestGrep(tests.TestCaseWithTransport):
23 def _str_contains(self, base, pattern, flags=re.MULTILINE|re.DOTALL):
24 res = re.findall(pattern, base, flags)
25 return res != []
26
27 def _mk_file(self, path, line_prefix, total_lines, versioned):
28 text=''
29 for i in range(total_lines):
30 text += line_prefix + str(i+1) + "\n"
31
32 open(path, 'w').write(text)
33 if versioned:
34 self.run_bzr(['add', path])
35 self.run_bzr(['ci', '-m', '"' + path + '"'])
36
37 def _update_file(self, path, text):
38 """append text to file 'path' and check it in"""
39 open(path, 'a').write(text)
40 self.run_bzr(['ci', '-m', '"' + path + '"'])
41
42 def _mk_unknown_file(self, path, line_prefix='line', total_lines=10):
43 self._mk_file(path, line_prefix, total_lines, versioned=False)
44
45 def _mk_versioned_file(self, path, line_prefix='line', total_lines=10):
46 self._mk_file(path, line_prefix, total_lines, versioned=True)
47
48 def _mk_dir(self, path, versioned):
49 os.mkdir(path)
50 if versioned:
51 self.run_bzr(['add', path])
52 self.run_bzr(['ci', '-m', '"' + path + '"'])
53
54 def _mk_unknown_dir(self, path):
55 self._mk_dir(path, versioned=False)
56
57 def _mk_versioned_dir(self, path):
58 self._mk_dir(path, versioned=True)
59
60 def test_basic_unknown_file(self):
61 """search for pattern in specfic file. should issue warning."""
62 wd = 'foobar0'
63 self.make_branch_and_tree(wd)
64 os.chdir(wd)
65 self._mk_versioned_file('filex.txt') # force rev to revno:1 and not revno:0
66 self._mk_unknown_file('file0.txt')
67 out, err = self.run_bzr(['grep', 'line1', 'file0.txt'])
68 self.assertFalse(self._str_contains(out, "file0.txt:line1"))
69 self.assertTrue(self._str_contains(err, "warning: skipped.*file0.txt.*\."))
70
71 def test_revno0(self):
72 """search for pattern in when only revno0 is present"""
73 wd = 'foobar0'
74 self.make_branch_and_tree(wd) # only revno 0 in branch
75 os.chdir(wd)
76 out, err = self.run_bzr(['grep', 'line1'], retcode=3)
77 self.assertTrue(self._str_contains(err, "ERROR: No revisions found"))
78
79 def test_basic_versioned_file(self):
80 """search for pattern in specfic file"""
81 wd = 'foobar0'
82 self.make_branch_and_tree(wd)
83 os.chdir(wd)
84 self._mk_versioned_file('file0.txt')
85 out, err = self.run_bzr(['grep', 'line1', 'file0.txt'])
86 self.assertTrue(self._str_contains(out, "file0.txt:line1"))
87 self.assertFalse(self._str_contains(err, "warning: skipped.*file0.txt.*\."))
88
89 def test_multiple_files(self):
90 """search for pattern in multiple files"""
91 wd = 'foobar0'
92 self.make_branch_and_tree(wd)
93 os.chdir(wd)
94 self._mk_versioned_file('file0.txt', total_lines=2)
95 self._mk_versioned_file('file1.txt', total_lines=2)
96 self._mk_versioned_file('file2.txt', total_lines=2)
97 out, err = self.run_bzr(['grep', 'line[1-2]'])
98
99 self.assertTrue(self._str_contains(out, "file0.txt:line1"))
100 self.assertTrue(self._str_contains(out, "file0.txt:line2"))
101 self.assertTrue(self._str_contains(out, "file1.txt:line1"))
102 self.assertTrue(self._str_contains(out, "file1.txt:line2"))
103 self.assertTrue(self._str_contains(out, "file2.txt:line1"))
104 self.assertTrue(self._str_contains(out, "file2.txt:line2"))
105
106 def test_null_option(self):
107 """--null option should use NUL instead of newline"""
108 wd = 'foobar0'
109 self.make_branch_and_tree(wd)
110 os.chdir(wd)
111 self._mk_versioned_file('file0.txt', total_lines=3)
112
113 out, err = self.run_bzr(['grep', '--null', 'line[1-3]'])
114 self.assertTrue(out == "file0.txt:line1\0file0.txt:line2\0file0.txt:line3\0")
115
116 out, err = self.run_bzr(['grep', '-Z', 'line[1-3]'])
117 self.assertTrue(out == "file0.txt:line1\0file0.txt:line2\0file0.txt:line3\0")
118
119 def test_versioned_file_in_dir_no_recurse(self):
120 """should not recurse without -R"""
121 wd = 'foobar0'
122 self.make_branch_and_tree(wd)
123 os.chdir(wd)
124 self._mk_versioned_dir('dir0')
125 self._mk_versioned_file('dir0/file0.txt')
126 out, err = self.run_bzr(['grep', 'line1'])
127 self.assertFalse(self._str_contains(out, "file0.txt:line1"))
128
129 def test_versioned_file_in_dir_recurse(self):
130 """should find pattern in hierarchy with -R"""
131 wd = 'foobar0'
132 self.make_branch_and_tree(wd)
133 os.chdir(wd)
134 self._mk_versioned_dir('dir0')
135 self._mk_versioned_file('dir0/file0.txt')
136 out, err = self.run_bzr(['grep', '-R', 'line1'])
137 self.assertTrue(self._str_contains(out, "^dir0/file0.txt:line1"))
138 out, err = self.run_bzr(['grep', '--recursive', 'line1'])
139 self.assertTrue(self._str_contains(out, "^dir0/file0.txt:line1"))
140
141 def test_versioned_file_within_dir(self):
142 """search for pattern while in nested dir"""
143 wd = 'foobar0'
144 self.make_branch_and_tree(wd)
145 os.chdir(wd)
146 self._mk_versioned_dir('dir0')
147 self._mk_versioned_file('dir0/file0.txt')
148 os.chdir('dir0')
149 out, err = self.run_bzr(['grep', 'line1'])
150 self.assertTrue(self._str_contains(out, "^file0.txt:line1"))
151
152 def test_versioned_files_from_outside_dir(self):
153 """grep for pattern with dirs passed as argument"""
154 wd = 'foobar0'
155 self.make_branch_and_tree(wd)
156 os.chdir(wd)
157
158 self._mk_versioned_dir('dir0')
159 self._mk_versioned_file('dir0/file0.txt')
160
161 self._mk_versioned_dir('dir1')
162 self._mk_versioned_file('dir1/file1.txt')
163
164 out, err = self.run_bzr(['grep', 'line1', 'dir0', 'dir1'])
165 self.assertTrue(self._str_contains(out, "^dir0/file0.txt:line1"))
166 self.assertTrue(self._str_contains(out, "^dir1/file1.txt:line1"))
167
168 def test_versioned_files_from_outside_dir(self):
169 """grep for pattern with dirs passed as argument"""
170 wd = 'foobar0'
171 self.make_branch_and_tree(wd)
172 os.chdir(wd)
173
174 self._mk_versioned_dir('dir0')
175 self._mk_versioned_file('dir0/file0.txt')
176
177 self._mk_versioned_dir('dir1')
178 self._mk_versioned_file('dir1/file1.txt')
179
180 out, err = self.run_bzr(['grep', 'line1', 'dir0', 'dir1'])
181 self.assertTrue(self._str_contains(out, "^dir0/file0.txt:line1"))
182 self.assertTrue(self._str_contains(out, "^dir1/file1.txt:line1"))
183
184 def test_versioned_files_from_outside_two_dirs(self):
185 """grep for pattern with two levels of nested dir"""
186 wd = 'foobar0'
187 self.make_branch_and_tree(wd)
188 os.chdir(wd)
189
190 self._mk_versioned_dir('dir0')
191 self._mk_versioned_file('dir0/file0.txt')
192
193 self._mk_versioned_dir('dir1')
194 self._mk_versioned_file('dir1/file1.txt')
195
196 self._mk_versioned_dir('dir0/dir00')
197 self._mk_versioned_file('dir0/dir00/file0.txt')
198
199 out, err = self.run_bzr(['grep', 'line1', 'dir0/dir00'])
200 self.assertTrue(self._str_contains(out, "^dir0/dir00/file0.txt:line1"))
201
202 out, err = self.run_bzr(['grep', '-R', 'line1'])
203 self.assertTrue(self._str_contains(out, "^dir0/dir00/file0.txt:line1"))
204
205 def test_versioned_file_within_dir_two_levels(self):
206 """search for pattern while in nested dir (two levels)"""
207 wd = 'foobar0'
208 self.make_branch_and_tree(wd)
209 os.chdir(wd)
210 self._mk_versioned_dir('dir0')
211 self._mk_versioned_dir('dir0/dir1')
212 self._mk_versioned_file('dir0/dir1/file0.txt')
213 os.chdir('dir0')
214 out, err = self.run_bzr(['grep', '-R', 'line1'])
215 self.assertTrue(self._str_contains(out, "^dir1/file0.txt:line1"))
216 out, err = self.run_bzr(['grep', '--from-root', 'line1'])
217 self.assertTrue(self._str_contains(out, "^dir0/dir1/file0.txt:line1"))
218 out, err = self.run_bzr(['grep', 'line1'])
219 self.assertFalse(self._str_contains(out, "file0.txt"))
220
221 def test_ignore_case_no_match(self):
222 """match fails without --ignore-case"""
223 wd = 'foobar0'
224 self.make_branch_and_tree(wd)
225 os.chdir(wd)
226 self._mk_versioned_file('file0.txt')
227 out, err = self.run_bzr(['grep', 'LinE1', 'file0.txt'])
228 self.assertFalse(self._str_contains(out, "file0.txt:line1"))
229
230 def test_ignore_case_match(self):
231 """match fails without --ignore-case"""
232 wd = 'foobar0'
233 self.make_branch_and_tree(wd)
234 os.chdir(wd)
235 self._mk_versioned_file('file0.txt')
236 out, err = self.run_bzr(['grep', '-i', 'LinE1', 'file0.txt'])
237 self.assertTrue(self._str_contains(out, "file0.txt:line1"))
238 out, err = self.run_bzr(['grep', '--ignore-case', 'LinE1', 'file0.txt'])
239 self.assertTrue(self._str_contains(out, "^file0.txt:line1"))
240
241 def test_from_root_fail(self):
242 """match should fail without --from-root"""
243 wd = 'foobar0'
244 self.make_branch_and_tree(wd)
245 os.chdir(wd)
246 self._mk_versioned_file('file0.txt')
247 self._mk_versioned_dir('dir0')
248 os.chdir('dir0')
249 out, err = self.run_bzr(['grep', 'line1'])
250 self.assertFalse(self._str_contains(out, ".*file0.txt:line1"))
251
252 def test_from_root_pass(self):
253 """match pass with --from-root"""
254 wd = 'foobar0'
255 self.make_branch_and_tree(wd)
256 os.chdir(wd)
257 self._mk_versioned_file('file0.txt')
258 self._mk_versioned_dir('dir0')
259 os.chdir('dir0')
260 out, err = self.run_bzr(['grep', '--from-root', 'line1'])
261 self.assertTrue(self._str_contains(out, ".*file0.txt:line1"))
262
263 def test_with_line_number(self):
264 """search for pattern with --line-number"""
265 wd = 'foobar0'
266 self.make_branch_and_tree(wd)
267 os.chdir(wd)
268 self._mk_versioned_file('file0.txt')
269
270 out, err = self.run_bzr(['grep', '--line-number', 'line3', 'file0.txt'])
271 self.assertTrue(self._str_contains(out, "file0.txt:3:line3"))
272
273 out, err = self.run_bzr(['grep', '-n', 'line1', 'file0.txt'])
274 self.assertTrue(self._str_contains(out, "file0.txt:1:line1"))
275
276 def test_revno_basic_history_grep_file(self):
277 """search for pattern in specific revision number in a file"""
278 wd = 'foobar0'
279 fname = 'file0.txt'
280 self.make_branch_and_tree(wd)
281 os.chdir(wd)
282 self._mk_versioned_file(fname, total_lines=0)
283 self._update_file(fname, text="v2 text\n")
284 self._update_file(fname, text="v3 text\n")
285 self._update_file(fname, text="v4 text\n")
286
287 # rev 2 should not have text 'v3'
288 out, err = self.run_bzr(['grep', '-r', '2', 'v3', fname])
289 self.assertFalse(self._str_contains(out, "file0.txt"))
290
291 # rev 3 should not have text 'v3'
292 out, err = self.run_bzr(['grep', '-r', '3', 'v3', fname])
293 self.assertTrue(self._str_contains(out, "file0.txt~3:v3.*"))
294
295 # rev 3 should not have text 'v3' with line number
296 out, err = self.run_bzr(['grep', '-r', '3', '-n', 'v3', fname])
297 self.assertTrue(self._str_contains(out, "file0.txt~3:2:v3.*"))
298
299 def test_revno_basic_history_grep_full(self):
300 """search for pattern in specific revision number in a file"""
301 wd = 'foobar0'
302 fname = 'file0.txt'
303 self.make_branch_and_tree(wd)
304 os.chdir(wd)
305 self._mk_versioned_file(fname, total_lines=0) # rev1
306 self._mk_versioned_file('file1.txt') # rev2
307 self._update_file(fname, text="v3 text\n") # rev3
308 self._update_file(fname, text="v4 text\n") # rev4
309 self._update_file(fname, text="v5 text\n") # rev5
310
311 # rev 2 should not have text 'v3'
312 out, err = self.run_bzr(['grep', '-r', '2', 'v3'])
313 self.assertFalse(self._str_contains(out, "file0.txt"))
314
315 # rev 3 should not have text 'v3'
316 out, err = self.run_bzr(['grep', '-r', '3', 'v3'])
317 self.assertTrue(self._str_contains(out, "file0.txt~3:v3"))
318
319 # rev 3 should not have text 'v3' with line number
320 out, err = self.run_bzr(['grep', '-r', '3', '-n', 'v3'])
321 self.assertTrue(self._str_contains(out, "file0.txt~3:1:v3"))
322
323 def test_revno_versioned_file_in_dir(self):
324 """we create a file 'foobar0/dir0/file0.txt' and grep specific version of content"""
325 wd = 'foobar0'
326 self.make_branch_and_tree(wd)
327 os.chdir(wd)
328 self._mk_versioned_dir('dir0') # rev1
329 self._mk_versioned_file('dir0/file0.txt') # rev2
330 self._update_file('dir0/file0.txt', "v3 text\n") # rev3
331 self._update_file('dir0/file0.txt', "v4 text\n") # rev4
332 self._update_file('dir0/file0.txt', "v5 text\n") # rev5
333
334 # v4 should not be present in revno 3
335 out, err = self.run_bzr(['grep', '-r', 'last:3', '-R', 'v4'])
336 self.assertFalse(self._str_contains(out, "^dir0/file0.txt"))
337
338 # v4 should be present in revno 4
339 out, err = self.run_bzr(['grep', '-r', 'last:2', '-R', 'v4'])
340 self.assertTrue(self._str_contains(out, "^dir0/file0.txt~4:v4"))
341
342 def test_revno_range_basic_history_grep(self):
343 """search for pattern in revision range for file"""
344 wd = 'foobar0'
345 fname = 'file0.txt'
346 self.make_branch_and_tree(wd)
347 os.chdir(wd)
348 self._mk_versioned_file(fname, total_lines=0) # rev1
349 self._mk_versioned_file('file1.txt') # rev2
350 self._update_file(fname, text="v3 text\n") # rev3
351 self._update_file(fname, text="v4 text\n") # rev4
352 self._update_file(fname, text="v5 text\n") # rev5
353 self._update_file(fname, text="v6 text\n") # rev6
354
355 out, err = self.run_bzr(['grep', '-r', '1..', 'v3'])
356 self.assertTrue(self._str_contains(out, "file0.txt~3:v3"))
357 self.assertTrue(self._str_contains(out, "file0.txt~4:v3"))
358 self.assertTrue(self._str_contains(out, "file0.txt~5:v3"))
359
360 out, err = self.run_bzr(['grep', '-r', '1..5', 'v3'])
361 self.assertTrue(self._str_contains(out, "file0.txt~3:v3"))
362 self.assertTrue(self._str_contains(out, "file0.txt~4:v3"))
363 self.assertTrue(self._str_contains(out, "file0.txt~5:v3"))
364 self.assertFalse(self._str_contains(out, "file0.txt~6:v3"))
365
366 def test_revno_range_versioned_file_in_dir(self):
367 """we create a file 'foobar0/dir0/file0.txt' and grep rev-range for pattern"""
368 wd = 'foobar0'
369 self.make_branch_and_tree(wd)
370 os.chdir(wd)
371 self._mk_versioned_dir('dir0') # rev1
372 self._mk_versioned_file('dir0/file0.txt') # rev2
373 self._update_file('dir0/file0.txt', "v3 text\n") # rev3
374 self._update_file('dir0/file0.txt', "v4 text\n") # rev4
375 self._update_file('dir0/file0.txt', "v5 text\n") # rev5
376 self._update_file('dir0/file0.txt', "v6 text\n") # rev6
377
378 out, err = self.run_bzr(['grep', '-R', '-r', '2..5', 'v3'])
379 self.assertTrue(self._str_contains(out, "^dir0/file0.txt~3:v3"))
380 self.assertTrue(self._str_contains(out, "^dir0/file0.txt~4:v3"))
381 self.assertTrue(self._str_contains(out, "^dir0/file0.txt~5:v3"))
382 self.assertFalse(self._str_contains(out, "^dir0/file0.txt~6:v3"))
383
384 def test_revno_range_versioned_file_from_outside_dir(self):
385 """grep rev-range for pattern from outside dir"""
386 wd = 'foobar0'
387 self.make_branch_and_tree(wd)
388 os.chdir(wd)
389 self._mk_versioned_dir('dir0') # rev1
390 self._mk_versioned_file('dir0/file0.txt') # rev2
391 self._update_file('dir0/file0.txt', "v3 text\n") # rev3
392 self._update_file('dir0/file0.txt', "v4 text\n") # rev4
393 self._update_file('dir0/file0.txt', "v5 text\n") # rev5
394 self._update_file('dir0/file0.txt', "v6 text\n") # rev6
395
396 out, err = self.run_bzr(['grep', '-r', '2..5', 'v3', 'dir0'])
397 self.assertTrue(self._str_contains(out, "^dir0/file0.txt~3:v3"))
398 self.assertTrue(self._str_contains(out, "^dir0/file0.txt~4:v3"))
399 self.assertTrue(self._str_contains(out, "^dir0/file0.txt~5:v3"))
400 self.assertFalse(self._str_contains(out, "^dir0/file0.txt~6:v3"))
401
402 def test_levels(self):
403 """levels=0 should show findings from merged revision"""
404 wd0 = 'foobar0'
405 wd1 = 'foobar1'
406
407 self.make_branch_and_tree(wd0)
408 os.chdir(wd0)
409 self._mk_versioned_file('file0.txt')
410 os.chdir('..')
411
412 out, err = self.run_bzr(['branch', wd0, wd1])
413 os.chdir(wd1)
414 self._mk_versioned_file('file1.txt')
415 os.chdir(osutils.pathjoin('..', wd0))
416
417 out, err = self.run_bzr(['merge', osutils.pathjoin('..', wd1)])
418 out, err = self.run_bzr(['ci', '-m', 'merged'])
419
420 out, err = self.run_bzr(['grep', 'line1'])
421 self.assertTrue(self._str_contains(out, "file0.txt:line1"))
422 self.assertTrue(self._str_contains(out, "file1.txt:line1"))
423
424 out, err = self.run_bzr(['grep', '--levels=0', 'line1'])
425 self.assertTrue(self._str_contains(out, "file0.txt~2:line1"))
426 self.assertTrue(self._str_contains(out, "file1.txt~2:line1"))
427 self.assertTrue(self._str_contains(out, "file0.txt~1.1.1:line1"))
428 self.assertTrue(self._str_contains(out, "file1.txt~1.1.1:line1"))
429
430 out, err = self.run_bzr(['grep', '-n', '--levels=0', 'line1'])
431 self.assertTrue(self._str_contains(out, "file0.txt~2:1:line1"))
432 self.assertTrue(self._str_contains(out, "file1.txt~2:1:line1"))
433 self.assertTrue(self._str_contains(out, "file0.txt~1.1.1:1:line1"))
434 self.assertTrue(self._str_contains(out, "file1.txt~1.1.1:1:line1"))
435