Rev 9: Fix the file selection to do the right thing in sub directories. in http://code.launchpad.net/%7Ev-ladeuil/bzr/grep

Vincent Ladeuil v.ladeuil+lp at free.fr
Wed Jan 16 09:24:07 GMT 2008


At http://code.launchpad.net/%7Ev-ladeuil/bzr/grep

------------------------------------------------------------
revno: 9
revision-id:v.ladeuil+lp at free.fr-20080116092404-o6jg97wylpesvl72
parent: v.ladeuil+lp at free.fr-20080111162538-ie97n1n60nk1bmy7
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: grep
timestamp: Wed 2008-01-16 10:24:04 +0100
message:
  Fix the file selection to do the right thing in sub directories.
  
  * __init__.py:
  Rework the file selection.
  
  * test_grep.py:
  (TestGrepInDirs): Tests behavior in root dir and sub dirs.
modified:
  __init__.py                    __init__.py-20060323100923-17d68e8a2f14f018
  test_grep.py                   test_grep.py-20080111162345-dtinprpim0xk8s6e-1
-------------- next part --------------
=== modified file '__init__.py'
--- a/__init__.py	2008-01-11 16:25:38 +0000
+++ b/__init__.py	2008-01-16 09:24:04 +0000
@@ -16,15 +16,46 @@
 
 """Grep through your working tree, but only files managed by bzr."""
 
+import os.path
 import subprocess
+import sys
+
 
 from bzrlib import (
     builtins,
+    bzrdir,
     commands,
     option,
     osutils,
     )
 
+
+if sys.version_info < (2, 6):
+    # Borrowed from python2.6 until we get there
+    def _relpath(path, start=os.path.curdir):
+        """Return a relative version of a path.
+
+        This a slighty simplified and modified version of the 2.6
+        version. Windows specific error handling deleted since we always calls
+        this function for paths on the same volume.
+        """
+
+        if not path:
+            raise ValueError("no path specified")
+
+        start_list = os.path.abspath(start).split(os.path.sep)
+        path_list = os.path.abspath(path).split(os.path.sep)
+
+        # Work out how much of the filepath is shared by start and path.
+        i = len(os.path.commonprefix([start_list, path_list]))
+
+        rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
+        return os.path.join(*rel_list)
+else:
+    _relpath = os.path.relpath
+
+
+# TODO: avoid xargs completely ?
 class cmd_grep(commands.Command):
     """Grep through your working tree, but only files managed by bzr.
 
@@ -40,37 +71,65 @@
     takes_args = ['pattern', 'file*']
     takes_options = [
         option.Option(
-            'no-recurse',
+            'non-recursive',
             help="Don't recursively grep the contents of directories."),
         ]
 
+    # TODO: Separate file selection from file processing
     @commands.display_command
-    def run(self, pattern, no_recurse=False, file_list=None):
+    def run(self, pattern, non_recursive=False, file_list=None):
+
+        curdir = osutils.getcwd()
+
         options, file_list = self._read_options(file_list)
 
-        tree, file_list = builtins.tree_files(file_list)
+        versioned = 'V'
+
+        file_list = builtins.tree_files(file_list)[1]
+        tree, b, reldir = bzrdir.BzrDir.open_containing_tree_or_branch('.')
+        if reldir:
+            reldir += '/'
         tree.lock_read()
-        inv = tree.read_working_inventory()
-        tree.unlock()
-
-        cmd = ['xargs', '-0', 'grep', '-e', pattern]
-        cmd.extend(options)
-
-        #print cmd
-        #print file_list
-
-        xargs = subprocess.Popen(cmd, stdin=subprocess.PIPE)
-
-        for path, entry in inv.entries():
-            if file_list:
-                if no_recurse:
-                    if not path in file_list:
+
+        try:
+            cmd = ['xargs', '-0', 'grep', '-e', pattern]
+            cmd.extend(options)
+
+            xargs = subprocess.Popen(cmd, stdin=subprocess.PIPE,
+                                     stdout=subprocess.PIPE,
+                                     stderr=subprocess.PIPE,
+                                     )
+
+            for fpath, fclass, fkind, fid, entry \
+                    in tree.list_files(include_root=False):
+                # XXX: this selection part is messy, more tests are needed and
+                # a better flow too
+                if fkind == 'directory':
+                    continue
+                if file_list:
+                    if non_recursive:
+                        if not fpath in file_list:
+                            continue
+                    elif not osutils.is_inside_any(file_list, fpath):
                         continue
-                elif not osutils.is_inside_any(file_list, path):
-                    continue
-            xargs.stdin.write('%s\0' % path)
-
-        xargs.communicate()
+                elif reldir and not fpath.startswith(reldir):
+                    continue
+                if (non_recursive and reldir
+                    and fpath.startswith(reldir)
+                    and '/' in fpath[len(reldir):]):
+                    continue
+                if fclass != versioned:
+                    continue
+                rpath = _relpath(tree.abspath(fpath))
+                xargs.stdin.write(rpath + '\0')
+            xargs.stdin.flush()
+            out, err = xargs.communicate()
+            if out:
+                sys.stdout.write(out)
+            if err:
+                sys.stderr.write(err)
+        finally:
+            tree.unlock()
 
     def _read_options(self, list):
         # This is a bit of a hack, but if we scan the file list for

=== modified file 'test_grep.py'
--- a/test_grep.py	2008-01-11 16:25:38 +0000
+++ b/test_grep.py	2008-01-16 09:24:04 +0000
@@ -14,10 +14,43 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
+from cStringIO import StringIO
+
+
 from bzrlib import tests
 
 
-class TestGrep(tests.TestCase):
-
-    def test_fake(self):
-        pass
+class TestGrepInDirs(tests.TestCaseWithTransport):
+
+    def setUp(self):
+        super(TestGrepInDirs, self).setUp()
+        tree = self.make_branch_and_tree('.')
+        file_list = ['top', 'subdir/', 'subdir/down']
+        self.build_tree(file_list)
+        tree.add(file_list)
+        tree.commit('setup')
+
+    def assertGrepResultIs(self, expected_output, grep_args,
+                           working_dir='.',
+                           expected_error_re='.*'):
+        out, err = self.run_bzr(['grep'] + grep_args, working_dir=working_dir)
+        self.assertEquals(expected_output, out)
+        self.assertContainsRe(err, expected_error_re)
+
+    def test_grep_in_root_dir(self):
+        self.assertGrepResultIs('subdir/down:contents of subdir/down\n'
+                                'top:contents of top\n' ,
+                                ['contents'])
+        self.assertGrepResultIs('contents of top\n', ['top', 'top'])
+        self.assertGrepResultIs('contents of subdir/down\n', ['down',
+                                                              'subdir/down'])
+
+    def test_grep_in_subir(self):
+        self.assertGrepResultIs('contents of subdir/down\n', ['down', 'down'],
+                                working_dir='subdir')
+        self.assertGrepResultIs('contents of top\n', ['top', '../top'],
+                                working_dir='subdir')
+
+    def test_grep_in_subdir_doesnt_go_up(self):
+        self.assertGrepResultIs('contents of subdir/down\n', ['contents'],
+                                working_dir='subdir')



More information about the bazaar-commits mailing list