Repository: iterative/dvc

Issue:
status: add --recursive flag
For some reason `dvc status` doesn't have `-R` flag in v 0.93.


Discussion:
Hi @nik123 !

`status` never had `-R` flag. We need to introduce it. All logic is there, we would simply need to add it to `dvc/command/status.py` and `dvc/repo/status.py`, that would then pass it to `collect` and `used_cache` in `dvc/repo/status.py`, similar how push/pull/fetch/etc do that.

Patch:
diff --git a/dvc/command/data_sync.py b/dvc/command/data_sync.py
--- a/dvc/command/data_sync.py
+++ b/dvc/command/data_sync.py
@@ -334,4 +334,12 @@ def add_parser(subparsers, _parent_parser):
         default=False,
         help="Show status for all dependencies of the specified target.",
     )
+    status_parser.add_argument(
+        "-R",
+        "--recursive",
+        action="store_true",
+        default=False,
+        help="Show status of all stages in the specified directory.",
+    )
+
     status_parser.set_defaults(func=CmdDataStatus)
diff --git a/dvc/command/status.py b/dvc/command/status.py
--- a/dvc/command/status.py
+++ b/dvc/command/status.py
@@ -49,6 +49,7 @@ def run(self):
                 all_tags=self.args.all_tags,
                 all_commits=self.args.all_commits,
                 with_deps=self.args.with_deps,
+                recursive=self.args.recursive,
             )
             if st:
                 if self.args.quiet:
diff --git a/dvc/repo/status.py b/dvc/repo/status.py
--- a/dvc/repo/status.py
+++ b/dvc/repo/status.py
@@ -25,11 +25,14 @@ def _joint_status(stages):
     return status
 
 
-def _local_status(self, targets=None, with_deps=False):
+def _local_status(self, targets=None, with_deps=False, recursive=False):
     if targets:
-        stages = cat(self.collect(t, with_deps=with_deps) for t in targets)
+        stages = cat(
+            self.collect(t, with_deps=with_deps, recursive=recursive)
+            for t in targets
+        )
     else:
-        stages = self.collect(None, with_deps=with_deps)
+        stages = self.collect(None, with_deps=with_deps, recursive=recursive)
 
     return _joint_status(stages)
 
@@ -42,6 +45,7 @@ def _cloud_status(
     all_branches=False,
     with_deps=False,
     all_tags=False,
+    recursive=False,
     all_commits=False,
 ):
     """Returns a dictionary with the files that are new or deleted.
@@ -81,6 +85,7 @@ def _cloud_status(
         force=True,
         remote=remote,
         jobs=jobs,
+        recursive=recursive,
     )
 
     ret = {}
@@ -109,6 +114,7 @@ def status(
     with_deps=False,
     all_tags=False,
     all_commits=False,
+    recursive=False,
 ):
     if cloud or remote:
         return _cloud_status(
@@ -120,6 +126,7 @@ def status(
             remote=remote,
             all_tags=all_tags,
             all_commits=all_commits,
+            recursive=True,
         )
 
     ignored = list(
@@ -132,4 +139,6 @@ def status(
         msg = "The following options are meaningless for local status: {}"
         raise InvalidArgumentError(msg.format(", ".join(ignored)))
 
-    return _local_status(self, targets, with_deps=with_deps)
+    return _local_status(
+        self, targets, with_deps=with_deps, recursive=recursive
+    )


Test patch:
diff --git a/tests/func/test_status.py b/tests/func/test_status.py
--- a/tests/func/test_status.py
+++ b/tests/func/test_status.py
@@ -93,3 +93,23 @@ def test_status_on_pipeline_stages(tmp_dir, dvc, run_copy):
             "changed command",
         ],
     }
+
+
+def test_status_recursive(tmp_dir, dvc):
+    tmp_dir.gen({"dir": {"file": "text1", "subdir": {"file2": "text2"}}})
+    stages = dvc.add("dir", recursive=True, no_commit=True)
+
+    assert len(stages) == 2
+
+    assert dvc.status(targets=["dir"], recursive=True) == {
+        os.path.join("dir", "file.dvc"): [
+            {"changed outs": {os.path.join("dir", "file"): "not in cache"}}
+        ],
+        os.path.join("dir", "subdir", "file2.dvc"): [
+            {
+                "changed outs": {
+                    os.path.join("dir", "subdir", "file2"): "not in cache"
+                }
+            }
+        ],
+    }
diff --git a/tests/unit/command/test_status.py b/tests/unit/command/test_status.py
--- a/tests/unit/command/test_status.py
+++ b/tests/unit/command/test_status.py
@@ -17,6 +17,7 @@ def test_cloud_status(mocker):
             "--all-tags",
             "--all-commits",
             "--with-deps",
+            "--recursive",
         ]
     )
     assert cli_args.func == CmdDataStatus
@@ -35,4 +36,5 @@ def test_cloud_status(mocker):
         all_tags=True,
         all_commits=True,
         with_deps=True,
+        recursive=True,
     )