internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: iterative/dvc

Issue:
`dvc check-ignore --stdin` broken in Linux pipelines.
## Bug Report

### Please provide information about your setup

**Output of `dvc version`:**

```console
$ dvc version
1.4.0
```

**Additional Information (if any):**

If applicable, please also provide a `--verbose` output of the command, eg: `dvc add --verbose`.

$ find download_* -ctime -10 | dvc check-ignore --stdin
![image](https://user-images.githubusercontent.com/6745454/90387247-6ddc9b80-e0b8-11ea-914d-52f9e8d29e13.png)

Might caused by 
https://github.com/iterative/dvc/blob/1cfccef22e86acdab7c8fa8c0c6aaec1f576c9e7/dvc/prompt.py#L10-L12


Discussion:

Resolving patch:
diff --git a/dvc/command/check_ignore.py b/dvc/command/check_ignore.py
--- a/dvc/command/check_ignore.py
+++ b/dvc/command/check_ignore.py
@@ -4,7 +4,6 @@
 from dvc.command import completion
 from dvc.command.base import CmdBase, append_doc_link
 from dvc.exceptions import DvcException
-from dvc.prompt import ask
 
 logger = logging.getLogger(__name__)
 
@@ -39,7 +38,7 @@ def _interactive_mode(self):
         ret = 1
         while True:
             try:
-                target = ask("")
+                target = input()
             except (KeyboardInterrupt, EOFError):
                 break
             if target == "":


Test patch:
diff --git a/tests/func/test_check_ignore.py b/tests/func/test_check_ignore.py
--- a/tests/func/test_check_ignore.py
+++ b/tests/func/test_check_ignore.py
@@ -130,8 +130,6 @@ def test_check_ignore_stdin_mode(
 ):
     tmp_dir.gen(DvcIgnore.DVCIGNORE_FILE, "ignored")
     mocker.patch("builtins.input", side_effect=[file, ""])
-    stdout_mock = mocker.patch("sys.stdout")
-    stdout_mock.isatty.return_value = True
 
     assert main(["check-ignore", "--stdin"]) == ret
     assert (file in caplog.text) is output
Repository: iterative/dvc

Issue:
Metric pointing to dir shows IsADirectoryError upon colleciton
Wrap it or skip as we do.


Discussion:

Resolving patch:
diff --git a/dvc/output/local.py b/dvc/output/local.py
--- a/dvc/output/local.py
+++ b/dvc/output/local.py
@@ -87,8 +87,9 @@ def verify_metric(self):
 
         name = "metrics" if self.metric else "plot"
         if os.path.isdir(path):
-            msg = "directory '{}' cannot be used as {}."
-            raise IsADirectoryError(msg.format(self.path_info, name))
+            msg = "directory '%s' cannot be used as %s."
+            logger.debug(msg, str(self.path_info), name)
+            return
 
         if not istextfile(path):
             msg = "binary file '{}' cannot be used as {}."


Test patch:
diff --git a/tests/dir_helpers.py b/tests/dir_helpers.py
--- a/tests/dir_helpers.py
+++ b/tests/dir_helpers.py
@@ -299,7 +299,12 @@ def git_init(path):
 def run_copy(tmp_dir, dvc):
     tmp_dir.gen(
         "copy.py",
-        "import sys, shutil\nshutil.copyfile(sys.argv[1], sys.argv[2])",
+        (
+            "import sys, shutil, os\n"
+            "shutil.copyfile(sys.argv[1], sys.argv[2]) "
+            "if os.path.isfile(sys.argv[1]) "
+            "else shutil.copytree(sys.argv[1], sys.argv[2])"
+        ),
     )
 
     def run_copy(src, dst, **run_kwargs):
diff --git a/tests/func/test_run_single_stage.py b/tests/func/test_run_single_stage.py
--- a/tests/func/test_run_single_stage.py
+++ b/tests/func/test_run_single_stage.py
@@ -992,32 +992,17 @@ def test_should_raise_on_stage_output(tmp_dir, dvc, run_copy):
         run_copy("foo", "name.dvc", single_stage=True)
 
 
-class TestRunDirMetrics:
-    @pytest.fixture(autouse=True)
-    def setup(self, dvc):
-        with open("script.py", "w+") as fobj:
-            fobj.write("import sys\n")
-            fobj.write("import os\n")
-            fobj.write("os.makedirs(sys.argv[1])\n")
-            fobj.write(
-                "with open(os.path.join(sys.argv[1], "
-                "'metrics.json'), 'a+') as fobj:\n"
-            )
-            fobj.write("    fobj.write('foo')\n")
-
-    def test_metrics_dir_cached(self, dvc):
-        with pytest.raises(IsADirectoryError):
-            dvc.run(
-                cmd="python script.py dir", metrics=["dir"], single_stage=True,
-            )
-
-    def test_metrics_dir_not_cached(self, dvc):
-        with pytest.raises(IsADirectoryError):
-            dvc.run(
-                cmd="python script.py dir",
-                metrics_no_cache=["dir"],
-                single_stage=True,
-            )
+@pytest.mark.parametrize(
+    "metrics_type", ["metrics", "metrics_no_cache"],
+)
+def test_metrics_dir(tmp_dir, dvc, caplog, run_copy_metrics, metrics_type):
+    copyargs = {metrics_type: ["dir_metric"]}
+    tmp_dir.gen({"dir": {"file": "content"}})
+    with caplog.at_level(logging.DEBUG, "dvc"):
+        run_copy_metrics("dir", "dir_metric", **copyargs)
+    assert (
+        "directory 'dir_metric' cannot be used as metrics." in caplog.messages
+    )
 
 
 def test_run_force_doesnot_preserve_comments_and_meta(tmp_dir, dvc, run_copy):
Repository: iterative/dvc

Issue:
experiments: experiment git patching will fail if patch contains binary files
Example from experiment using tensorflow ModelCheckpoint files

```
2020-08-24 12:56:57,888 ERROR: failed to apply experiment changes.
------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/pmrowla/git/dvc/dvc/repo/experiments/__init__.py", line 529, in checkout_exp
    self.repo.scm.repo.git.apply(tmp, reverse=True)
  File "/Users/pmrowla/.virtualenvs/dvc/lib/python3.8/site-packages/git/cmd.py", line 542, in <lambda>
    return lambda *args, **kwargs: self._call_process(name, *args, **kwargs)
  File "/Users/pmrowla/.virtualenvs/dvc/lib/python3.8/site-packages/git/cmd.py", line 1005, in _call_process
    return self.execute(call, **exec_kwargs)
  File "/Users/pmrowla/.virtualenvs/dvc/lib/python3.8/site-packages/git/cmd.py", line 822, in execute
    raise GitCommandError(command, status, stderr_value, stdout_value)
git.exc.GitCommandError: Cmd('git') failed due to: exit code(1)
  cmdline: git apply --reverse /var/folders/s_/j8cg24s945dcvlh77c677f080000gn/T/tmptqvdsgr5
  stderr: 'error: missing binary patch data for 'model.hdf5'
error: binary patch does not apply to 'model.hdf5'
error: model.hdf5: patch does not apply'
```

In this case, `model.hdf5` is untracked by both git and dvc. Unless a repro artifact is explicitly gitignored, we should be including it with our experiment/executor output, so we should be using `git diff --full-index --binary` and `git stash --include-untracked` when generating our patches


Discussion:

Resolving patch:
diff --git a/dvc/repo/experiments/__init__.py b/dvc/repo/experiments/__init__.py
--- a/dvc/repo/experiments/__init__.py
+++ b/dvc/repo/experiments/__init__.py
@@ -182,7 +182,9 @@ def _stash_exp(self, *args, params: Optional[dict] = None, **kwargs):
         # patch user's workspace into experiments clone
         tmp = tempfile.NamedTemporaryFile(delete=False).name
         try:
-            self.repo.scm.repo.git.diff(patch=True, output=tmp)
+            self.repo.scm.repo.git.diff(
+                patch=True, full_index=True, binary=True, output=tmp
+            )
             if os.path.getsize(tmp):
                 logger.debug("Patching experiment workspace")
                 self.scm.repo.git.apply(tmp)
@@ -504,12 +506,14 @@ def checkout_exp(self, rev):
         self._scm_checkout(rev)
 
         tmp = tempfile.NamedTemporaryFile(delete=False).name
-        self.scm.repo.head.commit.diff("HEAD~1", patch=True, output=tmp)
+        self.scm.repo.head.commit.diff(
+            "HEAD~1", patch=True, full_index=True, binary=True, output=tmp
+        )
 
         dirty = self.repo.scm.is_dirty()
         if dirty:
             logger.debug("Stashing workspace changes.")
-            self.repo.scm.repo.git.stash("push")
+            self.repo.scm.repo.git.stash("push", "--include-untracked")
 
         try:
             if os.path.getsize(tmp):


Test patch:
Repository: iterative/dvc

Issue:
experiments: workspace files not always updated on `dvc exp checkout`
Checkout into user workspace behavior is inconsistent:

```
$ dvc repro -e --params b1=0.5 # it changes params.yaml in workspace to b1=0.5
$ dvc repro -e --params b1=0.4 # it does NOT change params.yaml in workspace to b1=0.4 (still 0.5 after the previous run)
```


Discussion:
@pmrowla please check the title

Resolving patch:
diff --git a/dvc/repo/experiments/__init__.py b/dvc/repo/experiments/__init__.py
--- a/dvc/repo/experiments/__init__.py
+++ b/dvc/repo/experiments/__init__.py
@@ -557,10 +557,23 @@ def _unstash_workspace(self):
         # to prefer the applied experiment changes over stashed workspace
         # changes. git stash doesn't support merge strategy parameters, but we
         # can do it ourselves with checkout/reset.
+        from git.exc import GitCommandError
+
         logger.debug("Unstashing workspace changes.")
-        self.repo.scm.repo.git.checkout("--ours", "stash@{0}", "--", ".")
-        self.repo.scm.repo.git.reset("HEAD")
-        self.repo.scm.repo.git.stash("drop", "stash@{0}")
+        git_repo = self.repo.scm.repo.git
+
+        # stage workspace changes, then apply stashed changes on top
+        git_repo.add(A=True)
+        try:
+            git_repo.stash("apply", "stash@{0}")
+        except GitCommandError:
+            # stash apply will return error code on merge conflicts,
+            # prefer workspace changes over stash changes
+            git_repo.checkout("--ours", "--", ".")
+
+        # unstage changes and drop the stash entry
+        git_repo.reset("HEAD")
+        git_repo.stash("drop", "stash@{0}")
 
     def checkout(self, *args, **kwargs):
         from dvc.repo.experiments.checkout import checkout


Test patch:
diff --git a/tests/func/experiments/test_experiments.py b/tests/func/experiments/test_experiments.py
--- a/tests/func/experiments/test_experiments.py
+++ b/tests/func/experiments/test_experiments.py
@@ -49,3 +49,30 @@ def test_update_with_pull(tmp_dir, scm, dvc, mocker):
     exp_scm = dvc.experiments.scm
     for rev in expected_revs:
         assert exp_scm.has_rev(rev)
+
+
+def test_checkout(tmp_dir, scm, dvc):
+    tmp_dir.gen("copy.py", COPY_SCRIPT)
+    tmp_dir.gen("params.yaml", "foo: 1")
+    stage = dvc.run(
+        cmd="python copy.py params.yaml metrics.yaml",
+        metrics_no_cache=["metrics.yaml"],
+        params=["foo"],
+        name="copy-file",
+    )
+    scm.add(["dvc.yaml", "dvc.lock", "copy.py", "params.yaml", "metrics.yaml"])
+    scm.commit("init")
+
+    dvc.reproduce(stage.addressing, experiment=True, params=["foo=2"])
+    exp_a = dvc.experiments.scm.get_rev()
+
+    dvc.reproduce(stage.addressing, experiment=True, params=["foo=3"])
+    exp_b = dvc.experiments.scm.get_rev()
+
+    dvc.experiments.checkout(exp_a)
+    assert (tmp_dir / "params.yaml").read_text().strip() == "foo: 2"
+    assert (tmp_dir / "metrics.yaml").read_text().strip() == "foo: 2"
+
+    dvc.experiments.checkout(exp_b)
+    assert (tmp_dir / "params.yaml").read_text().strip() == "foo: 3"
+    assert (tmp_dir / "metrics.yaml").read_text().strip() == "foo: 3"
Repository: iterative/dvc

Issue:
check-ignore: should ignore subrepos
Subrepos are a part of dvcignore now (e.g. https://github.com/iterative/dvc/blob/7fa90e425d1c3f2b1771b541ccca4ac788d1abf9/dvc/ignore.py#L296 ) so this test https://github.com/iterative/dvc/blob/7fa90e425d1c3f2b1771b541ccca4ac788d1abf9/tests/func/test_check_ignore.py#L91 should be backwards.


Discussion:

Resolving patch:
diff --git a/dvc/ignore.py b/dvc/ignore.py
--- a/dvc/ignore.py
+++ b/dvc/ignore.py
@@ -230,7 +230,10 @@ def _update_sub_repo(self, root, dirs):
                 self._ignored_subrepos[root] = self._ignored_subrepos.get(
                     root, set()
                 ) | {d}
-                new_pattern = DvcIgnorePatterns([f"/{d}/"], root)
+                pattern_info = PatternInfo(
+                    f"/{d}/", "in sub_repo:{}".format(d)
+                )
+                new_pattern = DvcIgnorePatterns([pattern_info], root)
                 old_pattern = self.ignores_trie_tree.longest_prefix(root).value
                 if old_pattern:
                     self.ignores_trie_tree[root] = DvcIgnorePatterns(


Test patch:
diff --git a/tests/func/test_check_ignore.py b/tests/func/test_check_ignore.py
--- a/tests/func/test_check_ignore.py
+++ b/tests/func/test_check_ignore.py
@@ -83,12 +83,16 @@ def test_check_ignore_out_side_repo(tmp_dir, dvc):
     assert main(["check-ignore", "-q", "../file"]) == 1
 
 
-def test_check_ignore_sub_repo(tmp_dir, dvc):
+def test_check_ignore_sub_repo(tmp_dir, dvc, caplog):
     tmp_dir.gen(
         {DvcIgnore.DVCIGNORE_FILE: "other", "dir": {".dvc": {}, "foo": "bar"}}
     )
 
-    assert main(["check-ignore", "-q", os.path.join("dir", "foo")]) == 1
+    assert main(["check-ignore", "-d", os.path.join("dir", "foo")]) == 0
+    assert (
+        "in sub_repo:{}\t{}".format("dir", os.path.join("dir", "foo"),)
+        in caplog.text
+    )
 
 
 def test_check_sub_dir_ignore_file(tmp_dir, dvc, caplog):
Repository: iterative/dvc

Issue:
config: deep remote:// notation is broken
@PeterFogh experiencing checkout issues with external outputs, where a path for an output is in remote:// notation and has 2 levels of remote resolution inside of it. Likely caused by https://github.com/iterative/dvc/pull/3298, as it broke between 0.84 and 0.85


Discussion:

Resolving patch:
diff --git a/dvc/tree/__init__.py b/dvc/tree/__init__.py
--- a/dvc/tree/__init__.py
+++ b/dvc/tree/__init__.py
@@ -43,10 +43,10 @@ def _get_conf(repo, **kwargs):
         remote_conf = repo.config["remote"][name.lower()]
     else:
         remote_conf = kwargs
-    return _resolve_remote_refs(repo.config, remote_conf)
+    return _resolve_remote_refs(repo, remote_conf)
 
 
-def _resolve_remote_refs(config, remote_conf):
+def _resolve_remote_refs(repo, remote_conf):
     # Support for cross referenced remotes.
     # This will merge the settings, shadowing base ref with remote_conf.
     # For example, having:
@@ -72,7 +72,7 @@ def _resolve_remote_refs(config, remote_conf):
     if parsed.scheme != "remote":
         return remote_conf
 
-    base = config["remote"][parsed.netloc]
+    base = _get_conf(repo, name=parsed.netloc)
     url = posixpath.join(base["url"], parsed.path.lstrip("/"))
     return {**base, **remote_conf, "url": url}
 


Test patch:
diff --git a/tests/unit/tree/test_tree.py b/tests/unit/tree/test_tree.py
new file mode 100644
--- /dev/null
+++ b/tests/unit/tree/test_tree.py
@@ -0,0 +1,18 @@
+from dvc.path_info import CloudURLInfo
+from dvc.tree import get_cloud_tree
+
+
+def test_get_cloud_tree(tmp_dir, dvc):
+    tmp_dir.add_remote(name="base", url="s3://bucket/path", default=False)
+    tmp_dir.add_remote(name="first", url="remote://base/first", default=False)
+    tmp_dir.add_remote(
+        name="second", url="remote://first/second", default=False
+    )
+
+    base = CloudURLInfo("s3://bucket/path")
+    first = base / "first"
+    second = first / "second"
+
+    assert get_cloud_tree(dvc, name="base").path_info == base
+    assert get_cloud_tree(dvc, name="first").path_info == first
+    assert get_cloud_tree(dvc, name="second").path_info == second
Repository: iterative/dvc

Issue:
Wheels don't build by default, printing many error messages.
## Bug Report
When running `pip install -e ".[all,tests]"`, according to [user-guide/contributing/core](https://dvc.org/doc/user-guide/contributing/core), an alarming number of error messages are printed, such as follows:

```
Building wheel for pyyaml (setup.py) ... error
  ERROR: Command errored out with exit status 1:
   command: /home/david/Documents/job-hunting/dvc/.env/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-nhmea2xe/pyyaml/setup.py'"'"'; __file__='"'"'/tmp/pip-install-nhmea2xe/pyyaml/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' bdist_wheel -d /tmp/pip-wheel-v2ewd51w
       cwd: /tmp/pip-install-nhmea2xe/pyyaml/
  Complete output (6 lines):
  usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
     or: setup.py --help [cmd1 cmd2 ...]
     or: setup.py --help-commands
     or: setup.py cmd --help
  
  error: invalid command 'bdist_wheel'
  ----------------------------------------
  ERROR: Failed building wheel for pyyaml
  Running setup.py clean for pyyaml
```

As per the time-honoured practise of Googling errors and clicking on the first Stack Overflow link, we arrive at https://stackoverflow.com/questions/34819221/why-is-python-setup-py-saying-invalid-command-bdist-wheel-on-travis-ci. This may be a good potential solution.

### Please provide information about your setup
**OS:**
`Ubuntu 20.04.1 LTS`

**Output of `dvc version`:**
`1.7.4+688da0`

**Additional Information (if any):**

I believe this may be largely a cosmetic error, as it seems to only affect wheels, and we may not care about those. Nonetheless, it makes for a bad first-use experience and does not inspire confidence.

An alternative fix is to run `pip install wheel`.

This error was reported during speculative work on https://github.com/iterative/dvc/issues/4395 for https://remoteok.io/remote-jobs/99244-remote-senior-software-engineer-iterative, as part of a job application process.



Discussion:

Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -82,6 +82,7 @@ def run(self):
     "shtab>=1.3.0,<2",
     "rich>=3.0.5",
     "dictdiffer>=0.8.1",
+    "wheel>=0.35.1",
 ]
 
 


Test patch:
Repository: iterative/dvc

Issue:
status: special output for empty projects
How about reporting `There are no data or pipelines yet!` when you run status in an empty project?


Discussion:

Resolving patch:
diff --git a/dvc/command/status.py b/dvc/command/status.py
--- a/dvc/command/status.py
+++ b/dvc/command/status.py
@@ -2,6 +2,7 @@
 
 from dvc.command.data_sync import CmdDataBase
 from dvc.exceptions import DvcException
+from dvc.utils import format_link
 
 logger = logging.getLogger(__name__)
 
@@ -10,6 +11,10 @@ class CmdDataStatus(CmdDataBase):
     STATUS_LEN = 20
     STATUS_INDENT = "\t"
     UP_TO_DATE_MSG = "Data and pipelines are up to date."
+    EMPTY_PROJECT_MSG = (
+        "There are no data or pipelines tracked in this project yet.\n"
+        "See {link} to get started!"
+    ).format(link=format_link("https://dvc.org/doc/start"))
 
     def _normalize(self, s):
         s += ":"
@@ -61,6 +66,8 @@ def run(self):
                 logger.info(json.dumps(st))
             elif st:
                 self._show(st, indent)
+            elif not self.repo.stages:
+                logger.info(self.EMPTY_PROJECT_MSG)
             else:
                 logger.info(self.UP_TO_DATE_MSG)
 
diff --git a/dvc/repo/init.py b/dvc/repo/init.py
--- a/dvc/repo/init.py
+++ b/dvc/repo/init.py
@@ -10,7 +10,9 @@
 from dvc.repo import Repo
 from dvc.scm import SCM
 from dvc.scm.base import SCMError
-from dvc.utils import boxify, relpath
+from dvc.utils import boxify
+from dvc.utils import format_link as fmt_link
+from dvc.utils import relpath
 from dvc.utils.fs import remove
 
 logger = logging.getLogger(__name__)
@@ -22,9 +24,7 @@ def _welcome_message():
             boxify(
                 "DVC has enabled anonymous aggregate usage analytics.\n"
                 "Read the analytics documentation (and how to opt-out) here:\n"
-                "{blue}https://dvc.org/doc/user-guide/analytics{nc}".format(
-                    blue=colorama.Fore.BLUE, nc=colorama.Fore.RESET
-                ),
+                + fmt_link("https://dvc.org/doc/user-guide/analytics"),
                 border_color="red",
             )
         )
@@ -32,14 +32,10 @@ def _welcome_message():
     msg = (
         "{yellow}What's next?{nc}\n"
         "{yellow}------------{nc}\n"
-        "- Check out the documentation: {blue}https://dvc.org/doc{nc}\n"
-        "- Get help and share ideas: {blue}https://dvc.org/chat{nc}\n"
-        "- Star us on GitHub: {blue}https://github.com/iterative/dvc{nc}"
-    ).format(
-        yellow=colorama.Fore.YELLOW,
-        blue=colorama.Fore.BLUE,
-        nc=colorama.Fore.RESET,
-    )
+        f"- Check out the documentation: {fmt_link('https://dvc.org/doc')}\n"
+        f"- Get help and share ideas: {fmt_link('https://dvc.org/chat')}\n"
+        f"- Star us on GitHub: {fmt_link('https://github.com/iterative/dvc')}"
+    ).format(yellow=colorama.Fore.YELLOW, nc=colorama.Fore.RESET)
 
     logger.info(msg)
 


Test patch:
Repository: iterative/dvc

Issue:
have `dvc cache dir` print the current cache dir when no cmd flags are used
> https://discordapp.com/channels/485586884165107732/565699007037571084/577605813271658519

Currently `dvc cache dir` by itself throws an error and display's the command help. It would be useful to, instead, print the location of the currently used cache dir, so that users can always know what it is (even if it's not specified in `.dvc/config` and thus unknown to `dvc config cache.dir`).


