We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
unable to plot file from repo subfolder
DVC version: `1.8.4`

```
#!/bin/bash

set -ex

rm -rf wspace
mkdir wspace
pushd wspace

mkdir repo
pushd repo

git init --quiet
dvc init --quiet

mkdir subfolder
pushd subfolder

echo '[{"val":1},{"val":3}]' >> plot.json

dvc plots show plot.json
```

fails with `unexpected error - 'plot.json'`

Need to fix for both Git repo and DVC repo.

Probably related to: #4665 #4559

Also, as @sudoandros mentioned under #4665 this error does not happen when dealing with `metrics`.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `dvc/repo/plots/__init__.py`
Content:
```
1 import logging
2 
3 from funcy import cached_property, first, project
4 
5 from dvc.exceptions import DvcException, NoPlotsError
6 from dvc.repo.collect import collect
7 from dvc.schema import PLOT_PROPS
8 from dvc.tree.repo import RepoTree
9 from dvc.utils import relpath
10 
11 logger = logging.getLogger(__name__)
12 
13 
14 class NotAPlotError(DvcException):
15     def __init__(self, out):
16         super().__init__(
17             f"'{out}' is not a known plot. Use `dvc plots modify` to turn it "
18             "into one."
19         )
20 
21 
22 class PropsNotFoundError(DvcException):
23     pass
24 
25 
26 class Plots:
27     def __init__(self, repo):
28         self.repo = repo
29 
30     def collect(self, targets=None, revs=None):
31         """Collects all props and data for plots.
32 
33         Returns a structure like:
34             {rev: {plots.csv: {
35                 props: {x: ..., "header": ..., ...},
36                 data: "...data as a string...",
37             }}}
38         Data parsing is postponed, since it's affected by props.
39         """
40         targets = [targets] if isinstance(targets, str) else targets or []
41         data = {}
42         for rev in self.repo.brancher(revs=revs):
43             # .brancher() adds unwanted workspace
44             if revs is not None and rev not in revs:
45                 continue
46             rev = rev or "workspace"
47 
48             tree = RepoTree(self.repo)
49             plots = _collect_plots(self.repo, targets, rev)
50             for path_info, props in plots.items():
51                 datafile = relpath(path_info, self.repo.root_dir)
52                 if rev not in data:
53                     data[rev] = {}
54                 data[rev].update({datafile: {"props": props}})
55 
56                 # Load data from git or dvc cache
57                 try:
58                     with tree.open(path_info) as fd:
59                         data[rev][datafile]["data"] = fd.read()
60                 except FileNotFoundError:
61                     # This might happen simply because cache is absent
62                     pass
63 
64         return data
65 
66     @staticmethod
67     def render(data, revs=None, props=None, templates=None):
68         """Renders plots"""
69         props = props or {}
70 
71         # Merge data by plot file and apply overriding props
72         plots = _prepare_plots(data, revs, props)
73 
74         return {
75             datafile: _render(datafile, desc["data"], desc["props"], templates)
76             for datafile, desc in plots.items()
77         }
78 
79     def show(self, targets=None, revs=None, props=None, templates=None):
80         from .data import NoMetricInHistoryError
81 
82         data = self.collect(targets, revs)
83 
84         # If any mentioned plot doesn't have any data then that's an error
85         targets = [targets] if isinstance(targets, str) else targets or []
86         for target in targets:
87             if not any("data" in d[target] for d in data.values()):
88                 raise NoMetricInHistoryError(target)
89 
90         # No data at all is a special error with a special message
91         if not data:
92             raise NoPlotsError()
93 
94         if templates is None:
95             templates = self.templates
96         return self.render(data, revs, props, templates)
97 
98     def diff(self, *args, **kwargs):
99         from .diff import diff
100 
101         return diff(self.repo, *args, **kwargs)
102 
103     @staticmethod
104     def _unset(out, props):
105         missing = list(set(props) - set(out.plot.keys()))
106         if missing:
107             raise PropsNotFoundError(
108                 f"display properties {missing} not found in plot '{out}'"
109             )
110 
111         for prop in props:
112             out.plot.pop(prop)
113 
114     def modify(self, path, props=None, unset=None):
115         from dvc.dvcfile import Dvcfile
116 
117         props = props or {}
118         template = props.get("template")
119         if template:
120             self.templates.get_template(template)
121 
122         (out,) = self.repo.find_outs_by_path(path)
123         if not out.plot and unset is not None:
124             raise NotAPlotError(out)
125 
126         # This out will become a plot unless it is one already
127         if not isinstance(out.plot, dict):
128             out.plot = {}
129 
130         if unset:
131             self._unset(out, unset)
132 
133         out.plot.update(props)
134 
135         # Empty dict will move it to non-plots
136         if not out.plot:
137             out.plot = True
138 
139         out.verify_metric()
140 
141         dvcfile = Dvcfile(self.repo, out.stage.path)
142         dvcfile.dump(out.stage, update_lock=False)
143 
144     @cached_property
145     def templates(self):
146         from .template import PlotTemplates
147 
148         return PlotTemplates(self.repo.dvc_dir)
149 
150 
151 def _is_plot(out):
152     return bool(out.plot)
153 
154 
155 def _collect_plots(repo, targets=None, rev=None):
156     plots, path_infos = collect(
157         repo, output_filter=_is_plot, targets=targets, rev=rev
158     )
159     result = {plot.path_info: _plot_props(plot) for plot in plots}
160     result.update({path_info: {} for path_info in path_infos})
161     return result
162 
163 
164 def _plot_props(out):
165     if not out.plot:
166         raise NotAPlotError(out)
167     if isinstance(out.plot, list):
168         raise DvcException("Multiple plots per data file not supported.")
169     if isinstance(out.plot, bool):
170         return {}
171 
172     return project(out.plot, PLOT_PROPS)
173 
174 
175 def _prepare_plots(data, revs, props):
176     """Groups data by plot file.
177 
178     Also resolves props conflicts between revs and applies global props.
179     """
180     # we go in order revs are supplied on props conflict first ones win.
181     revs = iter(data) if revs is None else revs
182 
183     plots, props_revs = {}, {}
184     for rev in revs:
185         # Asked for revision without data
186         if rev not in data:
187             continue
188 
189         for datafile, desc in data[rev].items():
190             # We silently skip on an absent data file,
191             # see also try/except/pass in .collect()
192             if "data" not in desc:
193                 continue
194 
195             # props from command line overwrite plot props from out definition
196             full_props = {**desc["props"], **props}
197 
198             if datafile in plots:
199                 saved = plots[datafile]
200                 if saved["props"] != full_props:
201                     logger.warning(
202                         f"Inconsistent plot props for '{datafile}' in "
203                         f"'{props_revs[datafile]}' and '{rev}'. "
204                         f"Going to use ones from '{props_revs[datafile]}'"
205                     )
206 
207                 saved["data"][rev] = desc["data"]
208             else:
209                 plots[datafile] = {
210                     "props": full_props,
211                     "data": {rev: desc["data"]},
212                 }
213                 # Save rev we got props from
214                 props_revs[datafile] = rev
215 
216     return plots
217 
218 
219 def _render(datafile, datas, props, templates):
220     from .data import PlotData, plot_data
221 
222     # Copy it to not modify a passed value
223     props = props.copy()
224 
225     # Add x and y to fields if set
226     fields = props.get("fields")
227     if fields is not None:
228         fields = {*fields, props.get("x"), props.get("y")} - {None}
229 
230     template = templates.load(props.get("template") or "default")
231 
232     # If x is not set add index field
233     if not props.get("x") and template.has_anchor("x"):
234         props["append_index"] = True
235         props["x"] = PlotData.INDEX_FIELD
236 
237     # Parse all data, preprocess it and collect as a list of dicts
238     data = []
239     for rev, datablob in datas.items():
240         rev_data = plot_data(datafile, rev, datablob).to_datapoints(
241             fields=fields,
242             path=props.get("path"),
243             header=props.get("header", True),
244             append_index=props.get("append_index", False),
245         )
246         data.extend(rev_data)
247 
248     # If y is not set then use last field not used yet
249     if not props.get("y") and template.has_anchor("y"):
250         fields = list(first(data))
251         skip = (PlotData.REVISION_FIELD, props.get("x"))
252         props["y"] = first(f for f in reversed(fields) if f not in skip)
253 
254     return template.render(data, props=props)
255
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

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
@@ -84,7 +84,8 @@
         # If any mentioned plot doesn't have any data then that's an error
         targets = [targets] if isinstance(targets, str) else targets or []
         for target in targets:
-            if not any("data" in d[target] for d in data.values()):
+            rpath = relpath(target, self.repo.root_dir)
+            if not any("data" in d[rpath] for d in data.values()):
                 raise NoMetricInHistoryError(target)
 
         # No data at all is a special error with a special message

{"golden_diff": "diff --git a/dvc/repo/plots/__init__.py b/dvc/repo/plots/__init__.py\n--- a/dvc/repo/plots/__init__.py\n+++ b/dvc/repo/plots/__init__.py\n@@ -84,7 +84,8 @@\n         # If any mentioned plot doesn't have any data then that's an error\n         targets = [targets] if isinstance(targets, str) else targets or []\n         for target in targets:\n-            if not any(\"data\" in d[target] for d in data.values()):\n+            rpath = relpath(target, self.repo.root_dir)\n+            if not any(\"data\" in d[rpath] for d in data.values()):\n                 raise NoMetricInHistoryError(target)\n \n         # No data at all is a special error with a special message\n", "issue": "unable to plot file from repo subfolder\nDVC version: `1.8.4`\r\n\r\n```\r\n#!/bin/bash\r\n\r\nset -ex\r\n\r\nrm -rf wspace\r\nmkdir wspace\r\npushd wspace\r\n\r\nmkdir repo\r\npushd repo\r\n\r\ngit init --quiet\r\ndvc init --quiet\r\n\r\nmkdir subfolder\r\npushd subfolder\r\n\r\necho '[{\"val\":1},{\"val\":3}]' >> plot.json\r\n\r\ndvc plots show plot.json\r\n```\r\n\r\nfails with `unexpected error - 'plot.json'`\r\n\r\nNeed to fix for both Git repo and DVC repo.\r\n\r\nProbably related to: #4665 #4559\r\n\r\nAlso, as @sudoandros mentioned under #4665 this error does not happen when dealing with `metrics`.\n", "before_files": [{"content": "import logging\n\nfrom funcy import cached_property, first, project\n\nfrom dvc.exceptions import DvcException, NoPlotsError\nfrom dvc.repo.collect import collect\nfrom dvc.schema import PLOT_PROPS\nfrom dvc.tree.repo import RepoTree\nfrom dvc.utils import relpath\n\nlogger = logging.getLogger(__name__)\n\n\nclass NotAPlotError(DvcException):\n    def __init__(self, out):\n        super().__init__(\n            f\"'{out}' is not a known plot. Use `dvc plots modify` to turn it \"\n            \"into one.\"\n        )\n\n\nclass PropsNotFoundError(DvcException):\n    pass\n\n\nclass Plots:\n    def __init__(self, repo):\n        self.repo = repo\n\n    def collect(self, targets=None, revs=None):\n        \"\"\"Collects all props and data for plots.\n\n        Returns a structure like:\n            {rev: {plots.csv: {\n                props: {x: ..., \"header\": ..., ...},\n                data: \"...data as a string...\",\n            }}}\n        Data parsing is postponed, since it's affected by props.\n        \"\"\"\n        targets = [targets] if isinstance(targets, str) else targets or []\n        data = {}\n        for rev in self.repo.brancher(revs=revs):\n            # .brancher() adds unwanted workspace\n            if revs is not None and rev not in revs:\n                continue\n            rev = rev or \"workspace\"\n\n            tree = RepoTree(self.repo)\n            plots = _collect_plots(self.repo, targets, rev)\n            for path_info, props in plots.items():\n                datafile = relpath(path_info, self.repo.root_dir)\n                if rev not in data:\n                    data[rev] = {}\n                data[rev].update({datafile: {\"props\": props}})\n\n                # Load data from git or dvc cache\n                try:\n                    with tree.open(path_info) as fd:\n                        data[rev][datafile][\"data\"] = fd.read()\n                except FileNotFoundError:\n                    # This might happen simply because cache is absent\n                    pass\n\n        return data\n\n    @staticmethod\n    def render(data, revs=None, props=None, templates=None):\n        \"\"\"Renders plots\"\"\"\n        props = props or {}\n\n        # Merge data by plot file and apply overriding props\n        plots = _prepare_plots(data, revs, props)\n\n        return {\n            datafile: _render(datafile, desc[\"data\"], desc[\"props\"], templates)\n            for datafile, desc in plots.items()\n        }\n\n    def show(self, targets=None, revs=None, props=None, templates=None):\n        from .data import NoMetricInHistoryError\n\n        data = self.collect(targets, revs)\n\n        # If any mentioned plot doesn't have any data then that's an error\n        targets = [targets] if isinstance(targets, str) else targets or []\n        for target in targets:\n            if not any(\"data\" in d[target] for d in data.values()):\n                raise NoMetricInHistoryError(target)\n\n        # No data at all is a special error with a special message\n        if not data:\n            raise NoPlotsError()\n\n        if templates is None:\n            templates = self.templates\n        return self.render(data, revs, props, templates)\n\n    def diff(self, *args, **kwargs):\n        from .diff import diff\n\n        return diff(self.repo, *args, **kwargs)\n\n    @staticmethod\n    def _unset(out, props):\n        missing = list(set(props) - set(out.plot.keys()))\n        if missing:\n            raise PropsNotFoundError(\n                f\"display properties {missing} not found in plot '{out}'\"\n            )\n\n        for prop in props:\n            out.plot.pop(prop)\n\n    def modify(self, path, props=None, unset=None):\n        from dvc.dvcfile import Dvcfile\n\n        props = props or {}\n        template = props.get(\"template\")\n        if template:\n            self.templates.get_template(template)\n\n        (out,) = self.repo.find_outs_by_path(path)\n        if not out.plot and unset is not None:\n            raise NotAPlotError(out)\n\n        # This out will become a plot unless it is one already\n        if not isinstance(out.plot, dict):\n            out.plot = {}\n\n        if unset:\n            self._unset(out, unset)\n\n        out.plot.update(props)\n\n        # Empty dict will move it to non-plots\n        if not out.plot:\n            out.plot = True\n\n        out.verify_metric()\n\n        dvcfile = Dvcfile(self.repo, out.stage.path)\n        dvcfile.dump(out.stage, update_lock=False)\n\n    @cached_property\n    def templates(self):\n        from .template import PlotTemplates\n\n        return PlotTemplates(self.repo.dvc_dir)\n\n\ndef _is_plot(out):\n    return bool(out.plot)\n\n\ndef _collect_plots(repo, targets=None, rev=None):\n    plots, path_infos = collect(\n        repo, output_filter=_is_plot, targets=targets, rev=rev\n    )\n    result = {plot.path_info: _plot_props(plot) for plot in plots}\n    result.update({path_info: {} for path_info in path_infos})\n    return result\n\n\ndef _plot_props(out):\n    if not out.plot:\n        raise NotAPlotError(out)\n    if isinstance(out.plot, list):\n        raise DvcException(\"Multiple plots per data file not supported.\")\n    if isinstance(out.plot, bool):\n        return {}\n\n    return project(out.plot, PLOT_PROPS)\n\n\ndef _prepare_plots(data, revs, props):\n    \"\"\"Groups data by plot file.\n\n    Also resolves props conflicts between revs and applies global props.\n    \"\"\"\n    # we go in order revs are supplied on props conflict first ones win.\n    revs = iter(data) if revs is None else revs\n\n    plots, props_revs = {}, {}\n    for rev in revs:\n        # Asked for revision without data\n        if rev not in data:\n            continue\n\n        for datafile, desc in data[rev].items():\n            # We silently skip on an absent data file,\n            # see also try/except/pass in .collect()\n            if \"data\" not in desc:\n                continue\n\n            # props from command line overwrite plot props from out definition\n            full_props = {**desc[\"props\"], **props}\n\n            if datafile in plots:\n                saved = plots[datafile]\n                if saved[\"props\"] != full_props:\n                    logger.warning(\n                        f\"Inconsistent plot props for '{datafile}' in \"\n                        f\"'{props_revs[datafile]}' and '{rev}'. \"\n                        f\"Going to use ones from '{props_revs[datafile]}'\"\n                    )\n\n                saved[\"data\"][rev] = desc[\"data\"]\n            else:\n                plots[datafile] = {\n                    \"props\": full_props,\n                    \"data\": {rev: desc[\"data\"]},\n                }\n                # Save rev we got props from\n                props_revs[datafile] = rev\n\n    return plots\n\n\ndef _render(datafile, datas, props, templates):\n    from .data import PlotData, plot_data\n\n    # Copy it to not modify a passed value\n    props = props.copy()\n\n    # Add x and y to fields if set\n    fields = props.get(\"fields\")\n    if fields is not None:\n        fields = {*fields, props.get(\"x\"), props.get(\"y\")} - {None}\n\n    template = templates.load(props.get(\"template\") or \"default\")\n\n    # If x is not set add index field\n    if not props.get(\"x\") and template.has_anchor(\"x\"):\n        props[\"append_index\"] = True\n        props[\"x\"] = PlotData.INDEX_FIELD\n\n    # Parse all data, preprocess it and collect as a list of dicts\n    data = []\n    for rev, datablob in datas.items():\n        rev_data = plot_data(datafile, rev, datablob).to_datapoints(\n            fields=fields,\n            path=props.get(\"path\"),\n            header=props.get(\"header\", True),\n            append_index=props.get(\"append_index\", False),\n        )\n        data.extend(rev_data)\n\n    # If y is not set then use last field not used yet\n    if not props.get(\"y\") and template.has_anchor(\"y\"):\n        fields = list(first(data))\n        skip = (PlotData.REVISION_FIELD, props.get(\"x\"))\n        props[\"y\"] = first(f for f in reversed(fields) if f not in skip)\n\n    return template.render(data, props=props)\n", "path": "dvc/repo/plots/__init__.py"}], "after_files": [{"content": "import logging\n\nfrom funcy import cached_property, first, project\n\nfrom dvc.exceptions import DvcException, NoPlotsError\nfrom dvc.repo.collect import collect\nfrom dvc.schema import PLOT_PROPS\nfrom dvc.tree.repo import RepoTree\nfrom dvc.utils import relpath\n\nlogger = logging.getLogger(__name__)\n\n\nclass NotAPlotError(DvcException):\n    def __init__(self, out):\n        super().__init__(\n            f\"'{out}' is not a known plot. Use `dvc plots modify` to turn it \"\n            \"into one.\"\n        )\n\n\nclass PropsNotFoundError(DvcException):\n    pass\n\n\nclass Plots:\n    def __init__(self, repo):\n        self.repo = repo\n\n    def collect(self, targets=None, revs=None):\n        \"\"\"Collects all props and data for plots.\n\n        Returns a structure like:\n            {rev: {plots.csv: {\n                props: {x: ..., \"header\": ..., ...},\n                data: \"...data as a string...\",\n            }}}\n        Data parsing is postponed, since it's affected by props.\n        \"\"\"\n        targets = [targets] if isinstance(targets, str) else targets or []\n        data = {}\n        for rev in self.repo.brancher(revs=revs):\n            # .brancher() adds unwanted workspace\n            if revs is not None and rev not in revs:\n                continue\n            rev = rev or \"workspace\"\n\n            tree = RepoTree(self.repo)\n            plots = _collect_plots(self.repo, targets, rev)\n            for path_info, props in plots.items():\n                datafile = relpath(path_info, self.repo.root_dir)\n                if rev not in data:\n                    data[rev] = {}\n                data[rev].update({datafile: {\"props\": props}})\n\n                # Load data from git or dvc cache\n                try:\n                    with tree.open(path_info) as fd:\n                        data[rev][datafile][\"data\"] = fd.read()\n                except FileNotFoundError:\n                    # This might happen simply because cache is absent\n                    pass\n\n        return data\n\n    @staticmethod\n    def render(data, revs=None, props=None, templates=None):\n        \"\"\"Renders plots\"\"\"\n        props = props or {}\n\n        # Merge data by plot file and apply overriding props\n        plots = _prepare_plots(data, revs, props)\n\n        return {\n            datafile: _render(datafile, desc[\"data\"], desc[\"props\"], templates)\n            for datafile, desc in plots.items()\n        }\n\n    def show(self, targets=None, revs=None, props=None, templates=None):\n        from .data import NoMetricInHistoryError\n\n        data = self.collect(targets, revs)\n\n        # If any mentioned plot doesn't have any data then that's an error\n        targets = [targets] if isinstance(targets, str) else targets or []\n        for target in targets:\n            rpath = relpath(target, self.repo.root_dir)\n            if not any(\"data\" in d[rpath] for d in data.values()):\n                raise NoMetricInHistoryError(target)\n\n        # No data at all is a special error with a special message\n        if not data:\n            raise NoPlotsError()\n\n        if templates is None:\n            templates = self.templates\n        return self.render(data, revs, props, templates)\n\n    def diff(self, *args, **kwargs):\n        from .diff import diff\n\n        return diff(self.repo, *args, **kwargs)\n\n    @staticmethod\n    def _unset(out, props):\n        missing = list(set(props) - set(out.plot.keys()))\n        if missing:\n            raise PropsNotFoundError(\n                f\"display properties {missing} not found in plot '{out}'\"\n            )\n\n        for prop in props:\n            out.plot.pop(prop)\n\n    def modify(self, path, props=None, unset=None):\n        from dvc.dvcfile import Dvcfile\n\n        props = props or {}\n        template = props.get(\"template\")\n        if template:\n            self.templates.get_template(template)\n\n        (out,) = self.repo.find_outs_by_path(path)\n        if not out.plot and unset is not None:\n            raise NotAPlotError(out)\n\n        # This out will become a plot unless it is one already\n        if not isinstance(out.plot, dict):\n            out.plot = {}\n\n        if unset:\n            self._unset(out, unset)\n\n        out.plot.update(props)\n\n        # Empty dict will move it to non-plots\n        if not out.plot:\n            out.plot = True\n\n        out.verify_metric()\n\n        dvcfile = Dvcfile(self.repo, out.stage.path)\n        dvcfile.dump(out.stage, update_lock=False)\n\n    @cached_property\n    def templates(self):\n        from .template import PlotTemplates\n\n        return PlotTemplates(self.repo.dvc_dir)\n\n\ndef _is_plot(out):\n    return bool(out.plot)\n\n\ndef _collect_plots(repo, targets=None, rev=None):\n    plots, path_infos = collect(\n        repo, output_filter=_is_plot, targets=targets, rev=rev\n    )\n    result = {plot.path_info: _plot_props(plot) for plot in plots}\n    result.update({path_info: {} for path_info in path_infos})\n    return result\n\n\ndef _plot_props(out):\n    if not out.plot:\n        raise NotAPlotError(out)\n    if isinstance(out.plot, list):\n        raise DvcException(\"Multiple plots per data file not supported.\")\n    if isinstance(out.plot, bool):\n        return {}\n\n    return project(out.plot, PLOT_PROPS)\n\n\ndef _prepare_plots(data, revs, props):\n    \"\"\"Groups data by plot file.\n\n    Also resolves props conflicts between revs and applies global props.\n    \"\"\"\n    # we go in order revs are supplied on props conflict first ones win.\n    revs = iter(data) if revs is None else revs\n\n    plots, props_revs = {}, {}\n    for rev in revs:\n        # Asked for revision without data\n        if rev not in data:\n            continue\n\n        for datafile, desc in data[rev].items():\n            # We silently skip on an absent data file,\n            # see also try/except/pass in .collect()\n            if \"data\" not in desc:\n                continue\n\n            # props from command line overwrite plot props from out definition\n            full_props = {**desc[\"props\"], **props}\n\n            if datafile in plots:\n                saved = plots[datafile]\n                if saved[\"props\"] != full_props:\n                    logger.warning(\n                        f\"Inconsistent plot props for '{datafile}' in \"\n                        f\"'{props_revs[datafile]}' and '{rev}'. \"\n                        f\"Going to use ones from '{props_revs[datafile]}'\"\n                    )\n\n                saved[\"data\"][rev] = desc[\"data\"]\n            else:\n                plots[datafile] = {\n                    \"props\": full_props,\n                    \"data\": {rev: desc[\"data\"]},\n                }\n                # Save rev we got props from\n                props_revs[datafile] = rev\n\n    return plots\n\n\ndef _render(datafile, datas, props, templates):\n    from .data import PlotData, plot_data\n\n    # Copy it to not modify a passed value\n    props = props.copy()\n\n    # Add x and y to fields if set\n    fields = props.get(\"fields\")\n    if fields is not None:\n        fields = {*fields, props.get(\"x\"), props.get(\"y\")} - {None}\n\n    template = templates.load(props.get(\"template\") or \"default\")\n\n    # If x is not set add index field\n    if not props.get(\"x\") and template.has_anchor(\"x\"):\n        props[\"append_index\"] = True\n        props[\"x\"] = PlotData.INDEX_FIELD\n\n    # Parse all data, preprocess it and collect as a list of dicts\n    data = []\n    for rev, datablob in datas.items():\n        rev_data = plot_data(datafile, rev, datablob).to_datapoints(\n            fields=fields,\n            path=props.get(\"path\"),\n            header=props.get(\"header\", True),\n            append_index=props.get(\"append_index\", False),\n        )\n        data.extend(rev_data)\n\n    # If y is not set then use last field not used yet\n    if not props.get(\"y\") and template.has_anchor(\"y\"):\n        fields = list(first(data))\n        skip = (PlotData.REVISION_FIELD, props.get(\"x\"))\n        props[\"y\"] = first(f for f in reversed(fields) if f not in skip)\n\n    return template.render(data, props=props)\n", "path": "dvc/repo/plots/__init__.py"}]}