Repository: iterative/dvc

Issue:
diff: fails with absolute file paths (2.10.0 ok, 2.11.0 fails)
# Bug Report

## Description

`dvc diff` with absolute paths fail as dvc can't find the path in the repository. Relative paths still work.
This bug was introduce with changes between version 2.10.0 and 2.11.0.

### Reproduce

```bash
pip install dvc==2.11.0

# Init git and dvc
mkdir dvc_test
cd dvc_test
git init
dvc init


# Add empty file with git tag
touch test_file.txt
git add .
git commit -m "initital commit"
git tag first_tag


# Change file and add new commit
echo "1234" > test_file.txt
git add .
git commit -m "second commit"


# Test dvc diff's

dvc diff --targets ./test_file.txt -- HEAD
# Result: Nothing with exitcode 0 (as expected)

dvc diff --targets ~/dvc_test/test_file.txt -- HEAD
# AND of course with user specific path
dvc diff --targets /home/andreas/dvc_test/test_file.txt -- HEAD
# Result:
# ERROR: failed to get diff - The path '/home/andreas/dvc_test/test_file.txt' does not exist in the target repository '/home/andreas/dvc_test' neither as a DVC output nor as a Git-tracked file.

### EXPECTED:

# Change to old dvc version works again
pip install dvc==2.10.0

dvc diff --targets ~/dvc_test/test_file.txt -- HEAD
# Result: Nothing with exitcode 0 (as expected)
```


### Expected
Absolute and relative paths work the same

### Environment information
dvc==2.11.0 -> Fails
dvc==2.10.0 -> OK
<!--
This is required to ensure that we can reproduce the bug.
-->

**Output of `dvc doctor`:**

```console
$ dvc doctor

DVC version: 2.11.0 (pip)
---------------------------------
Platform: Python 3.8.10 on Linux-5.15.0-10033-tuxedo-x86_64-with-glibc2.17
Supports:
        webhdfs (fsspec = 2022.5.0),
        http (aiohttp = 3.8.1, aiohttp-retry = 2.4.6),
        https (aiohttp = 3.8.1, aiohttp-retry = 2.4.6),
        ssh (sshfs = 2022.6.0)
Cache types: hardlink, symlink
Cache directory: ext4 on /dev/mapper/system-root
Caches: local
Remotes: ssh, local
Workspace directory: ext4 on /dev/mapper/system-root
Repo: dvc (subdir), git
```


```console
$ dvc doctor

DVC version: 2.11.0 (pip)
---------------------------------
Platform: Python 3.8.10 on Linux-5.15.0-10033-tuxedo-x86_64-with-glibc2.17
Supports:
        webhdfs (fsspec = 2022.5.0),
        http (aiohttp = 3.8.1, aiohttp-retry = 2.4.6),
        https (aiohttp = 3.8.1, aiohttp-retry = 2.4.6),
        ssh (sshfs = 2022.6.0)
Cache types: hardlink, symlink
Cache directory: ext4 on /dev/mapper/system-root
Caches: local
Remotes: ssh, local
Workspace directory: ext4 on /dev/mapper/system-root
Repo: dvc (subdir), git

You are using dvc version 2.10.2; however, version 2.11.0 is available.
To upgrade, run 'pip install --upgrade dvc'.

DVC version: 2.10.0 (pip)
---------------------------------
Platform: Python 3.8.10 on Linux-5.15.0-10033-tuxedo-x86_64-with-glibc2.17
Supports:
        webhdfs (fsspec = 2022.5.0),
        http (aiohttp = 3.8.1, aiohttp-retry = 2.4.6),
        https (aiohttp = 3.8.1, aiohttp-retry = 2.4.6),
        ssh (sshfs = 2022.6.0)
```


**Additional Information (if any):**

Related issue but slightly different (here it is not about symlinks, just absolute links and not for dvc plots diff, just dvc diff but can be the same)
https://github.com/iterative/dvc/issues/7668