Discussion:
@jorgeorpinel How about adding a new command like "dvc cache current" to return the current cache dir?
@cwallaceh hi! why don't we just reuse the existing subcommand - `dvc cache dir`? Do you have any concerns/see any potential problems in that solution?
@shcheklein not really, just an idea of how it could be clearer, I'll go with reusing the existing `dvc cache dir`
Thanks for the suggestion @cwallaceh but I agree with @shcheklein it's best to reuse the existing one, which was the original idea 🤓 

Resolving patch:
diff --git a/dvc/command/cache.py b/dvc/command/cache.py
--- a/dvc/command/cache.py
+++ b/dvc/command/cache.py
@@ -1,12 +1,18 @@
 import argparse
+import logging
 
 from dvc.command import completion
 from dvc.command.base import append_doc_link, fix_subparsers
 from dvc.command.config import CmdConfig
 
+logger = logging.getLogger(__name__)
+
 
 class CmdCacheDir(CmdConfig):
     def run(self):
+        if self.args.value is None and not self.args.unset:
+            logger.info(self.config["cache"]["dir"])
+            return 0
         with self.config.edit(level=self.args.level) as edit:
             edit["cache"]["dir"] = self.args.value
         return 0
@@ -55,6 +61,8 @@ def add_parser(subparsers, parent_parser):
         "value",
         help="Path to cache directory. Relative paths are resolved relative "
         "to the current directory and saved to config relative to the "
-        "config file location.",
+        "config file location. If no path is provided, it returns the "
+        "current cache directory.",
+        nargs="?",
     ).complete = completion.DIR
     cache_dir_parser.set_defaults(func=CmdCacheDir)


Test patch:
diff --git a/tests/func/test_cache.py b/tests/func/test_cache.py
--- a/tests/func/test_cache.py
+++ b/tests/func/test_cache.py
@@ -154,7 +154,7 @@ def test(self):
 class TestCmdCacheDir(TestDvc):
     def test(self):
         ret = main(["cache", "dir"])
-        self.assertEqual(ret, 254)
+        self.assertEqual(ret, 0)
 
     def test_abs_path(self):
         dname = os.path.join(os.path.dirname(self._root_dir), "dir")