Comments:

Patch:
diff --git a/dvc/repo/diff.py b/dvc/repo/diff.py
--- a/dvc/repo/diff.py
+++ b/dvc/repo/diff.py
@@ -5,6 +5,7 @@
 
 from dvc.exceptions import PathMissingError
 from dvc.repo import locked
+from dvc.utils.collections import ensure_list
 
 logger = logging.getLogger(__name__)
 
@@ -25,6 +26,8 @@ def diff(self, a_rev="HEAD", b_rev=None, targets=None):
     from dvc.fs.dvc import DvcFileSystem
 
     dvcfs = DvcFileSystem(repo=self)
+    targets = ensure_list(targets)
+    targets = [dvcfs.from_os_path(target) for target in targets]
 
     b_rev = b_rev if b_rev else "workspace"
     results = {}
@@ -36,7 +39,7 @@ def diff(self, a_rev="HEAD", b_rev=None, targets=None):
             continue
 
         targets_paths = None
-        if targets is not None:
+        if targets:
             # convert targets to paths, and capture any missing targets
             targets_paths, missing_targets[rev] = _targets_to_paths(
                 dvcfs, targets
@@ -44,7 +47,7 @@ def diff(self, a_rev="HEAD", b_rev=None, targets=None):
 
         results[rev] = _paths_checksums(self, targets_paths)
 
-    if targets is not None:
+    if targets:
         # check for overlapping missing targets between a_rev and b_rev
         for target in set(missing_targets[a_rev]) & set(
             missing_targets[b_rev]
diff --git a/dvc/repo/metrics/show.py b/dvc/repo/metrics/show.py
--- a/dvc/repo/metrics/show.py
+++ b/dvc/repo/metrics/show.py
@@ -11,6 +11,7 @@
 from dvc.repo.live import summary_fs_path
 from dvc.scm import NoSCMError
 from dvc.utils import error_handler, errored_revisions, onerror_collect
+from dvc.utils.collections import ensure_list
 from dvc.utils.serialize import load_yaml
 
 logger = logging.getLogger(__name__)
@@ -113,6 +114,9 @@ def show(
     if onerror is None:
         onerror = onerror_collect
 
+    targets = ensure_list(targets)
+    targets = [repo.dvcfs.from_os_path(target) for target in targets]
+
     res = {}
     for rev in repo.brancher(
         revs=revs,
diff --git a/dvc/repo/params/show.py b/dvc/repo/params/show.py
--- a/dvc/repo/params/show.py
+++ b/dvc/repo/params/show.py
@@ -21,6 +21,7 @@
 from dvc.stage import PipelineStage
 from dvc.ui import ui
 from dvc.utils import error_handler, errored_revisions, onerror_collect
+from dvc.utils.collections import ensure_list
 from dvc.utils.serialize import LOADERS
 
 if TYPE_CHECKING:
@@ -131,6 +132,9 @@ def show(
         onerror = onerror_collect
     res = {}
 
+    targets = ensure_list(targets)
+    targets = [repo.dvcfs.from_os_path(target) for target in targets]
+
     for branch in repo.brancher(revs=revs):
         params = error_handler(_gather_params)(
             repo=repo,
diff --git a/dvc/repo/plots/__init__.py b/dvc/repo/plots/__init__.py
--- a/dvc/repo/plots/__init__.py
+++ b/dvc/repo/plots/__init__.py
@@ -112,6 +112,8 @@ def collect(
         from dvc.utils.collections import ensure_list
 
         targets = ensure_list(targets)
+        targets = [self.repo.dvcfs.from_os_path(target) for target in targets]
+
         for rev in self.repo.brancher(revs=revs):
             # .brancher() adds unwanted workspace
             if revs is not None and rev not in revs:


Test patch:
diff --git a/tests/func/metrics/test_show.py b/tests/func/metrics/test_show.py
--- a/tests/func/metrics/test_show.py
+++ b/tests/func/metrics/test_show.py
@@ -31,6 +31,19 @@ def test_show(tmp_dir, dvc, run_copy_metrics):
     }
 
 
+def test_show_targets(tmp_dir, dvc, run_copy_metrics):
+    tmp_dir.gen("metrics_t.yaml", "foo: 1.1")
+    run_copy_metrics(
+        "metrics_t.yaml", "metrics.yaml", metrics=["metrics.yaml"]
+    )
+    expected = {"": {"data": {"metrics.yaml": {"data": {"foo": 1.1}}}}}
+    assert dvc.metrics.show(targets=["metrics.yaml"]) == expected
+    assert (
+        dvc.metrics.show(targets=(tmp_dir / "metrics.yaml").fs_path)
+        == expected
+    )
+
+
 def test_show_multiple(tmp_dir, dvc, run_copy_metrics):
     tmp_dir.gen("foo_temp", "foo: 1\n")
     tmp_dir.gen("baz_temp", "baz: 2\n")
diff --git a/tests/func/params/test_show.py b/tests/func/params/test_show.py
--- a/tests/func/params/test_show.py
+++ b/tests/func/params/test_show.py
@@ -20,6 +20,16 @@ def test_show(tmp_dir, dvc):
     }
 
 
+def test_show_targets(tmp_dir, dvc):
+    tmp_dir.gen("params.yaml", "foo: bar")
+    dvc.run(cmd="echo params.yaml", params=["foo"], single_stage=True)
+    expected = {"": {"data": {"params.yaml": {"data": {"foo": "bar"}}}}}
+    assert dvc.params.show(targets=["params.yaml"]) == expected
+    assert (
+        dvc.params.show(targets=(tmp_dir / "params.yaml").fs_path) == expected
+    )
+
+
 def test_show_toml(tmp_dir, dvc):
     tmp_dir.gen("params.toml", "[foo]\nbar = 42\nbaz = [1, 2]\n")
     dvc.run(
diff --git a/tests/func/plots/test_show.py b/tests/func/plots/test_show.py
--- a/tests/func/plots/test_show.py
+++ b/tests/func/plots/test_show.py
@@ -13,6 +13,17 @@
 from tests.utils.plots import get_plot
 
 
+def test_show_targets(tmp_dir, dvc):
+    metric = [{"first_val": 100, "val": 2}, {"first_val": 200, "val": 3}]
+    (tmp_dir / "metric.json").dump_json(metric, sort_keys=True)
+
+    plots = dvc.plots.show(targets=["metric.json"])
+    assert get_plot(plots, "workspace", file="metric.json") == metric
+
+    plots = dvc.plots.show(targets=(tmp_dir / "metric.json").fs_path)
+    assert get_plot(plots, "workspace", file="metric.json") == metric
+
+
 def test_plot_cache_missing(tmp_dir, scm, dvc, caplog, run_copy_metrics):
     metric1 = [{"y": 2}, {"y": 3}]
     (tmp_dir / "metric_t.json").dump_json(metric1, sort_keys=True)
diff --git a/tests/func/test_diff.py b/tests/func/test_diff.py
--- a/tests/func/test_diff.py
+++ b/tests/func/test_diff.py
@@ -331,6 +331,18 @@ def test_no_commits(tmp_dir):
     assert Repo.init().diff() == {}
 
 
+def test_abs_target(tmp_dir, scm, dvc):
+    tmp_dir.dvc_gen("file", "text")
+
+    assert dvc.diff(targets=(tmp_dir / "file").fs_path) == {
+        "added": [{"path": "file", "hash": digest("text")}],
+        "deleted": [],
+        "modified": [],
+        "not in cache": [],
+        "renamed": [],
+    }
+
+
 def setup_targets_test(tmp_dir):
     tmp_dir.dvc_gen("file", "first", commit="add a file")