prompt: 
  We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  brancher: rename `working tree` -> `workspace`
  Much more natural term. Was also noted by @dmpetrov while working with plots. Needed for cml release.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  
  --- BEGIN FILE ---
  ```
  ### dvc/repo/plots/show.py
  import copy
  import logging
  import os
  
  from funcy import first, last, project
  
  from dvc.exceptions import DvcException
  from dvc.repo import locked
  from dvc.schema import PLOT_PROPS
  
  from .data import NoMetricInHistoryError, PlotData
  from .template import NoDataForTemplateError, Template
  
  logger = logging.getLogger(__name__)
  
  
  class TooManyDataSourcesError(DvcException):
      def __init__(self, datafile, template_datafiles):
          super().__init__(
              "Unable to infer which of possible data sources: '{}' "
              "should be replaced with '{}'.".format(
                  ", ".join(template_datafiles), datafile
              )
          )
  
  
  class NoDataOrTemplateProvided(DvcException):
      def __init__(self):
          super().__init__("Datafile or template is not specified.")
  
  
  class TooManyTemplatesError(DvcException):
      pass
  
  
  def _evaluate_templatepath(repo, template=None):
      if not template:
          return repo.plot_templates.default_template
  
      if os.path.exists(template):
          return template
      return repo.plot_templates.get_template(template)
  
  
  @locked
  def fill_template(
      repo, datafile, template_path, revisions, props,
  ):
      # Copy things to not modify passed values
      props = props.copy()
      fields = copy.copy(props.get("fields"))
  
      if props.get("x") and fields:
          fields.add(props.get("x"))
  
      if props.get("y") and fields:
          fields.add(props.get("y"))
  
      template_datafiles, x_anchor, y_anchor = _parse_template(
          template_path, datafile
      )
      append_index = x_anchor and not props.get("x")
      if append_index:
          props["x"] = PlotData.INDEX_FIELD
  
      template_data = {}
      for template_datafile in template_datafiles:
          from .data import _load_from_revisions
  
          plot_datas = _load_from_revisions(repo, template_datafile, revisions)
          tmp_data = []
          for pd in plot_datas:
              rev_data_points = pd.to_datapoints(
                  fields=fields,
                  path=props.get("path"),
                  csv_header=props.get("csv_header", True),
                  append_index=append_index,
              )
  
              if y_anchor and not props.get("y"):
                  props["y"] = _infer_y_field(rev_data_points, props.get("x"))
              tmp_data.extend(rev_data_points)
  
          template_data[template_datafile] = tmp_data
  
      if len(template_data) == 0:
          raise NoDataForTemplateError(template_path)
  
      content = Template.fill(
          template_path, template_data, priority_datafile=datafile, props=props,
      )
  
      path = datafile or ",".join(template_datafiles)
  
      return path, content
  
  
  def _infer_y_field(rev_data_points, x_field):
      all_fields = list(first(rev_data_points).keys())
      all_fields.remove(PlotData.REVISION_FIELD)
      if x_field and x_field in all_fields:
          all_fields.remove(x_field)
      y_field = last(all_fields)
      return y_field
  
  
  def _show(repo, datafile=None, revs=None, props=None):
      if revs is None:
          revs = ["working tree"]
  
      if not datafile and not props.get("template"):
          raise NoDataOrTemplateProvided()
  
      template_path = _evaluate_templatepath(repo, props.get("template"))
  
      plot_datafile, plot_content = fill_template(
          repo, datafile, template_path, revs, props
      )
  
      return plot_datafile, plot_content
  
  
  def _parse_template(template_path, priority_datafile):
      with open(template_path) as fobj:
          tempalte_content = fobj.read()
  
      template_datafiles = Template.parse_data_anchors(tempalte_content)
      if priority_datafile:
          if len(template_datafiles) > 1:
              raise TooManyDataSourcesError(
                  priority_datafile, template_datafiles
              )
          template_datafiles = {priority_datafile}
  
      return (
          template_datafiles,
          Template.X_ANCHOR in tempalte_content,
          Template.Y_ANCHOR in tempalte_content,
      )
  
  
  def _collect_plots(repo, targets=None):
      from dvc.exceptions import OutputNotFoundError
      from contextlib import suppress
  
      def _targets_to_outs(targets):
          for t in targets:
              with suppress(OutputNotFoundError):
                  (out,) = repo.find_outs_by_path(t)
                  yield out
  
      if targets:
          outs = _targets_to_outs(targets)
      else:
          outs = (out for stage in repo.stages for out in stage.outs if out.plot)
  
      return {str(out): _plot_props(out) for out in outs}
  
  
  def _plot_props(out):
      if not out.plot:
          raise DvcException(
              f"'{out}' is not a plot. Use `dvc plots modify` to change that."
          )
      if isinstance(out.plot, list):
          raise DvcException("Multiple plots per data file not supported yet.")
      if isinstance(out.plot, bool):
          return {}
  
      return project(out.plot, PLOT_PROPS)
  
  
  def show(repo, targets=None, revs=None, props=None) -> dict:
      if isinstance(targets, str):
          targets = [targets]
      if props is None:
          props = {}
  
      # Collect plot data files with associated props
      plots = {}
      for rev in repo.brancher(revs=revs):
          if revs is not None and rev not in revs:
              continue
  
          for datafile, file_props in _collect_plots(repo, targets).items():
              # props from command line overwrite plot props from out definition
              full_props = {**file_props, **props}
  
              if datafile in plots:
                  saved_rev, saved_props = plots[datafile]
                  if saved_props != props:
                      logger.warning(
                          f"Inconsistent plot props for '{datafile}' in "
                          f"'{saved_rev}' and '{rev}'. "
                          f"Going to use ones from '{saved_rev}'"
                      )
              else:
                  plots[datafile] = rev, full_props
  
      if not plots:
          if targets:
              raise NoMetricInHistoryError(", ".join(targets))
  
          datafile, plot = _show(repo, datafile=None, revs=revs, props=props)
          return {datafile: plot}
  
      return {
          datafile: _show(repo, datafile=datafile, revs=revs, props=props)[1]
          for datafile, (_, props) in plots.items()
      }
  
  
  ### dvc/repo/brancher.py
  from funcy import group_by
  
  from dvc.scm.tree import WorkingTree
  
  
  def brancher(  # noqa: E302
      self, revs=None, all_branches=False, all_tags=False, all_commits=False
  ):
      """Generator that iterates over specified revisions.
  
      Args:
          revs (list): a list of revisions to iterate over.
          all_branches (bool): iterate over all available branches.
          all_commits (bool): iterate over all commits.
          all_tags (bool): iterate over all available tags.
  
      Yields:
          str: the display name for the currently selected tree, it could be:
              - a git revision identifier
              - empty string it there is no branches to iterate over
              - "Working Tree" if there are uncommitted changes in the SCM repo
      """
      if not any([revs, all_branches, all_tags, all_commits]):
          yield ""
          return
  
      saved_tree = self.tree
      revs = revs.copy() if revs else []
  
      scm = self.scm
  
      self.tree = WorkingTree(self.root_dir)
      yield "working tree"
  
      if revs and "working tree" in revs:
          revs.remove("working tree")
  
      if all_commits:
          revs = scm.list_all_commits()
      else:
          if all_branches:
              revs.extend(scm.list_branches())
  
          if all_tags:
              revs.extend(scm.list_tags())
  
      try:
          if revs:
              for sha, names in group_by(scm.resolve_rev, revs).items():
                  self.tree = scm.get_tree(sha)
                  # ignore revs that don't contain repo root
                  # (i.e. revs from before a subdir=True repo was init'ed)
                  if self.tree.exists(self.root_dir):
                      yield ", ".join(names)
      finally:
          self.tree = saved_tree
  
  
  ### dvc/command/gc.py
  import argparse
  import logging
  import os
  
  import dvc.prompt as prompt
  from dvc.command.base import CmdBase, append_doc_link
  
  logger = logging.getLogger(__name__)
  
  
  class CmdGC(CmdBase):
      def run(self):
          from dvc.repo.gc import _raise_error_if_all_disabled
  
          _raise_error_if_all_disabled(
              all_branches=self.args.all_branches,
              all_tags=self.args.all_tags,
              all_commits=self.args.all_commits,
              workspace=self.args.workspace,
          )
  
          msg = "This will remove all cache except items used in "
  
          msg += "the working tree"
          if self.args.all_commits:
              msg += " and all git commits"
          elif self.args.all_branches and self.args.all_tags:
              msg += " and all git branches and tags"
          elif self.args.all_branches:
              msg += " and all git branches"
          elif self.args.all_tags:
              msg += " and all git tags"
  
          if self.args.repos:
              msg += " of the current and the following repos:"
  
              for repo_path in self.args.repos:
                  msg += "\n  - %s" % os.path.abspath(repo_path)
          else:
              msg += " of the current repo."
  
          logger.warning(msg)
  
          msg = "Are you sure you want to proceed?"
          if not self.args.force and not prompt.confirm(msg):
              return 1
  
          self.repo.gc(
              all_branches=self.args.all_branches,
              all_tags=self.args.all_tags,
              all_commits=self.args.all_commits,
              cloud=self.args.cloud,
              remote=self.args.remote,
              force=self.args.force,
              jobs=self.args.jobs,
              repos=self.args.repos,
              workspace=self.args.workspace,
          )
          return 0
  
  
  def add_parser(subparsers, parent_parser):
      GC_HELP = "Garbage collect unused objects from cache or remote storage."
      GC_DESCRIPTION = (
          "Removes all files in the cache or a remote which are not in\n"
          "use by the specified Git revisions (defaults to just HEAD)."
      )
      gc_parser = subparsers.add_parser(
          "gc",
          parents=[parent_parser],
          description=append_doc_link(GC_DESCRIPTION, "gc"),
          help=GC_HELP,
          formatter_class=argparse.RawDescriptionHelpFormatter,
      )
      gc_parser.add_argument(
          "-w",
          "--workspace",
          action="store_true",
          default=False,
          help="Keep data files used in the current workspace (working tree).",
      )
      gc_parser.add_argument(
          "-a",
          "--all-branches",
          action="store_true",
          default=False,
          help="Keep data files for the tips of all Git branches.",
      )
      gc_parser.add_argument(
          "-T",
          "--all-tags",
          action="store_true",
          default=False,
          help="Keep data files for all Git tags.",
      )
      gc_parser.add_argument(
          "--all-commits",
          action="store_true",
          default=False,
          help="Keep data files for all Git commits.",
      )
      gc_parser.add_argument(
          "-c",
          "--cloud",
          action="store_true",
          default=False,
          help="Collect garbage in remote repository.",
      )
      gc_parser.add_argument(
          "-r",
          "--remote",
          help="Remote storage to collect garbage in",
          metavar="<name>",
      )
      gc_parser.add_argument(
          "-f",
          "--force",
          action="store_true",
          default=False,
          help="Force garbage collection - automatically agree to all prompts.",
      )
      gc_parser.add_argument(
          "-j",
          "--jobs",
          type=int,
          help="Number of jobs to run simultaneously.",
          metavar="<number>",
      )
      gc_parser.add_argument(
          "-p",
          "--projects",
          dest="repos",
          type=str,
          nargs="*",
          help="Keep data files required by these projects "
          "in addition to the current one. "
          "Useful if you share a single cache across repos.",
          metavar="<paths>",
      )
      gc_parser.set_defaults(func=CmdGC)
  
  
  ### dvc/repo/plots/data.py
  import csv
  import io
  import json
  import logging
  import os
  from collections import OrderedDict
  from copy import copy
  
  import yaml
  from funcy import first
  from yaml import SafeLoader
  
  from dvc.exceptions import DvcException, PathMissingError
  
  logger = logging.getLogger(__name__)
  
  
  class PlotMetricTypeError(DvcException):
      def __init__(self, file):
          super().__init__(
              "'{}' - file type error\n"
              "Only JSON, YAML, CSV and TSV formats are supported.".format(file)
          )
  
  
  class PlotDataStructureError(DvcException):
      def __init__(self):
          super().__init__(
              "Plot data extraction failed. Please see "
              "https://man.dvc.org/plot for supported data formats."
          )
  
  
  class JsonParsingError(DvcException):
      def __init__(self, file):
          super().__init__(
              "Failed to infer data structure from '{}'. Did you forget "
              "to specify JSONpath?".format(file)
          )
  
  
  class NoMetricOnRevisionError(DvcException):
      def __init__(self, path, revision):
          self.path = path
          self.revision = revision
          super().__init__(f"Could not find '{path}' on revision '{revision}'")
  
  
  class NoMetricInHistoryError(DvcException):
      def __init__(self, path):
          super().__init__(f"Could not find '{path}'.")
  
  
  def plot_data(filename, revision, content):
      _, extension = os.path.splitext(filename.lower())
      if extension == ".json":
          return JSONPlotData(filename, revision, content)
      elif extension == ".csv":
          return CSVPlotData(filename, revision, content)
      elif extension == ".tsv":
          return CSVPlotData(filename, revision, content, delimiter="\t")
      elif extension == ".yaml":
          return YAMLPlotData(filename, revision, content)
      raise PlotMetricTypeError(filename)
  
  
  def _filter_fields(data_points, filename, revision, fields=None, **kwargs):
      if not fields:
          return data_points
      assert isinstance(fields, set)
  
      new_data = []
      for data_point in data_points:
          new_dp = copy(data_point)
  
          keys = set(data_point.keys())
          if keys & fields != fields:
              raise DvcException(
                  "Could not find fields: '{}' for '{}' at '{}'.".format(
                      ", " "".join(fields), filename, revision
                  )
              )
  
          to_del = keys - fields
          for key in to_del:
              del new_dp[key]
          new_data.append(new_dp)
      return new_data
  
  
  def _apply_path(data, path=None, **kwargs):
      if not path or not isinstance(data, dict):
          return data
  
      import jsonpath_ng
  
      found = jsonpath_ng.parse(path).find(data)
      first_datum = first(found)
      if (
          len(found) == 1
          and isinstance(first_datum.value, list)
          and isinstance(first(first_datum.value), dict)
      ):
          data_points = first_datum.value
      elif len(first_datum.path.fields) == 1:
          field_name = first(first_datum.path.fields)
          data_points = [{field_name: datum.value} for datum in found]
      else:
          raise PlotDataStructureError()
  
      if not isinstance(data_points, list) or not (
          isinstance(first(data_points), dict)
      ):
          raise PlotDataStructureError()
  
      return data_points
  
  
  def _lists(dictionary):
      for _, value in dictionary.items():
          if isinstance(value, dict):
              yield from (_lists(value))
          elif isinstance(value, list):
              yield value
  
  
  def _find_data(data, fields=None, **kwargs):
      if not isinstance(data, dict):
          return data
  
      if not fields:
          # just look for first list of dicts
          fields = set()
  
      for l in _lists(data):
          if all(isinstance(dp, dict) for dp in l):
              if set(first(l).keys()) & fields == fields:
                  return l
      raise PlotDataStructureError()
  
  
  def _append_index(data_points, append_index=False, **kwargs):
      if not append_index:
          return data_points
  
      if PlotData.INDEX_FIELD in first(data_points).keys():
          raise DvcException(
              "Cannot append index. Field of same name ('{}') found in data. "
              "Use `-x` to specify x axis field.".format(PlotData.INDEX_FIELD)
          )
  
      for index, data_point in enumerate(data_points):
          data_point[PlotData.INDEX_FIELD] = index
      return data_points
  
  
  def _append_revision(data_points, revision, **kwargs):
      for data_point in data_points:
          data_point[PlotData.REVISION_FIELD] = revision
      return data_points
  
  
  class PlotData:
      REVISION_FIELD = "rev"
      INDEX_FIELD = "index"
  
      def __init__(self, filename, revision, content, **kwargs):
          self.filename = filename
          self.revision = revision
          self.content = content
  
      def raw(self, **kwargs):
          raise NotImplementedError
  
      def _processors(self):
          return [_filter_fields, _append_index, _append_revision]
  
      def to_datapoints(self, **kwargs):
          data = self.raw(**kwargs)
  
          for data_proc in self._processors():
              data = data_proc(
                  data, filename=self.filename, revision=self.revision, **kwargs
              )
          return data
  
  
  class JSONPlotData(PlotData):
      def raw(self, **kwargs):
          return json.loads(self.content, object_pairs_hook=OrderedDict)
  
      def _processors(self):
          parent_processors = super()._processors()
          return [_apply_path, _find_data] + parent_processors
  
  
  class CSVPlotData(PlotData):
      def __init__(self, filename, revision, content, delimiter=","):
          super().__init__(filename, revision, content)
          self.delimiter = delimiter
  
      def raw(self, csv_header=True, **kwargs):
          first_row = first(csv.reader(io.StringIO(self.content)))
  
          if csv_header:
              reader = csv.DictReader(
                  io.StringIO(self.content), delimiter=self.delimiter,
              )
          else:
              reader = csv.DictReader(
                  io.StringIO(self.content),
                  delimiter=self.delimiter,
                  fieldnames=[str(i) for i in range(len(first_row))],
              )
  
          fieldnames = reader.fieldnames
          data = [row for row in reader]
  
          return [
              OrderedDict([(field, data_point[field]) for field in fieldnames])
              for data_point in data
          ]
  
  
  class YAMLPlotData(PlotData):
      def raw(self, **kwargs):
          class OrderedLoader(SafeLoader):
              pass
  
          def construct_mapping(loader, node):
              loader.flatten_mapping(node)
              return OrderedDict(loader.construct_pairs(node))
  
          OrderedLoader.add_constructor(
              yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, construct_mapping
          )
  
          return yaml.load(self.content, OrderedLoader)
  
  
  def _load_from_revision(repo, datafile, revision):
      from dvc.repo.tree import RepoTree
  
      tree = RepoTree(repo)
  
      try:
          with tree.open(datafile) as fobj:
              datafile_content = fobj.read()
  
      except (FileNotFoundError, PathMissingError):
          raise NoMetricOnRevisionError(datafile, revision)
  
      return plot_data(datafile, revision, datafile_content)
  
  
  def _load_from_revisions(repo, datafile, revisions):
      data = []
      exceptions = []
  
      for rev in repo.brancher(revs=revisions):
          if rev == "working tree" and rev not in revisions:
              continue
  
          try:
              data.append(_load_from_revision(repo, datafile, rev))
          except NoMetricOnRevisionError as e:
              exceptions.append(e)
          except PlotMetricTypeError:
              raise
          except (yaml.error.YAMLError, json.decoder.JSONDecodeError, csv.Error):
              logger.error(f"Failed to parse '{datafile}' at '{rev}'.")
              raise
  
      if not data and exceptions:
          raise NoMetricInHistoryError(datafile)
      else:
          for e in exceptions:
              logger.warning(
                  "File '{}' was not found at: '{}'. It will not be "
                  "plotted.".format(e.path, e.revision)
              )
      return data
  
  
  ### dvc/repo/diff.py
  import os
  
  from dvc.repo import locked
  from dvc.scm.tree import is_working_tree
  
  
  @locked
  def diff(self, a_rev="HEAD", b_rev=None):
      """
      By default, it compares the working tree with the last commit's tree.
  
      This implementation differs from `git diff` since DVC doesn't have
      the concept of `index`, but it keeps the same interface, thus,
      `dvc diff` would be the same as `dvc diff HEAD`.
      """
  
      def _paths_checksums():
          """
          A dictionary of checksums addressed by relpaths collected from
          the current tree outputs.
  
          To help distinguish between a directory and a file output,
          the former one will come with a trailing slash in the path:
  
              directory: "data/"
              file:      "data"
          """
  
          def _to_path(output):
              return (
                  str(output)
                  if not output.is_dir_checksum
                  else os.path.join(str(output), "")
              )
  
          on_working_tree = is_working_tree(self.tree)
  
          def _to_checksum(output):
              if on_working_tree:
                  return self.cache.local.get_checksum(output.path_info)
              return output.checksum
  
          def _exists(output):
              if on_working_tree:
                  return output.exists
              return True
  
          return {
              _to_path(output): _to_checksum(output)
              for stage in self.stages
              for output in stage.outs
              if _exists(output)
          }
  
      if self.scm.no_commits:
          return {}
  
      working_tree = self.tree
      a_tree = self.scm.get_tree(a_rev)
      b_tree = self.scm.get_tree(b_rev) if b_rev else working_tree
  
      try:
          self.tree = a_tree
          old = _paths_checksums()
  
          self.tree = b_tree
          new = _paths_checksums()
      finally:
          self.tree = working_tree
  
      # Compare paths between the old and new tree.
      # set() efficiently converts dict keys to a set
      added = sorted(set(new) - set(old))
      deleted = sorted(set(old) - set(new))
      modified = sorted(set(old) & set(new))
  
      ret = {
          "added": [{"path": path, "hash": new[path]} for path in added],
          "deleted": [{"path": path, "hash": old[path]} for path in deleted],
          "modified": [
              {"path": path, "hash": {"old": old[path], "new": new[path]}}
              for path in modified
              if old[path] != new[path]
          ],
      }
  
      return ret if any(ret.values()) else {}
  
  
  ### dvc/repo/params/show.py
  import logging
  
  import yaml
  
  from dvc.dependency.param import ParamsDependency
  from dvc.exceptions import DvcException
  from dvc.path_info import PathInfo
  from dvc.repo import locked
  
  logger = logging.getLogger(__name__)
  
  
  class NoParamsError(DvcException):
      pass
  
  
  def _collect_configs(repo):
      configs = set()
      configs.add(PathInfo(repo.root_dir) / ParamsDependency.DEFAULT_PARAMS_FILE)
      for stage in repo.stages:
          for dep in stage.deps:
              if not isinstance(dep, ParamsDependency):
                  continue
  
              configs.add(dep.path_info)
      return list(configs)
  
  
  def _read_params(repo, configs, rev):
      res = {}
      for config in configs:
          if not repo.tree.exists(config):
              continue
  
          with repo.tree.open(config, "r") as fobj:
              try:
                  res[str(config)] = yaml.safe_load(fobj)
              except yaml.YAMLError:
                  logger.debug(
                      "failed to read '%s' on '%s'", config, rev, exc_info=True
                  )
                  continue
  
      return res
  
  
  @locked
  def show(repo, revs=None):
      res = {}
  
      for branch in repo.brancher(revs=revs):
          configs = _collect_configs(repo)
          params = _read_params(repo, configs, branch)
  
          if params:
              res[branch] = params
  
      if not res:
          raise NoParamsError("no parameter configs files in this repository")
  
      # Hide working tree params if they are the same as in the active branch
      try:
          active_branch = repo.scm.active_branch()
      except TypeError:
          pass  # Detached head
      else:
          if res.get("working tree") == res.get(active_branch):
              res.pop("working tree", None)
  
      return res
  
  
  ### dvc/repo/plots/diff.py
  def _revisions(revs, is_dirty):
      revisions = revs or []
      if len(revisions) <= 1:
          if len(revisions) == 0 and is_dirty:
              revisions.append("HEAD")
          revisions.append("working tree")
      return revisions
  
  
  def diff(repo, *args, revs=None, **kwargs):
      return repo.plots.show(
          *args, revs=_revisions(revs, repo.scm.is_dirty()), **kwargs
      )
  
  
  ### dvc/repo/metrics/show.py
  import logging
  import os
  
  import yaml
  
  from dvc.exceptions import NoMetricsError
  from dvc.path_info import PathInfo
  from dvc.repo import locked
  from dvc.repo.tree import RepoTree
  
  logger = logging.getLogger(__name__)
  
  
  def _collect_metrics(repo, targets, recursive):
      metrics = set()
  
      for stage in repo.stages:
          for out in stage.outs:
              if not out.metric:
                  continue
  
              metrics.add(out.path_info)
  
      if not targets:
          return list(metrics)
  
      target_infos = [PathInfo(os.path.abspath(target)) for target in targets]
  
      def _filter(path_info):
          for info in target_infos:
              func = path_info.isin_or_eq if recursive else path_info.__eq__
              if func(info):
                  return True
          return False
  
      return list(filter(_filter, metrics))
  
  
  def _extract_metrics(metrics):
      if isinstance(metrics, (int, float)):
          return metrics
  
      if not isinstance(metrics, dict):
          return None
  
      ret = {}
      for key, val in metrics.items():
          m = _extract_metrics(val)
          if m:
              ret[key] = m
  
      return ret
  
  
  def _read_metrics(repo, metrics, rev):
      tree = RepoTree(repo)
  
      res = {}
      for metric in metrics:
          if not tree.exists(metric):
              continue
  
          with tree.open(metric, "r") as fobj:
              try:
                  # NOTE this also supports JSON
                  val = yaml.safe_load(fobj)
              except yaml.YAMLError:
                  logger.debug(
                      "failed to read '%s' on '%s'", metric, rev, exc_info=True
                  )
                  continue
  
              val = _extract_metrics(val)
              if val:
                  res[str(metric)] = val
  
      return res
  
  
  @locked
  def show(
      repo,
      targets=None,
      all_branches=False,
      all_tags=False,
      recursive=False,
      revs=None,
      all_commits=False,
  ):
      res = {}
  
      for rev in repo.brancher(
          revs=revs,
          all_branches=all_branches,
          all_tags=all_tags,
          all_commits=all_commits,
      ):
          metrics = _collect_metrics(repo, targets, recursive)
          vals = _read_metrics(repo, metrics, rev)
  
          if vals:
              res[rev] = vals
  
      if not res:
          raise NoMetricsError()
  
      # Hide working tree metrics if they are the same as in the active branch
      try:
          active_branch = repo.scm.active_branch()
      except TypeError:
          pass  # Detached head
      else:
          if res.get("working tree") == res.get(active_branch):
              res.pop("working tree", None)
  
      return res
  
  ```
  --- END FILE ---
  
  Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.
  
  Every *SEARCH/REPLACE* edit must use this format:
  1. The file path
  2. The start of search block: <<<<<<< SEARCH
  3. A contiguous chunk of lines to search for in the existing source code
  4. The dividing line: =======
  5. The lines to replace into the source code
  6. The end of the replace block: >>>>>>> REPLACE
  
  Here is an example:
  
  ```python
  ### mathweb/flask/app.py
  <<<<<<< SEARCH
  from flask import Flask
  =======
  import math
  from flask import Flask
  >>>>>>> REPLACE
  ```
  
  Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
  Wrap the *SEARCH/REPLACE* edit in blocks ```python...```.
  
instance: {"FAIL_TO_PASS": ["tests/unit/repo/plots/test_diff.py::test_revisions[arg_revisions1-True-expected_revisions1]", "tests/unit/repo/plots/test_diff.py::test_revisions[arg_revisions0-False-expected_revisions0]"], "PASS_TO_PASS": ["tests/unit/repo/plots/test_diff.py::test_revisions[arg_revisions2-False-expected_revisions2]", "tests/func/params/test_show.py::test_show_empty", "tests/func/plots/test_plots.py::test_throw_on_no_metric_at_all", "tests/func/plots/test_plots.py::test_should_raise_on_no_template_and_datafile", "tests/func/plots/test_plots.py::test_plot_no_data", "tests/unit/repo/plots/test_diff.py::test_revisions[arg_revisions3-True-expected_revisions3]", "tests/func/metrics/test_show.py::test_show_empty"], "base_commit": "516d82e41107438756797611bbb15614cd4d989f", "created_at": "2020-05-29T21:46:59Z", "hints_text": "", "instance_id": "iterative__dvc-3914", "patch": "diff --git a/dvc/command/gc.py b/dvc/command/gc.py\n--- a/dvc/command/gc.py\n+++ b/dvc/command/gc.py\n@@ -21,7 +21,7 @@ def run(self):\n \n         msg = \"This will remove all cache except items used in \"\n \n-        msg += \"the working tree\"\n+        msg += \"the workspace\"\n         if self.args.all_commits:\n             msg += \" and all git commits\"\n         elif self.args.all_branches and self.args.all_tags:\n@@ -77,7 +77,7 @@ def add_parser(subparsers, parent_parser):\n         \"--workspace\",\n         action=\"store_true\",\n         default=False,\n-        help=\"Keep data files used in the current workspace (working tree).\",\n+        help=\"Keep data files used in the current workspace.\",\n     )\n     gc_parser.add_argument(\n         \"-a\",\ndiff --git a/dvc/repo/brancher.py b/dvc/repo/brancher.py\n--- a/dvc/repo/brancher.py\n+++ b/dvc/repo/brancher.py\n@@ -18,7 +18,7 @@ def brancher(  # noqa: E302\n         str: the display name for the currently selected tree, it could be:\n             - a git revision identifier\n             - empty string it there is no branches to iterate over\n-            - \"Working Tree\" if there are uncommitted changes in the SCM repo\n+            - \"workspace\" if there are uncommitted changes in the SCM repo\n     \"\"\"\n     if not any([revs, all_branches, all_tags, all_commits]):\n         yield \"\"\n@@ -30,10 +30,10 @@ def brancher(  # noqa: E302\n     scm = self.scm\n \n     self.tree = WorkingTree(self.root_dir)\n-    yield \"working tree\"\n+    yield \"workspace\"\n \n-    if revs and \"working tree\" in revs:\n-        revs.remove(\"working tree\")\n+    if revs and \"workspace\" in revs:\n+        revs.remove(\"workspace\")\n \n     if all_commits:\n         revs = scm.list_all_commits()\ndiff --git a/dvc/repo/diff.py b/dvc/repo/diff.py\n--- a/dvc/repo/diff.py\n+++ b/dvc/repo/diff.py\n@@ -7,7 +7,7 @@\n @locked\n def diff(self, a_rev=\"HEAD\", b_rev=None):\n     \"\"\"\n-    By default, it compares the working tree with the last commit's tree.\n+    By default, it compares the workspace with the last commit's tree.\n \n     This implementation differs from `git diff` since DVC doesn't have\n     the concept of `index`, but it keeps the same interface, thus,\ndiff --git a/dvc/repo/metrics/show.py b/dvc/repo/metrics/show.py\n--- a/dvc/repo/metrics/show.py\n+++ b/dvc/repo/metrics/show.py\n@@ -104,13 +104,13 @@ def show(\n     if not res:\n         raise NoMetricsError()\n \n-    # Hide working tree metrics if they are the same as in the active branch\n+    # Hide workspace metrics if they are the same as in the active branch\n     try:\n         active_branch = repo.scm.active_branch()\n     except TypeError:\n         pass  # Detached head\n     else:\n-        if res.get(\"working tree\") == res.get(active_branch):\n-            res.pop(\"working tree\", None)\n+        if res.get(\"workspace\") == res.get(active_branch):\n+            res.pop(\"workspace\", None)\n \n     return res\ndiff --git a/dvc/repo/params/show.py b/dvc/repo/params/show.py\n--- a/dvc/repo/params/show.py\n+++ b/dvc/repo/params/show.py\n@@ -58,13 +58,13 @@ def show(repo, revs=None):\n     if not res:\n         raise NoParamsError(\"no parameter configs files in this repository\")\n \n-    # Hide working tree params if they are the same as in the active branch\n+    # Hide workspace params if they are the same as in the active branch\n     try:\n         active_branch = repo.scm.active_branch()\n     except TypeError:\n         pass  # Detached head\n     else:\n-        if res.get(\"working tree\") == res.get(active_branch):\n-            res.pop(\"working tree\", None)\n+        if res.get(\"workspace\") == res.get(active_branch):\n+            res.pop(\"workspace\", None)\n \n     return res\ndiff --git a/dvc/repo/plots/data.py b/dvc/repo/plots/data.py\n--- a/dvc/repo/plots/data.py\n+++ b/dvc/repo/plots/data.py\n@@ -258,7 +258,7 @@ def _load_from_revisions(repo, datafile, revisions):\n     exceptions = []\n \n     for rev in repo.brancher(revs=revisions):\n-        if rev == \"working tree\" and rev not in revisions:\n+        if rev == \"workspace\" and rev not in revisions:\n             continue\n \n         try:\ndiff --git a/dvc/repo/plots/diff.py b/dvc/repo/plots/diff.py\n--- a/dvc/repo/plots/diff.py\n+++ b/dvc/repo/plots/diff.py\n@@ -3,7 +3,7 @@ def _revisions(revs, is_dirty):\n     if len(revisions) <= 1:\n         if len(revisions) == 0 and is_dirty:\n             revisions.append(\"HEAD\")\n-        revisions.append(\"working tree\")\n+        revisions.append(\"workspace\")\n     return revisions\n \n \ndiff --git a/dvc/repo/plots/show.py b/dvc/repo/plots/show.py\n--- a/dvc/repo/plots/show.py\n+++ b/dvc/repo/plots/show.py\n@@ -106,7 +106,7 @@ def _infer_y_field(rev_data_points, x_field):\n \n def _show(repo, datafile=None, revs=None, props=None):\n     if revs is None:\n-        revs = [\"working tree\"]\n+        revs = [\"workspace\"]\n \n     if not datafile and not props.get(\"template\"):\n         raise NoDataOrTemplateProvided()\n", "problem_statement": "brancher: rename `working tree` -> `workspace`\nMuch more natural term. Was also noted by @dmpetrov while working with plots. Needed for cml release.\n", "repo": "iterative/dvc", "test_patch": "diff --git a/tests/func/metrics/test_show.py b/tests/func/metrics/test_show.py\n--- a/tests/func/metrics/test_show.py\n+++ b/tests/func/metrics/test_show.py\n@@ -56,7 +56,7 @@ def test_show_branch(tmp_dir, scm, dvc, run_copy_metrics):\n         tmp_dir.scm_gen(\"metrics.yaml\", \"foo: 2\", commit=\"branch\")\n \n     assert dvc.metrics.show(revs=[\"branch\"]) == {\n-        \"working tree\": {\"metrics.yaml\": {\"foo\": 1}},\n+        \"workspace\": {\"metrics.yaml\": {\"foo\": 1}},\n         \"branch\": {\"metrics.yaml\": {\"foo\": 2}},\n     }\n \n@@ -90,6 +90,6 @@ def test_show_subrepo_with_preexisting_tags(tmp_dir, scm):\n \n     expected_path = os.path.join(\"subdir\", \"metrics.yaml\")\n     assert dvc.metrics.show(all_tags=True) == {\n-        \"working tree\": {expected_path: {\"foo\": 1}},\n+        \"workspace\": {expected_path: {\"foo\": 1}},\n         \"v1\": {expected_path: {\"foo\": 1}},\n     }\ndiff --git a/tests/func/params/test_show.py b/tests/func/params/test_show.py\n--- a/tests/func/params/test_show.py\n+++ b/tests/func/params/test_show.py\n@@ -49,7 +49,7 @@ def test_show_branch(tmp_dir, scm, dvc):\n         tmp_dir.scm_gen(\"params.yaml\", \"foo: baz\", commit=\"branch\")\n \n     assert dvc.params.show(revs=[\"branch\"]) == {\n-        \"working tree\": {\"params.yaml\": {\"foo\": \"bar\"}},\n+        \"workspace\": {\"params.yaml\": {\"foo\": \"bar\"}},\n         \"branch\": {\"params.yaml\": {\"foo\": \"baz\"}},\n     }\n \ndiff --git a/tests/func/plots/test_diff.py b/tests/func/plots/test_diff.py\n--- a/tests/func/plots/test_diff.py\n+++ b/tests/func/plots/test_diff.py\n@@ -32,8 +32,8 @@ def test_diff_dirty(tmp_dir, scm, dvc, run_copy_metrics):\n \n     plot_content = json.loads(plot_string)\n     assert plot_content[\"data\"][\"values\"] == [\n-        {\"y\": 5, PlotData.INDEX_FIELD: 0, \"rev\": \"working tree\"},\n-        {\"y\": 6, PlotData.INDEX_FIELD: 1, \"rev\": \"working tree\"},\n+        {\"y\": 5, PlotData.INDEX_FIELD: 0, \"rev\": \"workspace\"},\n+        {\"y\": 6, PlotData.INDEX_FIELD: 1, \"rev\": \"workspace\"},\n         {\"y\": 3, PlotData.INDEX_FIELD: 0, \"rev\": \"HEAD\"},\n         {\"y\": 5, PlotData.INDEX_FIELD: 1, \"rev\": \"HEAD\"},\n     ]\ndiff --git a/tests/func/plots/test_plots.py b/tests/func/plots/test_plots.py\n--- a/tests/func/plots/test_plots.py\n+++ b/tests/func/plots/test_plots.py\n@@ -60,8 +60,8 @@ def test_plot_csv_one_column(tmp_dir, scm, dvc, run_copy_metrics):\n     plot_content = json.loads(plot_string)\n     assert plot_content[\"title\"] == \"mytitle\"\n     assert plot_content[\"data\"][\"values\"] == [\n-        {\"0\": \"2\", PlotData.INDEX_FIELD: 0, \"rev\": \"working tree\"},\n-        {\"0\": \"3\", PlotData.INDEX_FIELD: 1, \"rev\": \"working tree\"},\n+        {\"0\": \"2\", PlotData.INDEX_FIELD: 0, \"rev\": \"workspace\"},\n+        {\"0\": \"3\", PlotData.INDEX_FIELD: 1, \"rev\": \"workspace\"},\n     ]\n     assert plot_content[\"encoding\"][\"x\"][\"field\"] == PlotData.INDEX_FIELD\n     assert plot_content[\"encoding\"][\"y\"][\"field\"] == \"0\"\n@@ -86,14 +86,14 @@ def test_plot_csv_multiple_columns(tmp_dir, scm, dvc, run_copy_metrics):\n         {\n             \"val\": \"2\",\n             PlotData.INDEX_FIELD: 0,\n-            \"rev\": \"working tree\",\n+            \"rev\": \"workspace\",\n             \"first_val\": \"100\",\n             \"second_val\": \"100\",\n         },\n         {\n             \"val\": \"3\",\n             PlotData.INDEX_FIELD: 1,\n-            \"rev\": \"working tree\",\n+            \"rev\": \"workspace\",\n             \"first_val\": \"200\",\n             \"second_val\": \"300\",\n         },\n@@ -119,13 +119,13 @@ def test_plot_csv_choose_axes(tmp_dir, scm, dvc, run_copy_metrics):\n     assert plot_content[\"data\"][\"values\"] == [\n         {\n             \"val\": \"2\",\n-            \"rev\": \"working tree\",\n+            \"rev\": \"workspace\",\n             \"first_val\": \"100\",\n             \"second_val\": \"100\",\n         },\n         {\n             \"val\": \"3\",\n-            \"rev\": \"working tree\",\n+            \"rev\": \"workspace\",\n             \"first_val\": \"200\",\n             \"second_val\": \"300\",\n         },\n@@ -148,8 +148,8 @@ def test_plot_json_single_val(tmp_dir, scm, dvc, run_copy_metrics):\n \n     plot_json = json.loads(plot_string)\n     assert plot_json[\"data\"][\"values\"] == [\n-        {\"val\": 2, PlotData.INDEX_FIELD: 0, \"rev\": \"working tree\"},\n-        {\"val\": 3, PlotData.INDEX_FIELD: 1, \"rev\": \"working tree\"},\n+        {\"val\": 2, PlotData.INDEX_FIELD: 0, \"rev\": \"workspace\"},\n+        {\"val\": 3, PlotData.INDEX_FIELD: 1, \"rev\": \"workspace\"},\n     ]\n     assert plot_json[\"encoding\"][\"x\"][\"field\"] == PlotData.INDEX_FIELD\n     assert plot_json[\"encoding\"][\"y\"][\"field\"] == \"val\"\n@@ -176,13 +176,13 @@ def test_plot_json_multiple_val(tmp_dir, scm, dvc, run_copy_metrics):\n             \"val\": 2,\n             PlotData.INDEX_FIELD: 0,\n             \"first_val\": 100,\n-            \"rev\": \"working tree\",\n+            \"rev\": \"workspace\",\n         },\n         {\n             \"val\": 3,\n             PlotData.INDEX_FIELD: 1,\n             \"first_val\": 200,\n-            \"rev\": \"working tree\",\n+            \"rev\": \"workspace\",\n         },\n     ]\n     assert plot_content[\"encoding\"][\"x\"][\"field\"] == PlotData.INDEX_FIELD\n@@ -207,8 +207,8 @@ def test_plot_confusion(tmp_dir, dvc, run_copy_metrics):\n \n     plot_content = json.loads(plot_string)\n     assert plot_content[\"data\"][\"values\"] == [\n-        {\"predicted\": \"B\", \"actual\": \"A\", \"rev\": \"working tree\"},\n-        {\"predicted\": \"A\", \"actual\": \"A\", \"rev\": \"working tree\"},\n+        {\"predicted\": \"B\", \"actual\": \"A\", \"rev\": \"workspace\"},\n+        {\"predicted\": \"A\", \"actual\": \"A\", \"rev\": \"workspace\"},\n     ]\n     assert plot_content[\"encoding\"][\"x\"][\"field\"] == \"predicted\"\n     assert plot_content[\"encoding\"][\"y\"][\"field\"] == \"actual\"\n@@ -380,8 +380,8 @@ def test_custom_template(tmp_dir, scm, dvc, custom_template, run_copy_metrics):\n \n     plot_content = json.loads(plot_string)\n     assert plot_content[\"data\"][\"values\"] == [\n-        {\"a\": 1, \"b\": 2, \"rev\": \"working tree\"},\n-        {\"a\": 2, \"b\": 3, \"rev\": \"working tree\"},\n+        {\"a\": 1, \"b\": 2, \"rev\": \"workspace\"},\n+        {\"a\": 2, \"b\": 3, \"rev\": \"workspace\"},\n     ]\n     assert plot_content[\"encoding\"][\"x\"][\"field\"] == \"a\"\n     assert plot_content[\"encoding\"][\"y\"][\"field\"] == \"b\"\n@@ -413,8 +413,8 @@ def test_custom_template_with_specified_data(\n \n     plot_content = json.loads(plot_string)\n     assert plot_content[\"data\"][\"values\"] == [\n-        {\"a\": 1, \"b\": 2, \"rev\": \"working tree\"},\n-        {\"a\": 2, \"b\": 3, \"rev\": \"working tree\"},\n+        {\"a\": 1, \"b\": 2, \"rev\": \"workspace\"},\n+        {\"a\": 2, \"b\": 3, \"rev\": \"workspace\"},\n     ]\n     assert plot_content[\"encoding\"][\"x\"][\"field\"] == \"a\"\n     assert plot_content[\"encoding\"][\"y\"][\"field\"] == \"b\"\n@@ -450,8 +450,8 @@ def test_plot_override_specified_data_source(\n \n     plot_content = json.loads(plot_string)\n     assert plot_content[\"data\"][\"values\"] == [\n-        {\"a\": 1, \"b\": 2, \"rev\": \"working tree\"},\n-        {\"a\": 2, \"b\": 3, \"rev\": \"working tree\"},\n+        {\"a\": 1, \"b\": 2, \"rev\": \"workspace\"},\n+        {\"a\": 2, \"b\": 3, \"rev\": \"workspace\"},\n     ]\n     assert plot_content[\"encoding\"][\"x\"][\"field\"] == \"a\"\n     assert plot_content[\"encoding\"][\"y\"][\"field\"] == \"b\"\n@@ -518,8 +518,8 @@ def test_plot_choose_columns(\n \n     plot_content = json.loads(plot_string)\n     assert plot_content[\"data\"][\"values\"] == [\n-        {\"b\": 2, \"c\": 3, \"rev\": \"working tree\"},\n-        {\"b\": 3, \"c\": 4, \"rev\": \"working tree\"},\n+        {\"b\": 2, \"c\": 3, \"rev\": \"workspace\"},\n+        {\"b\": 3, \"c\": 4, \"rev\": \"workspace\"},\n     ]\n     assert plot_content[\"encoding\"][\"x\"][\"field\"] == \"b\"\n     assert plot_content[\"encoding\"][\"y\"][\"field\"] == \"c\"\n@@ -540,8 +540,8 @@ def test_plot_default_choose_column(tmp_dir, scm, dvc, run_copy_metrics):\n \n     plot_content = json.loads(plot_string)\n     assert plot_content[\"data\"][\"values\"] == [\n-        {PlotData.INDEX_FIELD: 0, \"b\": 2, \"rev\": \"working tree\"},\n-        {PlotData.INDEX_FIELD: 1, \"b\": 3, \"rev\": \"working tree\"},\n+        {PlotData.INDEX_FIELD: 0, \"b\": 2, \"rev\": \"workspace\"},\n+        {PlotData.INDEX_FIELD: 1, \"b\": 3, \"rev\": \"workspace\"},\n     ]\n     assert plot_content[\"encoding\"][\"x\"][\"field\"] == PlotData.INDEX_FIELD\n     assert plot_content[\"encoding\"][\"y\"][\"field\"] == \"b\"\n@@ -560,8 +560,8 @@ def test_plot_yaml(tmp_dir, scm, dvc, run_copy_metrics):\n \n     plot_content = json.loads(plot_string)\n     assert plot_content[\"data\"][\"values\"] == [\n-        {\"val\": 2, PlotData.INDEX_FIELD: 0, \"rev\": \"working tree\"},\n-        {\"val\": 3, PlotData.INDEX_FIELD: 1, \"rev\": \"working tree\"},\n+        {\"val\": 2, PlotData.INDEX_FIELD: 0, \"rev\": \"workspace\"},\n+        {\"val\": 3, PlotData.INDEX_FIELD: 1, \"rev\": \"workspace\"},\n     ]\n \n \ndiff --git a/tests/unit/repo/plots/test_diff.py b/tests/unit/repo/plots/test_diff.py\n--- a/tests/unit/repo/plots/test_diff.py\n+++ b/tests/unit/repo/plots/test_diff.py\n@@ -6,10 +6,10 @@\n @pytest.mark.parametrize(\n     \"arg_revisions,is_dirty,expected_revisions\",\n     [\n-        ([], False, [\"working tree\"]),\n-        ([], True, [\"HEAD\", \"working tree\"]),\n-        ([\"v1\", \"v2\", \"working tree\"], False, [\"v1\", \"v2\", \"working tree\"]),\n-        ([\"v1\", \"v2\", \"working tree\"], True, [\"v1\", \"v2\", \"working tree\"]),\n+        ([], False, [\"workspace\"]),\n+        ([], True, [\"HEAD\", \"workspace\"]),\n+        ([\"v1\", \"v2\", \"workspace\"], False, [\"v1\", \"v2\", \"workspace\"]),\n+        ([\"v1\", \"v2\", \"workspace\"], True, [\"v1\", \"v2\", \"workspace\"]),\n     ],\n )\n def test_revisions(mocker, arg_revisions, is_dirty, expected_revisions):\ndiff --git a/tests/unit/repo/test_repo_tree.py b/tests/unit/repo/test_repo_tree.py\n--- a/tests/unit/repo/test_repo_tree.py\n+++ b/tests/unit/repo/test_repo_tree.py\n@@ -38,7 +38,7 @@ def test_open_in_history(tmp_dir, scm, dvc):\n     dvc.scm.commit(\"foofoo\")\n \n     for rev in dvc.brancher(revs=[\"HEAD~1\"]):\n-        if rev == \"working tree\":\n+        if rev == \"workspace\":\n             continue\n \n         tree = RepoTree(dvc)\ndiff --git a/tests/unit/repo/test_tree.py b/tests/unit/repo/test_tree.py\n--- a/tests/unit/repo/test_tree.py\n+++ b/tests/unit/repo/test_tree.py\n@@ -38,7 +38,7 @@ def test_open_in_history(tmp_dir, scm, dvc):\n     dvc.scm.commit(\"foofoo\")\n \n     for rev in dvc.brancher(revs=[\"HEAD~1\"]):\n-        if rev == \"working tree\":\n+        if rev == \"workspace\":\n             continue\n \n         tree = DvcTree(dvc)\n", "version": "1.0"}
parsed_commit_content: {"file_diffs":[{"old_file_content":"import argparse\nimport logging\nimport os\n\nimport dvc.prompt as prompt\nfrom dvc.command.base import CmdBase, append_doc_link\n\nlogger = logging.getLogger(__name__)\n\n\nclass CmdGC(CmdBase):\n    def run(self):\n        from dvc.repo.gc import _raise_error_if_all_disabled\n\n        _raise_error_if_all_disabled(\n            all_branches=self.args.all_branches,\n            all_tags=self.args.all_tags,\n            all_commits=self.args.all_commits,\n            workspace=self.args.workspace,\n        )\n\n        msg = \"This will remove all cache except items used in \"\n\n        msg += \"the working tree\"\n        if self.args.all_commits:\n            msg += \" and all git commits\"\n        elif self.args.all_branches and self.args.all_tags:\n            msg += \" and all git branches and tags\"\n        elif self.args.all_branches:\n            msg += \" and all git branches\"\n        elif self.args.all_tags:\n            msg += \" and all git tags\"\n\n        if self.args.repos:\n            msg += \" of the current and the following repos:\"\n\n            for repo_path in self.args.repos:\n                msg += \"\\n  - %s\" % os.path.abspath(repo_path)\n        else:\n            msg += \" of the current repo.\"\n\n        logger.warning(msg)\n\n        msg = \"Are you sure you want to proceed?\"\n        if not self.args.force and not prompt.confirm(msg):\n            return 1\n\n        self.repo.gc(\n            all_branches=self.args.all_branches,\n            all_tags=self.args.all_tags,\n            all_commits=self.args.all_commits,\n            cloud=self.args.cloud,\n            remote=self.args.remote,\n            force=self.args.force,\n            jobs=self.args.jobs,\n            repos=self.args.repos,\n            workspace=self.args.workspace,\n        )\n        return 0\n\n\ndef add_parser(subparsers, parent_parser):\n    GC_HELP = \"Garbage collect unused objects from cache or remote storage.\"\n    GC_DESCRIPTION = (\n        \"Removes all files in the cache or a remote which are not in\\n\"\n        \"use by the specified Git revisions (defaults to just HEAD).\"\n    )\n    gc_parser = subparsers.add_parser(\n        \"gc\",\n        parents=[parent_parser],\n        description=append_doc_link(GC_DESCRIPTION, \"gc\"),\n        help=GC_HELP,\n        formatter_class=argparse.RawDescriptionHelpFormatter,\n    )\n    gc_parser.add_argument(\n        \"-w\",\n        \"--workspace\",\n        action=\"store_true\",\n        default=False,\n        help=\"Keep data files used in the current workspace (working tree).\",\n    )\n    gc_parser.add_argument(\n        \"-a\",\n        \"--all-branches\",\n        action=\"store_true\",\n        default=False,\n        help=\"Keep data files for the tips of all Git branches.\",\n    )\n    gc_parser.add_argument(\n        \"-T\",\n        \"--all-tags\",\n        action=\"store_true\",\n        default=False,\n        help=\"Keep data files for all Git tags.\",\n    )\n    gc_parser.add_argument(\n        \"--all-commits\",\n        action=\"store_true\",\n        default=False,\n        help=\"Keep data files for all Git commits.\",\n    )\n    gc_parser.add_argument(\n        \"-c\",\n        \"--cloud\",\n        action=\"store_true\",\n        default=False,\n        help=\"Collect garbage in remote repository.\",\n    )\n    gc_parser.add_argument(\n        \"-r\",\n        \"--remote\",\n        help=\"Remote storage to collect garbage in\",\n        metavar=\"<name>\",\n    )\n    gc_parser.add_argument(\n        \"-f\",\n        \"--force\",\n        action=\"store_true\",\n        default=False,\n        help=\"Force garbage collection - automatically agree to all prompts.\",\n    )\n    gc_parser.add_argument(\n        \"-j\",\n        \"--jobs\",\n        type=int,\n        help=\"Number of jobs to run simultaneously.\",\n        metavar=\"<number>\",\n    )\n    gc_parser.add_argument(\n        \"-p\",\n        \"--projects\",\n        dest=\"repos\",\n        type=str,\n        nargs=\"*\",\n        help=\"Keep data files required by these projects \"\n        \"in addition to the current one. \"\n        \"Useful if you share a single cache across repos.\",\n        metavar=\"<paths>\",\n    )\n    gc_parser.set_defaults(func=CmdGC)\n","new_file_content":"import argparse\nimport logging\nimport os\n\nimport dvc.prompt as prompt\nfrom dvc.command.base import CmdBase, append_doc_link\n\nlogger = logging.getLogger(__name__)\n\n\nclass CmdGC(CmdBase):\n    def run(self):\n        from dvc.repo.gc import _raise_error_if_all_disabled\n\n        _raise_error_if_all_disabled(\n            all_branches=self.args.all_branches,\n            all_tags=self.args.all_tags,\n            all_commits=self.args.all_commits,\n            workspace=self.args.workspace,\n        )\n\n        msg = \"This will remove all cache except items used in \"\n\n        msg += \"the workspace\"\n        if self.args.all_commits:\n            msg += \" and all git commits\"\n        elif self.args.all_branches and self.args.all_tags:\n            msg += \" and all git branches and tags\"\n        elif self.args.all_branches:\n            msg += \" and all git branches\"\n        elif self.args.all_tags:\n            msg += \" and all git tags\"\n\n        if self.args.repos:\n            msg += \" of the current and the following repos:\"\n\n            for repo_path in self.args.repos:\n                msg += \"\\n  - %s\" % os.path.abspath(repo_path)\n        else:\n            msg += \" of the current repo.\"\n\n        logger.warning(msg)\n\n        msg = \"Are you sure you want to proceed?\"\n        if not self.args.force and not prompt.confirm(msg):\n            return 1\n\n        self.repo.gc(\n            all_branches=self.args.all_branches,\n            all_tags=self.args.all_tags,\n            all_commits=self.args.all_commits,\n            cloud=self.args.cloud,\n            remote=self.args.remote,\n            force=self.args.force,\n            jobs=self.args.jobs,\n            repos=self.args.repos,\n            workspace=self.args.workspace,\n        )\n        return 0\n\n\ndef add_parser(subparsers, parent_parser):\n    GC_HELP = \"Garbage collect unused objects from cache or remote storage.\"\n    GC_DESCRIPTION = (\n        \"Removes all files in the cache or a remote which are not in\\n\"\n        \"use by the specified Git revisions (defaults to just HEAD).\"\n    )\n    gc_parser = subparsers.add_parser(\n        \"gc\",\n        parents=[parent_parser],\n        description=append_doc_link(GC_DESCRIPTION, \"gc\"),\n        help=GC_HELP,\n        formatter_class=argparse.RawDescriptionHelpFormatter,\n    )\n    gc_parser.add_argument(\n        \"-w\",\n        \"--workspace\",\n        action=\"store_true\",\n        default=False,\n        help=\"Keep data files used in the current workspace.\",\n    )\n    gc_parser.add_argument(\n        \"-a\",\n        \"--all-branches\",\n        action=\"store_true\",\n        default=False,\n        help=\"Keep data files for the tips of all Git branches.\",\n    )\n    gc_parser.add_argument(\n        \"-T\",\n        \"--all-tags\",\n        action=\"store_true\",\n        default=False,\n        help=\"Keep data files for all Git tags.\",\n    )\n    gc_parser.add_argument(\n        \"--all-commits\",\n        action=\"store_true\",\n        default=False,\n        help=\"Keep data files for all Git commits.\",\n    )\n    gc_parser.add_argument(\n        \"-c\",\n        \"--cloud\",\n        action=\"store_true\",\n        default=False,\n        help=\"Collect garbage in remote repository.\",\n    )\n    gc_parser.add_argument(\n        \"-r\",\n        \"--remote\",\n        help=\"Remote storage to collect garbage in\",\n        metavar=\"<name>\",\n    )\n    gc_parser.add_argument(\n        \"-f\",\n        \"--force\",\n        action=\"store_true\",\n        default=False,\n        help=\"Force garbage collection - automatically agree to all prompts.\",\n    )\n    gc_parser.add_argument(\n        \"-j\",\n        \"--jobs\",\n        type=int,\n        help=\"Number of jobs to run simultaneously.\",\n        metavar=\"<number>\",\n    )\n    gc_parser.add_argument(\n        \"-p\",\n        \"--projects\",\n        dest=\"repos\",\n        type=str,\n        nargs=\"*\",\n        help=\"Keep data files required by these projects \"\n        \"in addition to the current one. \"\n        \"Useful if you share a single cache across repos.\",\n        metavar=\"<paths>\",\n    )\n    gc_parser.set_defaults(func=CmdGC)\n","header":{"file":{"path":"dvc/command/gc.py"},"misc_line":null},"index_line":null,"is_binary_file":false,"binary_line":null,"minus_file":{"path":"a/dvc/command/gc.py"},"plus_file":{"path":"b/dvc/command/gc.py"},"hunks":[{"descriptor":{"old_range":{"start":21,"length":7},"new_range":{"start":21,"length":7},"section":"def run(self):"},"line_group":{"all_lines":[{"content":"","type":"context"},{"content":"        msg = \"This will remove all cache except items used in \"","type":"context"},{"content":"","type":"context"},{"content":"        msg += \"the working tree\"","type":"deleted"},{"content":"        msg += \"the workspace\"","type":"added"},{"content":"        if self.args.all_commits:","type":"context"},{"content":"            msg += \" and all git commits\"","type":"context"},{"content":"        elif self.args.all_branches and self.args.all_tags:","type":"context"}]},"modified_entities":[],"added_entities":[],"deleted_entities":[]},{"descriptor":{"old_range":{"start":77,"length":7},"new_range":{"start":77,"length":7},"section":"def add_parser(subparsers, parent_parser):"},"line_group":{"all_lines":[{"content":"        \"--workspace\",","type":"context"},{"content":"        action=\"store_true\",","type":"context"},{"content":"        default=False,","type":"context"},{"content":"        help=\"Keep data files used in the current workspace (working tree).\",","type":"deleted"},{"content":"        help=\"Keep data files used in the current workspace.\",","type":"added"},{"content":"    )","type":"context"},{"content":"    gc_parser.add_argument(","type":"context"},{"content":"        \"-a\",","type":"context"}]},"modified_entities":[],"added_entities":[],"deleted_entities":[]}]},{"old_file_content":"from funcy import group_by\n\nfrom dvc.scm.tree import WorkingTree\n\n\ndef brancher(  # noqa: E302\n    self, revs=None, all_branches=False, all_tags=False, all_commits=False\n):\n    \"\"\"Generator that iterates over specified revisions.\n\n    Args:\n        revs (list): a list of revisions to iterate over.\n        all_branches (bool): iterate over all available branches.\n        all_commits (bool): iterate over all commits.\n        all_tags (bool): iterate over all available tags.\n\n    Yields:\n        str: the display name for the currently selected tree, it could be:\n            - a git revision identifier\n            - empty string it there is no branches to iterate over\n            - \"Working Tree\" if there are uncommitted changes in the SCM repo\n    \"\"\"\n    if not any([revs, all_branches, all_tags, all_commits]):\n        yield \"\"\n        return\n\n    saved_tree = self.tree\n    revs = revs.copy() if revs else []\n\n    scm = self.scm\n\n    self.tree = WorkingTree(self.root_dir)\n    yield \"working tree\"\n\n    if revs and \"working tree\" in revs:\n        revs.remove(\"working tree\")\n\n    if all_commits:\n        revs = scm.list_all_commits()\n    else:\n        if all_branches:\n            revs.extend(scm.list_branches())\n\n        if all_tags:\n            revs.extend(scm.list_tags())\n\n    try:\n        if revs:\n            for sha, names in group_by(scm.resolve_rev, revs).items():\n                self.tree = scm.get_tree(sha)\n                # ignore revs that don't contain repo root\n                # (i.e. revs from before a subdir=True repo was init'ed)\n                if self.tree.exists(self.root_dir):\n                    yield \", \".join(names)\n    finally:\n        self.tree = saved_tree\n","new_file_content":"from funcy import group_by\n\nfrom dvc.scm.tree import WorkingTree\n\n\ndef brancher(  # noqa: E302\n    self, revs=None, all_branches=False, all_tags=False, all_commits=False\n):\n    \"\"\"Generator that iterates over specified revisions.\n\n    Args:\n        revs (list): a list of revisions to iterate over.\n        all_branches (bool): iterate over all available branches.\n        all_commits (bool): iterate over all commits.\n        all_tags (bool): iterate over all available tags.\n\n    Yields:\n        str: the display name for the currently selected tree, it could be:\n            - a git revision identifier\n            - empty string it there is no branches to iterate over\n            - \"workspace\" if there are uncommitted changes in the SCM repo\n    \"\"\"\n    if not any([revs, all_branches, all_tags, all_commits]):\n        yield \"\"\n        return\n\n    saved_tree = self.tree\n    revs = revs.copy() if revs else []\n\n    scm = self.scm\n\n    self.tree = WorkingTree(self.root_dir)\n    yield \"workspace\"\n\n    if revs and \"workspace\" in revs:\n        revs.remove(\"workspace\")\n\n    if all_commits:\n        revs = scm.list_all_commits()\n    else:\n        if all_branches:\n            revs.extend(scm.list_branches())\n\n        if all_tags:\n            revs.extend(scm.list_tags())\n\n    try:\n        if revs:\n            for sha, names in group_by(scm.resolve_rev, revs).items():\n                self.tree = scm.get_tree(sha)\n                # ignore revs that don't contain repo root\n                # (i.e. revs from before a subdir=True repo was init'ed)\n                if self.tree.exists(self.root_dir):\n                    yield \", \".join(names)\n    finally:\n        self.tree = saved_tree\n","header":{"file":{"path":"dvc/repo/brancher.py"},"misc_line":null},"index_line":null,"is_binary_file":false,"binary_line":null,"minus_file":{"path":"a/dvc/repo/brancher.py"},"plus_file":{"path":"b/dvc/repo/brancher.py"},"hunks":[{"descriptor":{"old_range":{"start":18,"length":7},"new_range":{"start":18,"length":7},"section":"def brancher(  # noqa: E302"},"line_group":{"all_lines":[{"content":"        str: the display name for the currently selected tree, it could be:","type":"context"},{"content":"            - a git revision identifier","type":"context"},{"content":"            - empty string it there is no branches to iterate over","type":"context"},{"content":"            - \"Working Tree\" if there are uncommitted changes in the SCM repo","type":"deleted"},{"content":"            - \"workspace\" if there are uncommitted changes in the SCM repo","type":"added"},{"content":"    \"\"\"","type":"context"},{"content":"    if not any([revs, all_branches, all_tags, all_commits]):","type":"context"},{"content":"        yield \"\"","type":"context"}]},"modified_entities":[],"added_entities":[],"deleted_entities":[]},{"descriptor":{"old_range":{"start":30,"length":10},"new_range":{"start":30,"length":10},"section":"def brancher(  # noqa: E302"},"line_group":{"all_lines":[{"content":"    scm = self.scm","type":"context"},{"content":"","type":"context"},{"content":"    self.tree = WorkingTree(self.root_dir)","type":"context"},{"content":"    yield \"working tree\"","type":"deleted"},{"content":"    yield \"workspace\"","type":"added"},{"content":"","type":"context"},{"content":"    if revs and \"working tree\" in revs:","type":"deleted"},{"content":"        revs.remove(\"working tree\")","type":"deleted"},{"content":"    if revs and \"workspace\" in revs:","type":"added"},{"content":"        revs.remove(\"workspace\")","type":"added"},{"content":"","type":"context"},{"content":"    if all_commits:","type":"context"},{"content":"        revs = scm.list_all_commits()","type":"context"}]},"modified_entities":[],"added_entities":[],"deleted_entities":[]}]},{"old_file_content":"import os\n\nfrom dvc.repo import locked\nfrom dvc.scm.tree import is_working_tree\n\n\n@locked\ndef diff(self, a_rev=\"HEAD\", b_rev=None):\n    \"\"\"\n    By default, it compares the working tree with the last commit's tree.\n\n    This implementation differs from `git diff` since DVC doesn't have\n    the concept of `index`, but it keeps the same interface, thus,\n    `dvc diff` would be the same as `dvc diff HEAD`.\n    \"\"\"\n\n    def _paths_checksums():\n        \"\"\"\n        A dictionary of checksums addressed by relpaths collected from\n        the current tree outputs.\n\n        To help distinguish between a directory and a file output,\n        the former one will come with a trailing slash in the path:\n\n            directory: \"data/\"\n            file:      \"data\"\n        \"\"\"\n\n        def _to_path(output):\n            return (\n                str(output)\n                if not output.is_dir_checksum\n                else os.path.join(str(output), \"\")\n            )\n\n        on_working_tree = is_working_tree(self.tree)\n\n        def _to_checksum(output):\n            if on_working_tree:\n                return self.cache.local.get_checksum(output.path_info)\n            return output.checksum\n\n        def _exists(output):\n            if on_working_tree:\n                return output.exists\n            return True\n\n        return {\n            _to_path(output): _to_checksum(output)\n            for stage in self.stages\n            for output in stage.outs\n            if _exists(output)\n        }\n\n    if self.scm.no_commits:\n        return {}\n\n    working_tree = self.tree\n    a_tree = self.scm.get_tree(a_rev)\n    b_tree = self.scm.get_tree(b_rev) if b_rev else working_tree\n\n    try:\n        self.tree = a_tree\n        old = _paths_checksums()\n\n        self.tree = b_tree\n        new = _paths_checksums()\n    finally:\n        self.tree = working_tree\n\n    # Compare paths between the old and new tree.\n    # set() efficiently converts dict keys to a set\n    added = sorted(set(new) - set(old))\n    deleted = sorted(set(old) - set(new))\n    modified = sorted(set(old) & set(new))\n\n    ret = {\n        \"added\": [{\"path\": path, \"hash\": new[path]} for path in added],\n        \"deleted\": [{\"path\": path, \"hash\": old[path]} for path in deleted],\n        \"modified\": [\n            {\"path\": path, \"hash\": {\"old\": old[path], \"new\": new[path]}}\n            for path in modified\n            if old[path] != new[path]\n        ],\n    }\n\n    return ret if any(ret.values()) else {}\n","new_file_content":"import os\n\nfrom dvc.repo import locked\nfrom dvc.scm.tree import is_working_tree\n\n\n@locked\ndef diff(self, a_rev=\"HEAD\", b_rev=None):\n    \"\"\"\n    By default, it compares the workspace with the last commit's tree.\n\n    This implementation differs from `git diff` since DVC doesn't have\n    the concept of `index`, but it keeps the same interface, thus,\n    `dvc diff` would be the same as `dvc diff HEAD`.\n    \"\"\"\n\n    def _paths_checksums():\n        \"\"\"\n        A dictionary of checksums addressed by relpaths collected from\n        the current tree outputs.\n\n        To help distinguish between a directory and a file output,\n        the former one will come with a trailing slash in the path:\n\n            directory: \"data/\"\n            file:      \"data\"\n        \"\"\"\n\n        def _to_path(output):\n            return (\n                str(output)\n                if not output.is_dir_checksum\n                else os.path.join(str(output), \"\")\n            )\n\n        on_working_tree = is_working_tree(self.tree)\n\n        def _to_checksum(output):\n            if on_working_tree:\n                return self.cache.local.get_checksum(output.path_info)\n            return output.checksum\n\n        def _exists(output):\n            if on_working_tree:\n                return output.exists\n            return True\n\n        return {\n            _to_path(output): _to_checksum(output)\n            for stage in self.stages\n            for output in stage.outs\n            if _exists(output)\n        }\n\n    if self.scm.no_commits:\n        return {}\n\n    working_tree = self.tree\n    a_tree = self.scm.get_tree(a_rev)\n    b_tree = self.scm.get_tree(b_rev) if b_rev else working_tree\n\n    try:\n        self.tree = a_tree\n        old = _paths_checksums()\n\n        self.tree = b_tree\n        new = _paths_checksums()\n    finally:\n        self.tree = working_tree\n\n    # Compare paths between the old and new tree.\n    # set() efficiently converts dict keys to a set\n    added = sorted(set(new) - set(old))\n    deleted = sorted(set(old) - set(new))\n    modified = sorted(set(old) & set(new))\n\n    ret = {\n        \"added\": [{\"path\": path, \"hash\": new[path]} for path in added],\n        \"deleted\": [{\"path\": path, \"hash\": old[path]} for path in deleted],\n        \"modified\": [\n            {\"path\": path, \"hash\": {\"old\": old[path], \"new\": new[path]}}\n            for path in modified\n            if old[path] != new[path]\n        ],\n    }\n\n    return ret if any(ret.values()) else {}\n","header":{"file":{"path":"dvc/repo/diff.py"},"misc_line":null},"index_line":null,"is_binary_file":false,"binary_line":null,"minus_file":{"path":"a/dvc/repo/diff.py"},"plus_file":{"path":"b/dvc/repo/diff.py"},"hunks":[{"descriptor":{"old_range":{"start":7,"length":7},"new_range":{"start":7,"length":7},"section":""},"line_group":{"all_lines":[{"content":"@locked","type":"context"},{"content":"def diff(self, a_rev=\"HEAD\", b_rev=None):","type":"context"},{"content":"    \"\"\"","type":"context"},{"content":"    By default, it compares the working tree with the last commit's tree.","type":"deleted"},{"content":"    By default, it compares the workspace with the last commit's tree.","type":"added"},{"content":"","type":"context"},{"content":"    This implementation differs from `git diff` since DVC doesn't have","type":"context"},{"content":"    the concept of `index`, but it keeps the same interface, thus,","type":"context"}]},"modified_entities":[],"added_entities":[],"deleted_entities":[]}]},{"old_file_content":"import logging\nimport os\n\nimport yaml\n\nfrom dvc.exceptions import NoMetricsError\nfrom dvc.path_info import PathInfo\nfrom dvc.repo import locked\nfrom dvc.repo.tree import RepoTree\n\nlogger = logging.getLogger(__name__)\n\n\ndef _collect_metrics(repo, targets, recursive):\n    metrics = set()\n\n    for stage in repo.stages:\n        for out in stage.outs:\n            if not out.metric:\n                continue\n\n            metrics.add(out.path_info)\n\n    if not targets:\n        return list(metrics)\n\n    target_infos = [PathInfo(os.path.abspath(target)) for target in targets]\n\n    def _filter(path_info):\n        for info in target_infos:\n            func = path_info.isin_or_eq if recursive else path_info.__eq__\n            if func(info):\n                return True\n        return False\n\n    return list(filter(_filter, metrics))\n\n\ndef _extract_metrics(metrics):\n    if isinstance(metrics, (int, float)):\n        return metrics\n\n    if not isinstance(metrics, dict):\n        return None\n\n    ret = {}\n    for key, val in metrics.items():\n        m = _extract_metrics(val)\n        if m:\n            ret[key] = m\n\n    return ret\n\n\ndef _read_metrics(repo, metrics, rev):\n    tree = RepoTree(repo)\n\n    res = {}\n    for metric in metrics:\n        if not tree.exists(metric):\n            continue\n\n        with tree.open(metric, \"r\") as fobj:\n            try:\n                # NOTE this also supports JSON\n                val = yaml.safe_load(fobj)\n            except yaml.YAMLError:\n                logger.debug(\n                    \"failed to read '%s' on '%s'\", metric, rev, exc_info=True\n                )\n                continue\n\n            val = _extract_metrics(val)\n            if val:\n                res[str(metric)] = val\n\n    return res\n\n\n@locked\ndef show(\n    repo,\n    targets=None,\n    all_branches=False,\n    all_tags=False,\n    recursive=False,\n    revs=None,\n    all_commits=False,\n):\n    res = {}\n\n    for rev in repo.brancher(\n        revs=revs,\n        all_branches=all_branches,\n        all_tags=all_tags,\n        all_commits=all_commits,\n    ):\n        metrics = _collect_metrics(repo, targets, recursive)\n        vals = _read_metrics(repo, metrics, rev)\n\n        if vals:\n            res[rev] = vals\n\n    if not res:\n        raise NoMetricsError()\n\n    # Hide working tree metrics if they are the same as in the active branch\n    try:\n        active_branch = repo.scm.active_branch()\n    except TypeError:\n        pass  # Detached head\n    else:\n        if res.get(\"working tree\") == res.get(active_branch):\n            res.pop(\"working tree\", None)\n\n    return res\n","new_file_content":"import logging\nimport os\n\nimport yaml\n\nfrom dvc.exceptions import NoMetricsError\nfrom dvc.path_info import PathInfo\nfrom dvc.repo import locked\nfrom dvc.repo.tree import RepoTree\n\nlogger = logging.getLogger(__name__)\n\n\ndef _collect_metrics(repo, targets, recursive):\n    metrics = set()\n\n    for stage in repo.stages:\n        for out in stage.outs:\n            if not out.metric:\n                continue\n\n            metrics.add(out.path_info)\n\n    if not targets:\n        return list(metrics)\n\n    target_infos = [PathInfo(os.path.abspath(target)) for target in targets]\n\n    def _filter(path_info):\n        for info in target_infos:\n            func = path_info.isin_or_eq if recursive else path_info.__eq__\n            if func(info):\n                return True\n        return False\n\n    return list(filter(_filter, metrics))\n\n\ndef _extract_metrics(metrics):\n    if isinstance(metrics, (int, float)):\n        return metrics\n\n    if not isinstance(metrics, dict):\n        return None\n\n    ret = {}\n    for key, val in metrics.items():\n        m = _extract_metrics(val)\n        if m:\n            ret[key] = m\n\n    return ret\n\n\ndef _read_metrics(repo, metrics, rev):\n    tree = RepoTree(repo)\n\n    res = {}\n    for metric in metrics:\n        if not tree.exists(metric):\n            continue\n\n        with tree.open(metric, \"r\") as fobj:\n            try:\n                # NOTE this also supports JSON\n                val = yaml.safe_load(fobj)\n            except yaml.YAMLError:\n                logger.debug(\n                    \"failed to read '%s' on '%s'\", metric, rev, exc_info=True\n                )\n                continue\n\n            val = _extract_metrics(val)\n            if val:\n                res[str(metric)] = val\n\n    return res\n\n\n@locked\ndef show(\n    repo,\n    targets=None,\n    all_branches=False,\n    all_tags=False,\n    recursive=False,\n    revs=None,\n    all_commits=False,\n):\n    res = {}\n\n    for rev in repo.brancher(\n        revs=revs,\n        all_branches=all_branches,\n        all_tags=all_tags,\n        all_commits=all_commits,\n    ):\n        metrics = _collect_metrics(repo, targets, recursive)\n        vals = _read_metrics(repo, metrics, rev)\n\n        if vals:\n            res[rev] = vals\n\n    if not res:\n        raise NoMetricsError()\n\n    # Hide workspace metrics if they are the same as in the active branch\n    try:\n        active_branch = repo.scm.active_branch()\n    except TypeError:\n        pass  # Detached head\n    else:\n        if res.get(\"workspace\") == res.get(active_branch):\n            res.pop(\"workspace\", None)\n\n    return res\n","header":{"file":{"path":"dvc/repo/metrics/show.py"},"misc_line":null},"index_line":null,"is_binary_file":false,"binary_line":null,"minus_file":{"path":"a/dvc/repo/metrics/show.py"},"plus_file":{"path":"b/dvc/repo/metrics/show.py"},"hunks":[{"descriptor":{"old_range":{"start":104,"length":13},"new_range":{"start":104,"length":13},"section":"def show("},"line_group":{"all_lines":[{"content":"    if not res:","type":"context"},{"content":"        raise NoMetricsError()","type":"context"},{"content":"","type":"context"},{"content":"    # Hide working tree metrics if they are the same as in the active branch","type":"deleted"},{"content":"    # Hide workspace metrics if they are the same as in the active branch","type":"added"},{"content":"    try:","type":"context"},{"content":"        active_branch = repo.scm.active_branch()","type":"context"},{"content":"    except TypeError:","type":"context"},{"content":"        pass  # Detached head","type":"context"},{"content":"    else:","type":"context"},{"content":"        if res.get(\"working tree\") == res.get(active_branch):","type":"deleted"},{"content":"            res.pop(\"working tree\", None)","type":"deleted"},{"content":"        if res.get(\"workspace\") == res.get(active_branch):","type":"added"},{"content":"            res.pop(\"workspace\", None)","type":"added"},{"content":"","type":"context"},{"content":"    return res","type":"context"}]},"modified_entities":[],"added_entities":[],"deleted_entities":[]}]},{"old_file_content":"import logging\n\nimport yaml\n\nfrom dvc.dependency.param import ParamsDependency\nfrom dvc.exceptions import DvcException\nfrom dvc.path_info import PathInfo\nfrom dvc.repo import locked\n\nlogger = logging.getLogger(__name__)\n\n\nclass NoParamsError(DvcException):\n    pass\n\n\ndef _collect_configs(repo):\n    configs = set()\n    configs.add(PathInfo(repo.root_dir) / ParamsDependency.DEFAULT_PARAMS_FILE)\n    for stage in repo.stages:\n        for dep in stage.deps:\n            if not isinstance(dep, ParamsDependency):\n                continue\n\n            configs.add(dep.path_info)\n    return list(configs)\n\n\ndef _read_params(repo, configs, rev):\n    res = {}\n    for config in configs:\n        if not repo.tree.exists(config):\n            continue\n\n        with repo.tree.open(config, \"r\") as fobj:\n            try:\n                res[str(config)] = yaml.safe_load(fobj)\n            except yaml.YAMLError:\n                logger.debug(\n                    \"failed to read '%s' on '%s'\", config, rev, exc_info=True\n                )\n                continue\n\n    return res\n\n\n@locked\ndef show(repo, revs=None):\n    res = {}\n\n    for branch in repo.brancher(revs=revs):\n        configs = _collect_configs(repo)\n        params = _read_params(repo, configs, branch)\n\n        if params:\n            res[branch] = params\n\n    if not res:\n        raise NoParamsError(\"no parameter configs files in this repository\")\n\n    # Hide working tree params if they are the same as in the active branch\n    try:\n        active_branch = repo.scm.active_branch()\n    except TypeError:\n        pass  # Detached head\n    else:\n        if res.get(\"working tree\") == res.get(active_branch):\n            res.pop(\"working tree\", None)\n\n    return res\n","new_file_content":"import logging\n\nimport yaml\n\nfrom dvc.dependency.param import ParamsDependency\nfrom dvc.exceptions import DvcException\nfrom dvc.path_info import PathInfo\nfrom dvc.repo import locked\n\nlogger = logging.getLogger(__name__)\n\n\nclass NoParamsError(DvcException):\n    pass\n\n\ndef _collect_configs(repo):\n    configs = set()\n    configs.add(PathInfo(repo.root_dir) / ParamsDependency.DEFAULT_PARAMS_FILE)\n    for stage in repo.stages:\n        for dep in stage.deps:\n            if not isinstance(dep, ParamsDependency):\n                continue\n\n            configs.add(dep.path_info)\n    return list(configs)\n\n\ndef _read_params(repo, configs, rev):\n    res = {}\n    for config in configs:\n        if not repo.tree.exists(config):\n            continue\n\n        with repo.tree.open(config, \"r\") as fobj:\n            try:\n                res[str(config)] = yaml.safe_load(fobj)\n            except yaml.YAMLError:\n                logger.debug(\n                    \"failed to read '%s' on '%s'\", config, rev, exc_info=True\n                )\n                continue\n\n    return res\n\n\n@locked\ndef show(repo, revs=None):\n    res = {}\n\n    for branch in repo.brancher(revs=revs):\n        configs = _collect_configs(repo)\n        params = _read_params(repo, configs, branch)\n\n        if params:\n            res[branch] = params\n\n    if not res:\n        raise NoParamsError(\"no parameter configs files in this repository\")\n\n    # Hide workspace params if they are the same as in the active branch\n    try:\n        active_branch = repo.scm.active_branch()\n    except TypeError:\n        pass  # Detached head\n    else:\n        if res.get(\"workspace\") == res.get(active_branch):\n            res.pop(\"workspace\", None)\n\n    return res\n","header":{"file":{"path":"dvc/repo/params/show.py"},"misc_line":null},"index_line":null,"is_binary_file":false,"binary_line":null,"minus_file":{"path":"a/dvc/repo/params/show.py"},"plus_file":{"path":"b/dvc/repo/params/show.py"},"hunks":[{"descriptor":{"old_range":{"start":58,"length":13},"new_range":{"start":58,"length":13},"section":"def show(repo, revs=None):"},"line_group":{"all_lines":[{"content":"    if not res:","type":"context"},{"content":"        raise NoParamsError(\"no parameter configs files in this repository\")","type":"context"},{"content":"","type":"context"},{"content":"    # Hide working tree params if they are the same as in the active branch","type":"deleted"},{"content":"    # Hide workspace params if they are the same as in the active branch","type":"added"},{"content":"    try:","type":"context"},{"content":"        active_branch = repo.scm.active_branch()","type":"context"},{"content":"    except TypeError:","type":"context"},{"content":"        pass  # Detached head","type":"context"},{"content":"    else:","type":"context"},{"content":"        if res.get(\"working tree\") == res.get(active_branch):","type":"deleted"},{"content":"            res.pop(\"working tree\", None)","type":"deleted"},{"content":"        if res.get(\"workspace\") == res.get(active_branch):","type":"added"},{"content":"            res.pop(\"workspace\", None)","type":"added"},{"content":"","type":"context"},{"content":"    return res","type":"context"}]},"modified_entities":[],"added_entities":[],"deleted_entities":[]}]},{"old_file_content":"import csv\nimport io\nimport json\nimport logging\nimport os\nfrom collections import OrderedDict\nfrom copy import copy\n\nimport yaml\nfrom funcy import first\nfrom yaml import SafeLoader\n\nfrom dvc.exceptions import DvcException, PathMissingError\n\nlogger = logging.getLogger(__name__)\n\n\nclass PlotMetricTypeError(DvcException):\n    def __init__(self, file):\n        super().__init__(\n            \"'{}' - file type error\\n\"\n            \"Only JSON, YAML, CSV and TSV formats are supported.\".format(file)\n        )\n\n\nclass PlotDataStructureError(DvcException):\n    def __init__(self):\n        super().__init__(\n            \"Plot data extraction failed. Please see \"\n            \"https://man.dvc.org/plot for supported data formats.\"\n        )\n\n\nclass JsonParsingError(DvcException):\n    def __init__(self, file):\n        super().__init__(\n            \"Failed to infer data structure from '{}'. Did you forget \"\n            \"to specify JSONpath?\".format(file)\n        )\n\n\nclass NoMetricOnRevisionError(DvcException):\n    def __init__(self, path, revision):\n        self.path = path\n        self.revision = revision\n        super().__init__(f\"Could not find '{path}' on revision '{revision}'\")\n\n\nclass NoMetricInHistoryError(DvcException):\n    def __init__(self, path):\n        super().__init__(f\"Could not find '{path}'.\")\n\n\ndef plot_data(filename, revision, content):\n    _, extension = os.path.splitext(filename.lower())\n    if extension == \".json\":\n        return JSONPlotData(filename, revision, content)\n    elif extension == \".csv\":\n        return CSVPlotData(filename, revision, content)\n    elif extension == \".tsv\":\n        return CSVPlotData(filename, revision, content, delimiter=\"\\t\")\n    elif extension == \".yaml\":\n        return YAMLPlotData(filename, revision, content)\n    raise PlotMetricTypeError(filename)\n\n\ndef _filter_fields(data_points, filename, revision, fields=None, **kwargs):\n    if not fields:\n        return data_points\n    assert isinstance(fields, set)\n\n    new_data = []\n    for data_point in data_points:\n        new_dp = copy(data_point)\n\n        keys = set(data_point.keys())\n        if keys & fields != fields:\n            raise DvcException(\n                \"Could not find fields: '{}' for '{}' at '{}'.\".format(\n                    \", \" \"\".join(fields), filename, revision\n                )\n            )\n\n        to_del = keys - fields\n        for key in to_del:\n            del new_dp[key]\n        new_data.append(new_dp)\n    return new_data\n\n\ndef _apply_path(data, path=None, **kwargs):\n    if not path or not isinstance(data, dict):\n        return data\n\n    import jsonpath_ng\n\n    found = jsonpath_ng.parse(path).find(data)\n    first_datum = first(found)\n    if (\n        len(found) == 1\n        and isinstance(first_datum.value, list)\n        and isinstance(first(first_datum.value), dict)\n    ):\n        data_points = first_datum.value\n    elif len(first_datum.path.fields) == 1:\n        field_name = first(first_datum.path.fields)\n        data_points = [{field_name: datum.value} for datum in found]\n    else:\n        raise PlotDataStructureError()\n\n    if not isinstance(data_points, list) or not (\n        isinstance(first(data_points), dict)\n    ):\n        raise PlotDataStructureError()\n\n    return data_points\n\n\ndef _lists(dictionary):\n    for _, value in dictionary.items():\n        if isinstance(value, dict):\n            yield from (_lists(value))\n        elif isinstance(value, list):\n            yield value\n\n\ndef _find_data(data, fields=None, **kwargs):\n    if not isinstance(data, dict):\n        return data\n\n    if not fields:\n        # just look for first list of dicts\n        fields = set()\n\n    for l in _lists(data):\n        if all(isinstance(dp, dict) for dp in l):\n            if set(first(l).keys()) & fields == fields:\n                return l\n    raise PlotDataStructureError()\n\n\ndef _append_index(data_points, append_index=False, **kwargs):\n    if not append_index:\n        return data_points\n\n    if PlotData.INDEX_FIELD in first(data_points).keys():\n        raise DvcException(\n            \"Cannot append index. Field of same name ('{}') found in data. \"\n            \"Use `-x` to specify x axis field.\".format(PlotData.INDEX_FIELD)\n        )\n\n    for index, data_point in enumerate(data_points):\n        data_point[PlotData.INDEX_FIELD] = index\n    return data_points\n\n\ndef _append_revision(data_points, revision, **kwargs):\n    for data_point in data_points:\n        data_point[PlotData.REVISION_FIELD] = revision\n    return data_points\n\n\nclass PlotData:\n    REVISION_FIELD = \"rev\"\n    INDEX_FIELD = \"index\"\n\n    def __init__(self, filename, revision, content, **kwargs):\n        self.filename = filename\n        self.revision = revision\n        self.content = content\n\n    def raw(self, **kwargs):\n        raise NotImplementedError\n\n    def _processors(self):\n        return [_filter_fields, _append_index, _append_revision]\n\n    def to_datapoints(self, **kwargs):\n        data = self.raw(**kwargs)\n\n        for data_proc in self._processors():\n            data = data_proc(\n                data, filename=self.filename, revision=self.revision, **kwargs\n            )\n        return data\n\n\nclass JSONPlotData(PlotData):\n    def raw(self, **kwargs):\n        return json.loads(self.content, object_pairs_hook=OrderedDict)\n\n    def _processors(self):\n        parent_processors = super()._processors()\n        return [_apply_path, _find_data] + parent_processors\n\n\nclass CSVPlotData(PlotData):\n    def __init__(self, filename, revision, content, delimiter=\",\"):\n        super().__init__(filename, revision, content)\n        self.delimiter = delimiter\n\n    def raw(self, csv_header=True, **kwargs):\n        first_row = first(csv.reader(io.StringIO(self.content)))\n\n        if csv_header:\n            reader = csv.DictReader(\n                io.StringIO(self.content), delimiter=self.delimiter,\n            )\n        else:\n            reader = csv.DictReader(\n                io.StringIO(self.content),\n                delimiter=self.delimiter,\n                fieldnames=[str(i) for i in range(len(first_row))],\n            )\n\n        fieldnames = reader.fieldnames\n        data = [row for row in reader]\n\n        return [\n            OrderedDict([(field, data_point[field]) for field in fieldnames])\n            for data_point in data\n        ]\n\n\nclass YAMLPlotData(PlotData):\n    def raw(self, **kwargs):\n        class OrderedLoader(SafeLoader):\n            pass\n\n        def construct_mapping(loader, node):\n            loader.flatten_mapping(node)\n            return OrderedDict(loader.construct_pairs(node))\n\n        OrderedLoader.add_constructor(\n            yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, construct_mapping\n        )\n\n        return yaml.load(self.content, OrderedLoader)\n\n\ndef _load_from_revision(repo, datafile, revision):\n    from dvc.repo.tree import RepoTree\n\n    tree = RepoTree(repo)\n\n    try:\n        with tree.open(datafile) as fobj:\n            datafile_content = fobj.read()\n\n    except (FileNotFoundError, PathMissingError):\n        raise NoMetricOnRevisionError(datafile, revision)\n\n    return plot_data(datafile, revision, datafile_content)\n\n\ndef _load_from_revisions(repo, datafile, revisions):\n    data = []\n    exceptions = []\n\n    for rev in repo.brancher(revs=revisions):\n        if rev == \"working tree\" and rev not in revisions:\n            continue\n\n        try:\n            data.append(_load_from_revision(repo, datafile, rev))\n        except NoMetricOnRevisionError as e:\n            exceptions.append(e)\n        except PlotMetricTypeError:\n            raise\n        except (yaml.error.YAMLError, json.decoder.JSONDecodeError, csv.Error):\n            logger.error(f\"Failed to parse '{datafile}' at '{rev}'.\")\n            raise\n\n    if not data and exceptions:\n        raise NoMetricInHistoryError(datafile)\n    else:\n        for e in exceptions:\n            logger.warning(\n                \"File '{}' was not found at: '{}'. It will not be \"\n                \"plotted.\".format(e.path, e.revision)\n            )\n    return data\n","new_file_content":"import csv\nimport io\nimport json\nimport logging\nimport os\nfrom collections import OrderedDict\nfrom copy import copy\n\nimport yaml\nfrom funcy import first\nfrom yaml import SafeLoader\n\nfrom dvc.exceptions import DvcException, PathMissingError\n\nlogger = logging.getLogger(__name__)\n\n\nclass PlotMetricTypeError(DvcException):\n    def __init__(self, file):\n        super().__init__(\n            \"'{}' - file type error\\n\"\n            \"Only JSON, YAML, CSV and TSV formats are supported.\".format(file)\n        )\n\n\nclass PlotDataStructureError(DvcException):\n    def __init__(self):\n        super().__init__(\n            \"Plot data extraction failed. Please see \"\n            \"https://man.dvc.org/plot for supported data formats.\"\n        )\n\n\nclass JsonParsingError(DvcException):\n    def __init__(self, file):\n        super().__init__(\n            \"Failed to infer data structure from '{}'. Did you forget \"\n            \"to specify JSONpath?\".format(file)\n        )\n\n\nclass NoMetricOnRevisionError(DvcException):\n    def __init__(self, path, revision):\n        self.path = path\n        self.revision = revision\n        super().__init__(f\"Could not find '{path}' on revision '{revision}'\")\n\n\nclass NoMetricInHistoryError(DvcException):\n    def __init__(self, path):\n        super().__init__(f\"Could not find '{path}'.\")\n\n\ndef plot_data(filename, revision, content):\n    _, extension = os.path.splitext(filename.lower())\n    if extension == \".json\":\n        return JSONPlotData(filename, revision, content)\n    elif extension == \".csv\":\n        return CSVPlotData(filename, revision, content)\n    elif extension == \".tsv\":\n        return CSVPlotData(filename, revision, content, delimiter=\"\\t\")\n    elif extension == \".yaml\":\n        return YAMLPlotData(filename, revision, content)\n    raise PlotMetricTypeError(filename)\n\n\ndef _filter_fields(data_points, filename, revision, fields=None, **kwargs):\n    if not fields:\n        return data_points\n    assert isinstance(fields, set)\n\n    new_data = []\n    for data_point in data_points:\n        new_dp = copy(data_point)\n\n        keys = set(data_point.keys())\n        if keys & fields != fields:\n            raise DvcException(\n                \"Could not find fields: '{}' for '{}' at '{}'.\".format(\n                    \", \" \"\".join(fields), filename, revision\n                )\n            )\n\n        to_del = keys - fields\n        for key in to_del:\n            del new_dp[key]\n        new_data.append(new_dp)\n    return new_data\n\n\ndef _apply_path(data, path=None, **kwargs):\n    if not path or not isinstance(data, dict):\n        return data\n\n    import jsonpath_ng\n\n    found = jsonpath_ng.parse(path).find(data)\n    first_datum = first(found)\n    if (\n        len(found) == 1\n        and isinstance(first_datum.value, list)\n        and isinstance(first(first_datum.value), dict)\n    ):\n        data_points = first_datum.value\n    elif len(first_datum.path.fields) == 1:\n        field_name = first(first_datum.path.fields)\n        data_points = [{field_name: datum.value} for datum in found]\n    else:\n        raise PlotDataStructureError()\n\n    if not isinstance(data_points, list) or not (\n        isinstance(first(data_points), dict)\n    ):\n        raise PlotDataStructureError()\n\n    return data_points\n\n\ndef _lists(dictionary):\n    for _, value in dictionary.items():\n        if isinstance(value, dict):\n            yield from (_lists(value))\n        elif isinstance(value, list):\n            yield value\n\n\ndef _find_data(data, fields=None, **kwargs):\n    if not isinstance(data, dict):\n        return data\n\n    if not fields:\n        # just look for first list of dicts\n        fields = set()\n\n    for l in _lists(data):\n        if all(isinstance(dp, dict) for dp in l):\n            if set(first(l).keys()) & fields == fields:\n                return l\n    raise PlotDataStructureError()\n\n\ndef _append_index(data_points, append_index=False, **kwargs):\n    if not append_index:\n        return data_points\n\n    if PlotData.INDEX_FIELD in first(data_points).keys():\n        raise DvcException(\n            \"Cannot append index. Field of same name ('{}') found in data. \"\n            \"Use `-x` to specify x axis field.\".format(PlotData.INDEX_FIELD)\n        )\n\n    for index, data_point in enumerate(data_points):\n        data_point[PlotData.INDEX_FIELD] = index\n    return data_points\n\n\ndef _append_revision(data_points, revision, **kwargs):\n    for data_point in data_points:\n        data_point[PlotData.REVISION_FIELD] = revision\n    return data_points\n\n\nclass PlotData:\n    REVISION_FIELD = \"rev\"\n    INDEX_FIELD = \"index\"\n\n    def __init__(self, filename, revision, content, **kwargs):\n        self.filename = filename\n        self.revision = revision\n        self.content = content\n\n    def raw(self, **kwargs):\n        raise NotImplementedError\n\n    def _processors(self):\n        return [_filter_fields, _append_index, _append_revision]\n\n    def to_datapoints(self, **kwargs):\n        data = self.raw(**kwargs)\n\n        for data_proc in self._processors():\n            data = data_proc(\n                data, filename=self.filename, revision=self.revision, **kwargs\n            )\n        return data\n\n\nclass JSONPlotData(PlotData):\n    def raw(self, **kwargs):\n        return json.loads(self.content, object_pairs_hook=OrderedDict)\n\n    def _processors(self):\n        parent_processors = super()._processors()\n        return [_apply_path, _find_data] + parent_processors\n\n\nclass CSVPlotData(PlotData):\n    def __init__(self, filename, revision, content, delimiter=\",\"):\n        super().__init__(filename, revision, content)\n        self.delimiter = delimiter\n\n    def raw(self, csv_header=True, **kwargs):\n        first_row = first(csv.reader(io.StringIO(self.content)))\n\n        if csv_header:\n            reader = csv.DictReader(\n                io.StringIO(self.content), delimiter=self.delimiter,\n            )\n        else:\n            reader = csv.DictReader(\n                io.StringIO(self.content),\n                delimiter=self.delimiter,\n                fieldnames=[str(i) for i in range(len(first_row))],\n            )\n\n        fieldnames = reader.fieldnames\n        data = [row for row in reader]\n\n        return [\n            OrderedDict([(field, data_point[field]) for field in fieldnames])\n            for data_point in data\n        ]\n\n\nclass YAMLPlotData(PlotData):\n    def raw(self, **kwargs):\n        class OrderedLoader(SafeLoader):\n            pass\n\n        def construct_mapping(loader, node):\n            loader.flatten_mapping(node)\n            return OrderedDict(loader.construct_pairs(node))\n\n        OrderedLoader.add_constructor(\n            yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, construct_mapping\n        )\n\n        return yaml.load(self.content, OrderedLoader)\n\n\ndef _load_from_revision(repo, datafile, revision):\n    from dvc.repo.tree import RepoTree\n\n    tree = RepoTree(repo)\n\n    try:\n        with tree.open(datafile) as fobj:\n            datafile_content = fobj.read()\n\n    except (FileNotFoundError, PathMissingError):\n        raise NoMetricOnRevisionError(datafile, revision)\n\n    return plot_data(datafile, revision, datafile_content)\n\n\ndef _load_from_revisions(repo, datafile, revisions):\n    data = []\n    exceptions = []\n\n    for rev in repo.brancher(revs=revisions):\n        if rev == \"workspace\" and rev not in revisions:\n            continue\n\n        try:\n            data.append(_load_from_revision(repo, datafile, rev))\n        except NoMetricOnRevisionError as e:\n            exceptions.append(e)\n        except PlotMetricTypeError:\n            raise\n        except (yaml.error.YAMLError, json.decoder.JSONDecodeError, csv.Error):\n            logger.error(f\"Failed to parse '{datafile}' at '{rev}'.\")\n            raise\n\n    if not data and exceptions:\n        raise NoMetricInHistoryError(datafile)\n    else:\n        for e in exceptions:\n            logger.warning(\n                \"File '{}' was not found at: '{}'. It will not be \"\n                \"plotted.\".format(e.path, e.revision)\n            )\n    return data\n","header":{"file":{"path":"dvc/repo/plots/data.py"},"misc_line":null},"index_line":null,"is_binary_file":false,"binary_line":null,"minus_file":{"path":"a/dvc/repo/plots/data.py"},"plus_file":{"path":"b/dvc/repo/plots/data.py"},"hunks":[{"descriptor":{"old_range":{"start":258,"length":7},"new_range":{"start":258,"length":7},"section":"def _load_from_revisions(repo, datafile, revisions):"},"line_group":{"all_lines":[{"content":"    exceptions = []","type":"context"},{"content":"","type":"context"},{"content":"    for rev in repo.brancher(revs=revisions):","type":"context"},{"content":"        if rev == \"working tree\" and rev not in revisions:","type":"deleted"},{"content":"        if rev == \"workspace\" and rev not in revisions:","type":"added"},{"content":"            continue","type":"context"},{"content":"","type":"context"},{"content":"        try:","type":"context"}]},"modified_entities":[],"added_entities":[],"deleted_entities":[]}]},{"old_file_content":"def _revisions(revs, is_dirty):\n    revisions = revs or []\n    if len(revisions) <= 1:\n        if len(revisions) == 0 and is_dirty:\n            revisions.append(\"HEAD\")\n        revisions.append(\"working tree\")\n    return revisions\n\n\ndef diff(repo, *args, revs=None, **kwargs):\n    return repo.plots.show(\n        *args, revs=_revisions(revs, repo.scm.is_dirty()), **kwargs\n    )\n","new_file_content":"def _revisions(revs, is_dirty):\n    revisions = revs or []\n    if len(revisions) <= 1:\n        if len(revisions) == 0 and is_dirty:\n            revisions.append(\"HEAD\")\n        revisions.append(\"workspace\")\n    return revisions\n\n\ndef diff(repo, *args, revs=None, **kwargs):\n    return repo.plots.show(\n        *args, revs=_revisions(revs, repo.scm.is_dirty()), **kwargs\n    )\n","header":{"file":{"path":"dvc/repo/plots/diff.py"},"misc_line":null},"index_line":null,"is_binary_file":false,"binary_line":null,"minus_file":{"path":"a/dvc/repo/plots/diff.py"},"plus_file":{"path":"b/dvc/repo/plots/diff.py"},"hunks":[{"descriptor":{"old_range":{"start":3,"length":7},"new_range":{"start":3,"length":7},"section":"def _revisions(revs, is_dirty):"},"line_group":{"all_lines":[{"content":"    if len(revisions) <= 1:","type":"context"},{"content":"        if len(revisions) == 0 and is_dirty:","type":"context"},{"content":"            revisions.append(\"HEAD\")","type":"context"},{"content":"        revisions.append(\"working tree\")","type":"deleted"},{"content":"        revisions.append(\"workspace\")","type":"added"},{"content":"    return revisions","type":"context"},{"content":"","type":"context"},{"content":"","type":"context"}]},"modified_entities":[],"added_entities":[],"deleted_entities":[]}]},{"old_file_content":"import copy\nimport logging\nimport os\n\nfrom funcy import first, last, project\n\nfrom dvc.exceptions import DvcException\nfrom dvc.repo import locked\nfrom dvc.schema import PLOT_PROPS\n\nfrom .data import NoMetricInHistoryError, PlotData\nfrom .template import NoDataForTemplateError, Template\n\nlogger = logging.getLogger(__name__)\n\n\nclass TooManyDataSourcesError(DvcException):\n    def __init__(self, datafile, template_datafiles):\n        super().__init__(\n            \"Unable to infer which of possible data sources: '{}' \"\n            \"should be replaced with '{}'.\".format(\n                \", \".join(template_datafiles), datafile\n            )\n        )\n\n\nclass NoDataOrTemplateProvided(DvcException):\n    def __init__(self):\n        super().__init__(\"Datafile or template is not specified.\")\n\n\nclass TooManyTemplatesError(DvcException):\n    pass\n\n\ndef _evaluate_templatepath(repo, template=None):\n    if not template:\n        return repo.plot_templates.default_template\n\n    if os.path.exists(template):\n        return template\n    return repo.plot_templates.get_template(template)\n\n\n@locked\ndef fill_template(\n    repo, datafile, template_path, revisions, props,\n):\n    # Copy things to not modify passed values\n    props = props.copy()\n    fields = copy.copy(props.get(\"fields\"))\n\n    if props.get(\"x\") and fields:\n        fields.add(props.get(\"x\"))\n\n    if props.get(\"y\") and fields:\n        fields.add(props.get(\"y\"))\n\n    template_datafiles, x_anchor, y_anchor = _parse_template(\n        template_path, datafile\n    )\n    append_index = x_anchor and not props.get(\"x\")\n    if append_index:\n        props[\"x\"] = PlotData.INDEX_FIELD\n\n    template_data = {}\n    for template_datafile in template_datafiles:\n        from .data import _load_from_revisions\n\n        plot_datas = _load_from_revisions(repo, template_datafile, revisions)\n        tmp_data = []\n        for pd in plot_datas:\n            rev_data_points = pd.to_datapoints(\n                fields=fields,\n                path=props.get(\"path\"),\n                csv_header=props.get(\"csv_header\", True),\n                append_index=append_index,\n            )\n\n            if y_anchor and not props.get(\"y\"):\n                props[\"y\"] = _infer_y_field(rev_data_points, props.get(\"x\"))\n            tmp_data.extend(rev_data_points)\n\n        template_data[template_datafile] = tmp_data\n\n    if len(template_data) == 0:\n        raise NoDataForTemplateError(template_path)\n\n    content = Template.fill(\n        template_path, template_data, priority_datafile=datafile, props=props,\n    )\n\n    path = datafile or \",\".join(template_datafiles)\n\n    return path, content\n\n\ndef _infer_y_field(rev_data_points, x_field):\n    all_fields = list(first(rev_data_points).keys())\n    all_fields.remove(PlotData.REVISION_FIELD)\n    if x_field and x_field in all_fields:\n        all_fields.remove(x_field)\n    y_field = last(all_fields)\n    return y_field\n\n\ndef _show(repo, datafile=None, revs=None, props=None):\n    if revs is None:\n        revs = [\"working tree\"]\n\n    if not datafile and not props.get(\"template\"):\n        raise NoDataOrTemplateProvided()\n\n    template_path = _evaluate_templatepath(repo, props.get(\"template\"))\n\n    plot_datafile, plot_content = fill_template(\n        repo, datafile, template_path, revs, props\n    )\n\n    return plot_datafile, plot_content\n\n\ndef _parse_template(template_path, priority_datafile):\n    with open(template_path) as fobj:\n        tempalte_content = fobj.read()\n\n    template_datafiles = Template.parse_data_anchors(tempalte_content)\n    if priority_datafile:\n        if len(template_datafiles) > 1:\n            raise TooManyDataSourcesError(\n                priority_datafile, template_datafiles\n            )\n        template_datafiles = {priority_datafile}\n\n    return (\n        template_datafiles,\n        Template.X_ANCHOR in tempalte_content,\n        Template.Y_ANCHOR in tempalte_content,\n    )\n\n\ndef _collect_plots(repo, targets=None):\n    from dvc.exceptions import OutputNotFoundError\n    from contextlib import suppress\n\n    def _targets_to_outs(targets):\n        for t in targets:\n            with suppress(OutputNotFoundError):\n                (out,) = repo.find_outs_by_path(t)\n                yield out\n\n    if targets:\n        outs = _targets_to_outs(targets)\n    else:\n        outs = (out for stage in repo.stages for out in stage.outs if out.plot)\n\n    return {str(out): _plot_props(out) for out in outs}\n\n\ndef _plot_props(out):\n    if not out.plot:\n        raise DvcException(\n            f\"'{out}' is not a plot. Use `dvc plots modify` to change that.\"\n        )\n    if isinstance(out.plot, list):\n        raise DvcException(\"Multiple plots per data file not supported yet.\")\n    if isinstance(out.plot, bool):\n        return {}\n\n    return project(out.plot, PLOT_PROPS)\n\n\ndef show(repo, targets=None, revs=None, props=None) -> dict:\n    if isinstance(targets, str):\n        targets = [targets]\n    if props is None:\n        props = {}\n\n    # Collect plot data files with associated props\n    plots = {}\n    for rev in repo.brancher(revs=revs):\n        if revs is not None and rev not in revs:\n            continue\n\n        for datafile, file_props in _collect_plots(repo, targets).items():\n            # props from command line overwrite plot props from out definition\n            full_props = {**file_props, **props}\n\n            if datafile in plots:\n                saved_rev, saved_props = plots[datafile]\n                if saved_props != props:\n                    logger.warning(\n                        f\"Inconsistent plot props for '{datafile}' in \"\n                        f\"'{saved_rev}' and '{rev}'. \"\n                        f\"Going to use ones from '{saved_rev}'\"\n                    )\n            else:\n                plots[datafile] = rev, full_props\n\n    if not plots:\n        if targets:\n            raise NoMetricInHistoryError(\", \".join(targets))\n\n        datafile, plot = _show(repo, datafile=None, revs=revs, props=props)\n        return {datafile: plot}\n\n    return {\n        datafile: _show(repo, datafile=datafile, revs=revs, props=props)[1]\n        for datafile, (_, props) in plots.items()\n    }\n","new_file_content":"import copy\nimport logging\nimport os\n\nfrom funcy import first, last, project\n\nfrom dvc.exceptions import DvcException\nfrom dvc.repo import locked\nfrom dvc.schema import PLOT_PROPS\n\nfrom .data import NoMetricInHistoryError, PlotData\nfrom .template import NoDataForTemplateError, Template\n\nlogger = logging.getLogger(__name__)\n\n\nclass TooManyDataSourcesError(DvcException):\n    def __init__(self, datafile, template_datafiles):\n        super().__init__(\n            \"Unable to infer which of possible data sources: '{}' \"\n            \"should be replaced with '{}'.\".format(\n                \", \".join(template_datafiles), datafile\n            )\n        )\n\n\nclass NoDataOrTemplateProvided(DvcException):\n    def __init__(self):\n        super().__init__(\"Datafile or template is not specified.\")\n\n\nclass TooManyTemplatesError(DvcException):\n    pass\n\n\ndef _evaluate_templatepath(repo, template=None):\n    if not template:\n        return repo.plot_templates.default_template\n\n    if os.path.exists(template):\n        return template\n    return repo.plot_templates.get_template(template)\n\n\n@locked\ndef fill_template(\n    repo, datafile, template_path, revisions, props,\n):\n    # Copy things to not modify passed values\n    props = props.copy()\n    fields = copy.copy(props.get(\"fields\"))\n\n    if props.get(\"x\") and fields:\n        fields.add(props.get(\"x\"))\n\n    if props.get(\"y\") and fields:\n        fields.add(props.get(\"y\"))\n\n    template_datafiles, x_anchor, y_anchor = _parse_template(\n        template_path, datafile\n    )\n    append_index = x_anchor and not props.get(\"x\")\n    if append_index:\n        props[\"x\"] = PlotData.INDEX_FIELD\n\n    template_data = {}\n    for template_datafile in template_datafiles:\n        from .data import _load_from_revisions\n\n        plot_datas = _load_from_revisions(repo, template_datafile, revisions)\n        tmp_data = []\n        for pd in plot_datas:\n            rev_data_points = pd.to_datapoints(\n                fields=fields,\n                path=props.get(\"path\"),\n                csv_header=props.get(\"csv_header\", True),\n                append_index=append_index,\n            )\n\n            if y_anchor and not props.get(\"y\"):\n                props[\"y\"] = _infer_y_field(rev_data_points, props.get(\"x\"))\n            tmp_data.extend(rev_data_points)\n\n        template_data[template_datafile] = tmp_data\n\n    if len(template_data) == 0:\n        raise NoDataForTemplateError(template_path)\n\n    content = Template.fill(\n        template_path, template_data, priority_datafile=datafile, props=props,\n    )\n\n    path = datafile or \",\".join(template_datafiles)\n\n    return path, content\n\n\ndef _infer_y_field(rev_data_points, x_field):\n    all_fields = list(first(rev_data_points).keys())\n    all_fields.remove(PlotData.REVISION_FIELD)\n    if x_field and x_field in all_fields:\n        all_fields.remove(x_field)\n    y_field = last(all_fields)\n    return y_field\n\n\ndef _show(repo, datafile=None, revs=None, props=None):\n    if revs is None:\n        revs = [\"workspace\"]\n\n    if not datafile and not props.get(\"template\"):\n        raise NoDataOrTemplateProvided()\n\n    template_path = _evaluate_templatepath(repo, props.get(\"template\"))\n\n    plot_datafile, plot_content = fill_template(\n        repo, datafile, template_path, revs, props\n    )\n\n    return plot_datafile, plot_content\n\n\ndef _parse_template(template_path, priority_datafile):\n    with open(template_path) as fobj:\n        tempalte_content = fobj.read()\n\n    template_datafiles = Template.parse_data_anchors(tempalte_content)\n    if priority_datafile:\n        if len(template_datafiles) > 1:\n            raise TooManyDataSourcesError(\n                priority_datafile, template_datafiles\n            )\n        template_datafiles = {priority_datafile}\n\n    return (\n        template_datafiles,\n        Template.X_ANCHOR in tempalte_content,\n        Template.Y_ANCHOR in tempalte_content,\n    )\n\n\ndef _collect_plots(repo, targets=None):\n    from dvc.exceptions import OutputNotFoundError\n    from contextlib import suppress\n\n    def _targets_to_outs(targets):\n        for t in targets:\n            with suppress(OutputNotFoundError):\n                (out,) = repo.find_outs_by_path(t)\n                yield out\n\n    if targets:\n        outs = _targets_to_outs(targets)\n    else:\n        outs = (out for stage in repo.stages for out in stage.outs if out.plot)\n\n    return {str(out): _plot_props(out) for out in outs}\n\n\ndef _plot_props(out):\n    if not out.plot:\n        raise DvcException(\n            f\"'{out}' is not a plot. Use `dvc plots modify` to change that.\"\n        )\n    if isinstance(out.plot, list):\n        raise DvcException(\"Multiple plots per data file not supported yet.\")\n    if isinstance(out.plot, bool):\n        return {}\n\n    return project(out.plot, PLOT_PROPS)\n\n\ndef show(repo, targets=None, revs=None, props=None) -> dict:\n    if isinstance(targets, str):\n        targets = [targets]\n    if props is None:\n        props = {}\n\n    # Collect plot data files with associated props\n    plots = {}\n    for rev in repo.brancher(revs=revs):\n        if revs is not None and rev not in revs:\n            continue\n\n        for datafile, file_props in _collect_plots(repo, targets).items():\n            # props from command line overwrite plot props from out definition\n            full_props = {**file_props, **props}\n\n            if datafile in plots:\n                saved_rev, saved_props = plots[datafile]\n                if saved_props != props:\n                    logger.warning(\n                        f\"Inconsistent plot props for '{datafile}' in \"\n                        f\"'{saved_rev}' and '{rev}'. \"\n                        f\"Going to use ones from '{saved_rev}'\"\n                    )\n            else:\n                plots[datafile] = rev, full_props\n\n    if not plots:\n        if targets:\n            raise NoMetricInHistoryError(\", \".join(targets))\n\n        datafile, plot = _show(repo, datafile=None, revs=revs, props=props)\n        return {datafile: plot}\n\n    return {\n        datafile: _show(repo, datafile=datafile, revs=revs, props=props)[1]\n        for datafile, (_, props) in plots.items()\n    }\n","header":{"file":{"path":"dvc/repo/plots/show.py"},"misc_line":null},"index_line":null,"is_binary_file":false,"binary_line":null,"minus_file":{"path":"a/dvc/repo/plots/show.py"},"plus_file":{"path":"b/dvc/repo/plots/show.py"},"hunks":[{"descriptor":{"old_range":{"start":106,"length":7},"new_range":{"start":106,"length":7},"section":"def _infer_y_field(rev_data_points, x_field):"},"line_group":{"all_lines":[{"content":"","type":"context"},{"content":"def _show(repo, datafile=None, revs=None, props=None):","type":"context"},{"content":"    if revs is None:","type":"context"},{"content":"        revs = [\"working tree\"]","type":"deleted"},{"content":"        revs = [\"workspace\"]","type":"added"},{"content":"","type":"context"},{"content":"    if not datafile and not props.get(\"template\"):","type":"context"},{"content":"        raise NoDataOrTemplateProvided()","type":"context"},{"content":"","type":"context"}]},"modified_entities":[],"added_entities":[],"deleted_entities":[]}]}],"old_commit_hash":"516d82e41107438756797611bbb15614cd4d989f","new_commit_hash":"63812fa26d34c377c332cd61eeb2461a6da47de1","commit_message":"dvc: rename \"working tree\" to \"workspace\"","commit_date":"2020-05-30T17:01:06+03:00","metadata":{}}
problem_statement: brancher: rename `working tree` -> `workspace`
  Much more natural term. Was also noted by @dmpetrov while working with plots. Needed for cml release.
  
patch: diff --git a/dvc/command/gc.py b/dvc/command/gc.py
  --- a/dvc/command/gc.py
  +++ b/dvc/command/gc.py
  @@ -21,7 +21,7 @@ def run(self):
   
           msg = "This will remove all cache except items used in "
   
  -        msg += "the working tree"
  +        msg += "the workspace"
           if self.args.all_commits:
               msg += " and all git commits"
           elif self.args.all_branches and self.args.all_tags:
  @@ -77,7 +77,7 @@ def add_parser(subparsers, parent_parser):
           "--workspace",
           action="store_true",
           default=False,
  -        help="Keep data files used in the current workspace (working tree).",
  +        help="Keep data files used in the current workspace.",
       )
       gc_parser.add_argument(
           "-a",
  diff --git a/dvc/repo/brancher.py b/dvc/repo/brancher.py
  --- a/dvc/repo/brancher.py
  +++ b/dvc/repo/brancher.py
  @@ -18,7 +18,7 @@ def brancher(  # noqa: E302
           str: the display name for the currently selected tree, it could be:
               - a git revision identifier
               - empty string it there is no branches to iterate over
  -            - "Working Tree" if there are uncommitted changes in the SCM repo
  +            - "workspace" if there are uncommitted changes in the SCM repo
       """
       if not any([revs, all_branches, all_tags, all_commits]):
           yield ""
  @@ -30,10 +30,10 @@ def brancher(  # noqa: E302
       scm = self.scm
   
       self.tree = WorkingTree(self.root_dir)
  -    yield "working tree"
  +    yield "workspace"
   
  -    if revs and "working tree" in revs:
  -        revs.remove("working tree")
  +    if revs and "workspace" in revs:
  +        revs.remove("workspace")
   
       if all_commits:
           revs = scm.list_all_commits()
  diff --git a/dvc/repo/diff.py b/dvc/repo/diff.py
  --- a/dvc/repo/diff.py
  +++ b/dvc/repo/diff.py
  @@ -7,7 +7,7 @@
   @locked
   def diff(self, a_rev="HEAD", b_rev=None):
       """
  -    By default, it compares the working tree with the last commit's tree.
  +    By default, it compares the workspace with the last commit's tree.
   
       This implementation differs from `git diff` since DVC doesn't have
       the concept of `index`, but it keeps the same interface, thus,
  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
  @@ -104,13 +104,13 @@ def show(
       if not res:
           raise NoMetricsError()
   
  -    # Hide working tree metrics if they are the same as in the active branch
  +    # Hide workspace metrics if they are the same as in the active branch
       try:
           active_branch = repo.scm.active_branch()
       except TypeError:
           pass  # Detached head
       else:
  -        if res.get("working tree") == res.get(active_branch):
  -            res.pop("working tree", None)
  +        if res.get("workspace") == res.get(active_branch):
  +            res.pop("workspace", None)
   
       return res
  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
  @@ -58,13 +58,13 @@ def show(repo, revs=None):
       if not res:
           raise NoParamsError("no parameter configs files in this repository")
   
  -    # Hide working tree params if they are the same as in the active branch
  +    # Hide workspace params if they are the same as in the active branch
       try:
           active_branch = repo.scm.active_branch()
       except TypeError:
           pass  # Detached head
       else:
  -        if res.get("working tree") == res.get(active_branch):
  -            res.pop("working tree", None)
  +        if res.get("workspace") == res.get(active_branch):
  +            res.pop("workspace", None)
   
       return res
  diff --git a/dvc/repo/plots/data.py b/dvc/repo/plots/data.py
  --- a/dvc/repo/plots/data.py
  +++ b/dvc/repo/plots/data.py
  @@ -258,7 +258,7 @@ def _load_from_revisions(repo, datafile, revisions):
       exceptions = []
   
       for rev in repo.brancher(revs=revisions):
  -        if rev == "working tree" and rev not in revisions:
  +        if rev == "workspace" and rev not in revisions:
               continue
   
           try:
  diff --git a/dvc/repo/plots/diff.py b/dvc/repo/plots/diff.py
  --- a/dvc/repo/plots/diff.py
  +++ b/dvc/repo/plots/diff.py
  @@ -3,7 +3,7 @@ def _revisions(revs, is_dirty):
       if len(revisions) <= 1:
           if len(revisions) == 0 and is_dirty:
               revisions.append("HEAD")
  -        revisions.append("working tree")
  +        revisions.append("workspace")
       return revisions
   
   
  diff --git a/dvc/repo/plots/show.py b/dvc/repo/plots/show.py
  --- a/dvc/repo/plots/show.py
  +++ b/dvc/repo/plots/show.py
  @@ -106,7 +106,7 @@ def _infer_y_field(rev_data_points, x_field):
   
   def _show(repo, datafile=None, revs=None, props=None):
       if revs is None:
  -        revs = ["working tree"]
  +        revs = ["workspace"]
   
       if not datafile and not props.get("template"):
           raise NoDataOrTemplateProvided()
   
  
file_context: {"dvc/command/gc.py": "import argparse\nimport logging\nimport os\n\nimport dvc.prompt as prompt\nfrom dvc.command.base import CmdBase, append_doc_link\n\nlogger = logging.getLogger(__name__)\n\n\nclass CmdGC(CmdBase):\n    def run(self):\n        from dvc.repo.gc import _raise_error_if_all_disabled\n\n        _raise_error_if_all_disabled(\n            all_branches=self.args.all_branches,\n            all_tags=self.args.all_tags,\n            all_commits=self.args.all_commits,\n            workspace=self.args.workspace,\n        )\n\n        msg = \"This will remove all cache except items used in \"\n\n        msg += \"the working tree\"\n        if self.args.all_commits:\n            msg += \" and all git commits\"\n        elif self.args.all_branches and self.args.all_tags:\n            msg += \" and all git branches and tags\"\n        elif self.args.all_branches:\n            msg += \" and all git branches\"\n        elif self.args.all_tags:\n            msg += \" and all git tags\"\n\n        if self.args.repos:\n            msg += \" of the current and the following repos:\"\n\n            for repo_path in self.args.repos:\n                msg += \"\\n  - %s\" % os.path.abspath(repo_path)\n        else:\n            msg += \" of the current repo.\"\n\n        logger.warning(msg)\n\n        msg = \"Are you sure you want to proceed?\"\n        if not self.args.force and not prompt.confirm(msg):\n            return 1\n\n        self.repo.gc(\n            all_branches=self.args.all_branches,\n            all_tags=self.args.all_tags,\n            all_commits=self.args.all_commits,\n            cloud=self.args.cloud,\n            remote=self.args.remote,\n            force=self.args.force,\n            jobs=self.args.jobs,\n            repos=self.args.repos,\n            workspace=self.args.workspace,\n        )\n        return 0\n\n\ndef add_parser(subparsers, parent_parser):\n    GC_HELP = \"Garbage collect unused objects from cache or remote storage.\"\n    GC_DESCRIPTION = (\n        \"Removes all files in the cache or a remote which are not in\\n\"\n        \"use by the specified Git revisions (defaults to just HEAD).\"\n    )\n    gc_parser = subparsers.add_parser(\n        \"gc\",\n        parents=[parent_parser],\n        description=append_doc_link(GC_DESCRIPTION, \"gc\"),\n        help=GC_HELP,\n        formatter_class=argparse.RawDescriptionHelpFormatter,\n    )\n    gc_parser.add_argument(\n        \"-w\",\n        \"--workspace\",\n        action=\"store_true\",\n        default=False,\n        help=\"Keep data files used in the current workspace (working tree).\",\n    )\n    gc_parser.add_argument(\n        \"-a\",\n        \"--all-branches\",\n        action=\"store_true\",\n        default=False,\n        help=\"Keep data files for the tips of all Git branches.\",\n    )\n    gc_parser.add_argument(\n        \"-T\",\n        \"--all-tags\",\n        action=\"store_true\",\n        default=False,\n        help=\"Keep data files for all Git tags.\",\n    )\n    gc_parser.add_argument(\n        \"--all-commits\",\n        action=\"store_true\",\n        default=False,\n        help=\"Keep data files for all Git commits.\",\n    )\n    gc_parser.add_argument(\n        \"-c\",\n        \"--cloud\",\n        action=\"store_true\",\n        default=False,\n        help=\"Collect garbage in remote repository.\",\n    )\n    gc_parser.add_argument(\n        \"-r\",\n        \"--remote\",\n        help=\"Remote storage to collect garbage in\",\n        metavar=\"<name>\",\n    )\n    gc_parser.add_argument(\n        \"-f\",\n        \"--force\",\n        action=\"store_true\",\n        default=False,\n        help=\"Force garbage collection - automatically agree to all prompts.\",\n    )\n    gc_parser.add_argument(\n        \"-j\",\n        \"--jobs\",\n        type=int,\n        help=\"Number of jobs to run simultaneously.\",\n        metavar=\"<number>\",\n    )\n    gc_parser.add_argument(\n        \"-p\",\n        \"--projects\",\n        dest=\"repos\",\n        type=str,\n        nargs=\"*\",\n        help=\"Keep data files required by these projects \"\n        \"in addition to the current one. \"\n        \"Useful if you share a single cache across repos.\",\n        metavar=\"<paths>\",\n    )\n    gc_parser.set_defaults(func=CmdGC)\n", "dvc/repo/brancher.py": "from funcy import group_by\n\nfrom dvc.scm.tree import WorkingTree\n\n\ndef brancher(  # noqa: E302\n    self, revs=None, all_branches=False, all_tags=False, all_commits=False\n):\n    \"\"\"Generator that iterates over specified revisions.\n\n    Args:\n        revs (list): a list of revisions to iterate over.\n        all_branches (bool): iterate over all available branches.\n        all_commits (bool): iterate over all commits.\n        all_tags (bool): iterate over all available tags.\n\n    Yields:\n        str: the display name for the currently selected tree, it could be:\n            - a git revision identifier\n            - empty string it there is no branches to iterate over\n            - \"Working Tree\" if there are uncommitted changes in the SCM repo\n    \"\"\"\n    if not any([revs, all_branches, all_tags, all_commits]):\n        yield \"\"\n        return\n\n    saved_tree = self.tree\n    revs = revs.copy() if revs else []\n\n    scm = self.scm\n\n    self.tree = WorkingTree(self.root_dir)\n    yield \"working tree\"\n\n    if revs and \"working tree\" in revs:\n        revs.remove(\"working tree\")\n\n    if all_commits:\n        revs = scm.list_all_commits()\n    else:\n        if all_branches:\n            revs.extend(scm.list_branches())\n\n        if all_tags:\n            revs.extend(scm.list_tags())\n\n    try:\n        if revs:\n            for sha, names in group_by(scm.resolve_rev, revs).items():\n                self.tree = scm.get_tree(sha)\n                # ignore revs that don't contain repo root\n                # (i.e. revs from before a subdir=True repo was init'ed)\n                if self.tree.exists(self.root_dir):\n                    yield \", \".join(names)\n    finally:\n        self.tree = saved_tree\n", "dvc/repo/diff.py": "import os\n\nfrom dvc.repo import locked\nfrom dvc.scm.tree import is_working_tree\n\n\n@locked\ndef diff(self, a_rev=\"HEAD\", b_rev=None):\n    \"\"\"\n    By default, it compares the working tree with the last commit's tree.\n\n    This implementation differs from `git diff` since DVC doesn't have\n    the concept of `index`, but it keeps the same interface, thus,\n    `dvc diff` would be the same as `dvc diff HEAD`.\n    \"\"\"\n\n    def _paths_checksums():\n        \"\"\"\n        A dictionary of checksums addressed by relpaths collected from\n        the current tree outputs.\n\n        To help distinguish between a directory and a file output,\n        the former one will come with a trailing slash in the path:\n\n            directory: \"data/\"\n            file:      \"data\"\n        \"\"\"\n\n        def _to_path(output):\n            return (\n                str(output)\n                if not output.is_dir_checksum\n                else os.path.join(str(output), \"\")\n            )\n\n        on_working_tree = is_working_tree(self.tree)\n\n        def _to_checksum(output):\n            if on_working_tree:\n                return self.cache.local.get_checksum(output.path_info)\n            return output.checksum\n\n        def _exists(output):\n            if on_working_tree:\n                return output.exists\n            return True\n\n        return {\n            _to_path(output): _to_checksum(output)\n            for stage in self.stages\n            for output in stage.outs\n            if _exists(output)\n        }\n\n    if self.scm.no_commits:\n        return {}\n\n    working_tree = self.tree\n    a_tree = self.scm.get_tree(a_rev)\n    b_tree = self.scm.get_tree(b_rev) if b_rev else working_tree\n\n    try:\n        self.tree = a_tree\n        old = _paths_checksums()\n\n        self.tree = b_tree\n        new = _paths_checksums()\n    finally:\n        self.tree = working_tree\n\n    # Compare paths between the old and new tree.\n    # set() efficiently converts dict keys to a set\n    added = sorted(set(new) - set(old))\n    deleted = sorted(set(old) - set(new))\n    modified = sorted(set(old) & set(new))\n\n    ret = {\n        \"added\": [{\"path\": path, \"hash\": new[path]} for path in added],\n        \"deleted\": [{\"path\": path, \"hash\": old[path]} for path in deleted],\n        \"modified\": [\n            {\"path\": path, \"hash\": {\"old\": old[path], \"new\": new[path]}}\n            for path in modified\n            if old[path] != new[path]\n        ],\n    }\n\n    return ret if any(ret.values()) else {}\n", "dvc/repo/metrics/show.py": "import logging\nimport os\n\nimport yaml\n\nfrom dvc.exceptions import NoMetricsError\nfrom dvc.path_info import PathInfo\nfrom dvc.repo import locked\nfrom dvc.repo.tree import RepoTree\n\nlogger = logging.getLogger(__name__)\n\n\ndef _collect_metrics(repo, targets, recursive):\n    metrics = set()\n\n    for stage in repo.stages:\n        for out in stage.outs:\n            if not out.metric:\n                continue\n\n            metrics.add(out.path_info)\n\n    if not targets:\n        return list(metrics)\n\n    target_infos = [PathInfo(os.path.abspath(target)) for target in targets]\n\n    def _filter(path_info):\n        for info in target_infos:\n            func = path_info.isin_or_eq if recursive else path_info.__eq__\n            if func(info):\n                return True\n        return False\n\n    return list(filter(_filter, metrics))\n\n\ndef _extract_metrics(metrics):\n    if isinstance(metrics, (int, float)):\n        return metrics\n\n    if not isinstance(metrics, dict):\n        return None\n\n    ret = {}\n    for key, val in metrics.items():\n        m = _extract_metrics(val)\n        if m:\n            ret[key] = m\n\n    return ret\n\n\ndef _read_metrics(repo, metrics, rev):\n    tree = RepoTree(repo)\n\n    res = {}\n    for metric in metrics:\n        if not tree.exists(metric):\n            continue\n\n        with tree.open(metric, \"r\") as fobj:\n            try:\n                # NOTE this also supports JSON\n                val = yaml.safe_load(fobj)\n            except yaml.YAMLError:\n                logger.debug(\n                    \"failed to read '%s' on '%s'\", metric, rev, exc_info=True\n                )\n                continue\n\n            val = _extract_metrics(val)\n            if val:\n                res[str(metric)] = val\n\n    return res\n\n\n@locked\ndef show(\n    repo,\n    targets=None,\n    all_branches=False,\n    all_tags=False,\n    recursive=False,\n    revs=None,\n    all_commits=False,\n):\n    res = {}\n\n    for rev in repo.brancher(\n        revs=revs,\n        all_branches=all_branches,\n        all_tags=all_tags,\n        all_commits=all_commits,\n    ):\n        metrics = _collect_metrics(repo, targets, recursive)\n        vals = _read_metrics(repo, metrics, rev)\n\n        if vals:\n            res[rev] = vals\n\n    if not res:\n        raise NoMetricsError()\n\n    # Hide working tree metrics if they are the same as in the active branch\n    try:\n        active_branch = repo.scm.active_branch()\n    except TypeError:\n        pass  # Detached head\n    else:\n        if res.get(\"working tree\") == res.get(active_branch):\n            res.pop(\"working tree\", None)\n\n    return res\n", "dvc/repo/params/show.py": "import logging\n\nimport yaml\n\nfrom dvc.dependency.param import ParamsDependency\nfrom dvc.exceptions import DvcException\nfrom dvc.path_info import PathInfo\nfrom dvc.repo import locked\n\nlogger = logging.getLogger(__name__)\n\n\nclass NoParamsError(DvcException):\n    pass\n\n\ndef _collect_configs(repo):\n    configs = set()\n    configs.add(PathInfo(repo.root_dir) / ParamsDependency.DEFAULT_PARAMS_FILE)\n    for stage in repo.stages:\n        for dep in stage.deps:\n            if not isinstance(dep, ParamsDependency):\n                continue\n\n            configs.add(dep.path_info)\n    return list(configs)\n\n\ndef _read_params(repo, configs, rev):\n    res = {}\n    for config in configs:\n        if not repo.tree.exists(config):\n            continue\n\n        with repo.tree.open(config, \"r\") as fobj:\n            try:\n                res[str(config)] = yaml.safe_load(fobj)\n            except yaml.YAMLError:\n                logger.debug(\n                    \"failed to read '%s' on '%s'\", config, rev, exc_info=True\n                )\n                continue\n\n    return res\n\n\n@locked\ndef show(repo, revs=None):\n    res = {}\n\n    for branch in repo.brancher(revs=revs):\n        configs = _collect_configs(repo)\n        params = _read_params(repo, configs, branch)\n\n        if params:\n            res[branch] = params\n\n    if not res:\n        raise NoParamsError(\"no parameter configs files in this repository\")\n\n    # Hide working tree params if they are the same as in the active branch\n    try:\n        active_branch = repo.scm.active_branch()\n    except TypeError:\n        pass  # Detached head\n    else:\n        if res.get(\"working tree\") == res.get(active_branch):\n            res.pop(\"working tree\", None)\n\n    return res\n", "dvc/repo/plots/data.py": "import csv\nimport io\nimport json\nimport logging\nimport os\nfrom collections import OrderedDict\nfrom copy import copy\n\nimport yaml\nfrom funcy import first\nfrom yaml import SafeLoader\n\nfrom dvc.exceptions import DvcException, PathMissingError\n\nlogger = logging.getLogger(__name__)\n\n\nclass PlotMetricTypeError(DvcException):\n    def __init__(self, file):\n        super().__init__(\n            \"'{}' - file type error\\n\"\n            \"Only JSON, YAML, CSV and TSV formats are supported.\".format(file)\n        )\n\n\nclass PlotDataStructureError(DvcException):\n    def __init__(self):\n        super().__init__(\n            \"Plot data extraction failed. Please see \"\n            \"https://man.dvc.org/plot for supported data formats.\"\n        )\n\n\nclass JsonParsingError(DvcException):\n    def __init__(self, file):\n        super().__init__(\n            \"Failed to infer data structure from '{}'. Did you forget \"\n            \"to specify JSONpath?\".format(file)\n        )\n\n\nclass NoMetricOnRevisionError(DvcException):\n    def __init__(self, path, revision):\n        self.path = path\n        self.revision = revision\n        super().__init__(f\"Could not find '{path}' on revision '{revision}'\")\n\n\nclass NoMetricInHistoryError(DvcException):\n    def __init__(self, path):\n        super().__init__(f\"Could not find '{path}'.\")\n\n\ndef plot_data(filename, revision, content):\n    _, extension = os.path.splitext(filename.lower())\n    if extension == \".json\":\n        return JSONPlotData(filename, revision, content)\n    elif extension == \".csv\":\n        return CSVPlotData(filename, revision, content)\n    elif extension == \".tsv\":\n        return CSVPlotData(filename, revision, content, delimiter=\"\\t\")\n    elif extension == \".yaml\":\n        return YAMLPlotData(filename, revision, content)\n    raise PlotMetricTypeError(filename)\n\n\ndef _filter_fields(data_points, filename, revision, fields=None, **kwargs):\n    if not fields:\n        return data_points\n    assert isinstance(fields, set)\n\n    new_data = []\n    for data_point in data_points:\n        new_dp = copy(data_point)\n\n        keys = set(data_point.keys())\n        if keys & fields != fields:\n            raise DvcException(\n                \"Could not find fields: '{}' for '{}' at '{}'.\".format(\n                    \", \" \"\".join(fields), filename, revision\n                )\n            )\n\n        to_del = keys - fields\n        for key in to_del:\n            del new_dp[key]\n        new_data.append(new_dp)\n    return new_data\n\n\ndef _apply_path(data, path=None, **kwargs):\n    if not path or not isinstance(data, dict):\n        return data\n\n    import jsonpath_ng\n\n    found = jsonpath_ng.parse(path).find(data)\n    first_datum = first(found)\n    if (\n        len(found) == 1\n        and isinstance(first_datum.value, list)\n        and isinstance(first(first_datum.value), dict)\n    ):\n        data_points = first_datum.value\n    elif len(first_datum.path.fields) == 1:\n        field_name = first(first_datum.path.fields)\n        data_points = [{field_name: datum.value} for datum in found]\n    else:\n        raise PlotDataStructureError()\n\n    if not isinstance(data_points, list) or not (\n        isinstance(first(data_points), dict)\n    ):\n        raise PlotDataStructureError()\n\n    return data_points\n\n\ndef _lists(dictionary):\n    for _, value in dictionary.items():\n        if isinstance(value, dict):\n            yield from (_lists(value))\n        elif isinstance(value, list):\n            yield value\n\n\ndef _find_data(data, fields=None, **kwargs):\n    if not isinstance(data, dict):\n        return data\n\n    if not fields:\n        # just look for first list of dicts\n        fields = set()\n\n    for l in _lists(data):\n        if all(isinstance(dp, dict) for dp in l):\n            if set(first(l).keys()) & fields == fields:\n                return l\n    raise PlotDataStructureError()\n\n\ndef _append_index(data_points, append_index=False, **kwargs):\n    if not append_index:\n        return data_points\n\n    if PlotData.INDEX_FIELD in first(data_points).keys():\n        raise DvcException(\n            \"Cannot append index. Field of same name ('{}') found in data. \"\n            \"Use `-x` to specify x axis field.\".format(PlotData.INDEX_FIELD)\n        )\n\n    for index, data_point in enumerate(data_points):\n        data_point[PlotData.INDEX_FIELD] = index\n    return data_points\n\n\ndef _append_revision(data_points, revision, **kwargs):\n    for data_point in data_points:\n        data_point[PlotData.REVISION_FIELD] = revision\n    return data_points\n\n\nclass PlotData:\n    REVISION_FIELD = \"rev\"\n    INDEX_FIELD = \"index\"\n\n    def __init__(self, filename, revision, content, **kwargs):\n        self.filename = filename\n        self.revision = revision\n        self.content = content\n\n    def raw(self, **kwargs):\n        raise NotImplementedError\n\n    def _processors(self):\n        return [_filter_fields, _append_index, _append_revision]\n\n    def to_datapoints(self, **kwargs):\n        data = self.raw(**kwargs)\n\n        for data_proc in self._processors():\n            data = data_proc(\n                data, filename=self.filename, revision=self.revision, **kwargs\n            )\n        return data\n\n\nclass JSONPlotData(PlotData):\n    def raw(self, **kwargs):\n        return json.loads(self.content, object_pairs_hook=OrderedDict)\n\n    def _processors(self):\n        parent_processors = super()._processors()\n        return [_apply_path, _find_data] + parent_processors\n\n\nclass CSVPlotData(PlotData):\n    def __init__(self, filename, revision, content, delimiter=\",\"):\n        super().__init__(filename, revision, content)\n        self.delimiter = delimiter\n\n    def raw(self, csv_header=True, **kwargs):\n        first_row = first(csv.reader(io.StringIO(self.content)))\n\n        if csv_header:\n            reader = csv.DictReader(\n                io.StringIO(self.content), delimiter=self.delimiter,\n            )\n        else:\n            reader = csv.DictReader(\n                io.StringIO(self.content),\n                delimiter=self.delimiter,\n                fieldnames=[str(i) for i in range(len(first_row))],\n            )\n\n        fieldnames = reader.fieldnames\n        data = [row for row in reader]\n\n        return [\n            OrderedDict([(field, data_point[field]) for field in fieldnames])\n            for data_point in data\n        ]\n\n\nclass YAMLPlotData(PlotData):\n    def raw(self, **kwargs):\n        class OrderedLoader(SafeLoader):\n            pass\n\n        def construct_mapping(loader, node):\n            loader.flatten_mapping(node)\n            return OrderedDict(loader.construct_pairs(node))\n\n        OrderedLoader.add_constructor(\n            yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, construct_mapping\n        )\n\n        return yaml.load(self.content, OrderedLoader)\n\n\ndef _load_from_revision(repo, datafile, revision):\n    from dvc.repo.tree import RepoTree\n\n    tree = RepoTree(repo)\n\n    try:\n        with tree.open(datafile) as fobj:\n            datafile_content = fobj.read()\n\n    except (FileNotFoundError, PathMissingError):\n        raise NoMetricOnRevisionError(datafile, revision)\n\n    return plot_data(datafile, revision, datafile_content)\n\n\ndef _load_from_revisions(repo, datafile, revisions):\n    data = []\n    exceptions = []\n\n    for rev in repo.brancher(revs=revisions):\n        if rev == \"working tree\" and rev not in revisions:\n            continue\n\n        try:\n            data.append(_load_from_revision(repo, datafile, rev))\n        except NoMetricOnRevisionError as e:\n            exceptions.append(e)\n        except PlotMetricTypeError:\n            raise\n        except (yaml.error.YAMLError, json.decoder.JSONDecodeError, csv.Error):\n            logger.error(f\"Failed to parse '{datafile}' at '{rev}'.\")\n            raise\n\n    if not data and exceptions:\n        raise NoMetricInHistoryError(datafile)\n    else:\n        for e in exceptions:\n            logger.warning(\n                \"File '{}' was not found at: '{}'. It will not be \"\n                \"plotted.\".format(e.path, e.revision)\n            )\n    return data\n", "dvc/repo/plots/diff.py": "def _revisions(revs, is_dirty):\n    revisions = revs or []\n    if len(revisions) <= 1:\n        if len(revisions) == 0 and is_dirty:\n            revisions.append(\"HEAD\")\n        revisions.append(\"working tree\")\n    return revisions\n\n\ndef diff(repo, *args, revs=None, **kwargs):\n    return repo.plots.show(\n        *args, revs=_revisions(revs, repo.scm.is_dirty()), **kwargs\n    )\n", "dvc/repo/plots/show.py": "import copy\nimport logging\nimport os\n\nfrom funcy import first, last, project\n\nfrom dvc.exceptions import DvcException\nfrom dvc.repo import locked\nfrom dvc.schema import PLOT_PROPS\n\nfrom .data import NoMetricInHistoryError, PlotData\nfrom .template import NoDataForTemplateError, Template\n\nlogger = logging.getLogger(__name__)\n\n\nclass TooManyDataSourcesError(DvcException):\n    def __init__(self, datafile, template_datafiles):\n        super().__init__(\n            \"Unable to infer which of possible data sources: '{}' \"\n            \"should be replaced with '{}'.\".format(\n                \", \".join(template_datafiles), datafile\n            )\n        )\n\n\nclass NoDataOrTemplateProvided(DvcException):\n    def __init__(self):\n        super().__init__(\"Datafile or template is not specified.\")\n\n\nclass TooManyTemplatesError(DvcException):\n    pass\n\n\ndef _evaluate_templatepath(repo, template=None):\n    if not template:\n        return repo.plot_templates.default_template\n\n    if os.path.exists(template):\n        return template\n    return repo.plot_templates.get_template(template)\n\n\n@locked\ndef fill_template(\n    repo, datafile, template_path, revisions, props,\n):\n    # Copy things to not modify passed values\n    props = props.copy()\n    fields = copy.copy(props.get(\"fields\"))\n\n    if props.get(\"x\") and fields:\n        fields.add(props.get(\"x\"))\n\n    if props.get(\"y\") and fields:\n        fields.add(props.get(\"y\"))\n\n    template_datafiles, x_anchor, y_anchor = _parse_template(\n        template_path, datafile\n    )\n    append_index = x_anchor and not props.get(\"x\")\n    if append_index:\n        props[\"x\"] = PlotData.INDEX_FIELD\n\n    template_data = {}\n    for template_datafile in template_datafiles:\n        from .data import _load_from_revisions\n\n        plot_datas = _load_from_revisions(repo, template_datafile, revisions)\n        tmp_data = []\n        for pd in plot_datas:\n            rev_data_points = pd.to_datapoints(\n                fields=fields,\n                path=props.get(\"path\"),\n                csv_header=props.get(\"csv_header\", True),\n                append_index=append_index,\n            )\n\n            if y_anchor and not props.get(\"y\"):\n                props[\"y\"] = _infer_y_field(rev_data_points, props.get(\"x\"))\n            tmp_data.extend(rev_data_points)\n\n        template_data[template_datafile] = tmp_data\n\n    if len(template_data) == 0:\n        raise NoDataForTemplateError(template_path)\n\n    content = Template.fill(\n        template_path, template_data, priority_datafile=datafile, props=props,\n    )\n\n    path = datafile or \",\".join(template_datafiles)\n\n    return path, content\n\n\ndef _infer_y_field(rev_data_points, x_field):\n    all_fields = list(first(rev_data_points).keys())\n    all_fields.remove(PlotData.REVISION_FIELD)\n    if x_field and x_field in all_fields:\n        all_fields.remove(x_field)\n    y_field = last(all_fields)\n    return y_field\n\n\ndef _show(repo, datafile=None, revs=None, props=None):\n    if revs is None:\n        revs = [\"working tree\"]\n\n    if not datafile and not props.get(\"template\"):\n        raise NoDataOrTemplateProvided()\n\n    template_path = _evaluate_templatepath(repo, props.get(\"template\"))\n\n    plot_datafile, plot_content = fill_template(\n        repo, datafile, template_path, revs, props\n    )\n\n    return plot_datafile, plot_content\n\n\ndef _parse_template(template_path, priority_datafile):\n    with open(template_path) as fobj:\n        tempalte_content = fobj.read()\n\n    template_datafiles = Template.parse_data_anchors(tempalte_content)\n    if priority_datafile:\n        if len(template_datafiles) > 1:\n            raise TooManyDataSourcesError(\n                priority_datafile, template_datafiles\n            )\n        template_datafiles = {priority_datafile}\n\n    return (\n        template_datafiles,\n        Template.X_ANCHOR in tempalte_content,\n        Template.Y_ANCHOR in tempalte_content,\n    )\n\n\ndef _collect_plots(repo, targets=None):\n    from dvc.exceptions import OutputNotFoundError\n    from contextlib import suppress\n\n    def _targets_to_outs(targets):\n        for t in targets:\n            with suppress(OutputNotFoundError):\n                (out,) = repo.find_outs_by_path(t)\n                yield out\n\n    if targets:\n        outs = _targets_to_outs(targets)\n    else:\n        outs = (out for stage in repo.stages for out in stage.outs if out.plot)\n\n    return {str(out): _plot_props(out) for out in outs}\n\n\ndef _plot_props(out):\n    if not out.plot:\n        raise DvcException(\n            f\"'{out}' is not a plot. Use `dvc plots modify` to change that.\"\n        )\n    if isinstance(out.plot, list):\n        raise DvcException(\"Multiple plots per data file not supported yet.\")\n    if isinstance(out.plot, bool):\n        return {}\n\n    return project(out.plot, PLOT_PROPS)\n\n\ndef show(repo, targets=None, revs=None, props=None) -> dict:\n    if isinstance(targets, str):\n        targets = [targets]\n    if props is None:\n        props = {}\n\n    # Collect plot data files with associated props\n    plots = {}\n    for rev in repo.brancher(revs=revs):\n        if revs is not None and rev not in revs:\n            continue\n\n        for datafile, file_props in _collect_plots(repo, targets).items():\n            # props from command line overwrite plot props from out definition\n            full_props = {**file_props, **props}\n\n            if datafile in plots:\n                saved_rev, saved_props = plots[datafile]\n                if saved_props != props:\n                    logger.warning(\n                        f\"Inconsistent plot props for '{datafile}' in \"\n                        f\"'{saved_rev}' and '{rev}'. \"\n                        f\"Going to use ones from '{saved_rev}'\"\n                    )\n            else:\n                plots[datafile] = rev, full_props\n\n    if not plots:\n        if targets:\n            raise NoMetricInHistoryError(\", \".join(targets))\n\n        datafile, plot = _show(repo, datafile=None, revs=revs, props=props)\n        return {datafile: plot}\n\n    return {\n        datafile: _show(repo, datafile=datafile, revs=revs, props=props)[1]\n        for datafile, (_, props) in plots.items()\n    }\n"}
filelevel_patches: {"dvc/command/gc.py": "@@ -21,7 +21,7 @@ def run(self):\n \n         msg = \"This will remove all cache except items used in \"\n \n-        msg += \"the working tree\"\n+        msg += \"the workspace\"\n         if self.args.all_commits:\n             msg += \" and all git commits\"\n         elif self.args.all_branches and self.args.all_tags:\n@@ -77,7 +77,7 @@ def add_parser(subparsers, parent_parser):\n         \"--workspace\",\n         action=\"store_true\",\n         default=False,\n-        help=\"Keep data files used in the current workspace (working tree).\",\n+        help=\"Keep data files used in the current workspace.\",\n     )\n     gc_parser.add_argument(\n         \"-a\",", "dvc/repo/brancher.py": "@@ -18,7 +18,7 @@ def brancher(  # noqa: E302\n         str: the display name for the currently selected tree, it could be:\n             - a git revision identifier\n             - empty string it there is no branches to iterate over\n-            - \"Working Tree\" if there are uncommitted changes in the SCM repo\n+            - \"workspace\" if there are uncommitted changes in the SCM repo\n     \"\"\"\n     if not any([revs, all_branches, all_tags, all_commits]):\n         yield \"\"\n@@ -30,10 +30,10 @@ def brancher(  # noqa: E302\n     scm = self.scm\n \n     self.tree = WorkingTree(self.root_dir)\n-    yield \"working tree\"\n+    yield \"workspace\"\n \n-    if revs and \"working tree\" in revs:\n-        revs.remove(\"working tree\")\n+    if revs and \"workspace\" in revs:\n+        revs.remove(\"workspace\")\n \n     if all_commits:\n         revs = scm.list_all_commits()", "dvc/repo/diff.py": "@@ -7,7 +7,7 @@\n @locked\n def diff(self, a_rev=\"HEAD\", b_rev=None):\n     \"\"\"\n-    By default, it compares the working tree with the last commit's tree.\n+    By default, it compares the workspace with the last commit's tree.\n \n     This implementation differs from `git diff` since DVC doesn't have\n     the concept of `index`, but it keeps the same interface, thus,", "dvc/repo/metrics/show.py": "@@ -104,13 +104,13 @@ def show(\n     if not res:\n         raise NoMetricsError()\n \n-    # Hide working tree metrics if they are the same as in the active branch\n+    # Hide workspace metrics if they are the same as in the active branch\n     try:\n         active_branch = repo.scm.active_branch()\n     except TypeError:\n         pass  # Detached head\n     else:\n-        if res.get(\"working tree\") == res.get(active_branch):\n-            res.pop(\"working tree\", None)\n+        if res.get(\"workspace\") == res.get(active_branch):\n+            res.pop(\"workspace\", None)\n \n     return res", "dvc/repo/params/show.py": "@@ -58,13 +58,13 @@ def show(repo, revs=None):\n     if not res:\n         raise NoParamsError(\"no parameter configs files in this repository\")\n \n-    # Hide working tree params if they are the same as in the active branch\n+    # Hide workspace params if they are the same as in the active branch\n     try:\n         active_branch = repo.scm.active_branch()\n     except TypeError:\n         pass  # Detached head\n     else:\n-        if res.get(\"working tree\") == res.get(active_branch):\n-            res.pop(\"working tree\", None)\n+        if res.get(\"workspace\") == res.get(active_branch):\n+            res.pop(\"workspace\", None)\n \n     return res", "dvc/repo/plots/data.py": "@@ -258,7 +258,7 @@ def _load_from_revisions(repo, datafile, revisions):\n     exceptions = []\n \n     for rev in repo.brancher(revs=revisions):\n-        if rev == \"working tree\" and rev not in revisions:\n+        if rev == \"workspace\" and rev not in revisions:\n             continue\n \n         try:", "dvc/repo/plots/diff.py": "@@ -3,7 +3,7 @@ def _revisions(revs, is_dirty):\n     if len(revisions) <= 1:\n         if len(revisions) == 0 and is_dirty:\n             revisions.append(\"HEAD\")\n-        revisions.append(\"working tree\")\n+        revisions.append(\"workspace\")\n     return revisions", "dvc/repo/plots/show.py": "@@ -106,7 +106,7 @@ def _infer_y_field(rev_data_points, x_field):\n \n def _show(repo, datafile=None, revs=None, props=None):\n     if revs is None:\n-        revs = [\"working tree\"]\n+        revs = [\"workspace\"]\n \n     if not datafile and not props.get(\"template\"):\n         raise NoDataOrTemplateProvided()"}
messages.content: 
  We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  brancher: rename `working tree` -> `workspace`
  Much more natural term. Was also noted by @dmpetrov while working with plots. Needed for cml release.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  
  --- BEGIN FILE ---
  ```
  ### dvc/repo/plots/show.py
  import copy
  import logging
  import os
  
  from funcy import first, last, project
  
  from dvc.exceptions import DvcException
  from dvc.repo import locked
  from dvc.schema import PLOT_PROPS
  
  from .data import NoMetricInHistoryError, PlotData
  from .template import NoDataForTemplateError, Template
  
  logger = logging.getLogger(__name__)
  
  
  class TooManyDataSourcesError(DvcException):
      def __init__(self, datafile, template_datafiles):
          super().__init__(
              "Unable to infer which of possible data sources: '{}' "
              "should be replaced with '{}'.".format(
                  ", ".join(template_datafiles), datafile
              )
          )
  
  
  class NoDataOrTemplateProvided(DvcException):
      def __init__(self):
          super().__init__("Datafile or template is not specified.")
  
  
  class TooManyTemplatesError(DvcException):
      pass
  
  
  def _evaluate_templatepath(repo, template=None):
      if not template:
          return repo.plot_templates.default_template
  
      if os.path.exists(template):
          return template
      return repo.plot_templates.get_template(template)
  
  
  @locked
  def fill_template(
      repo, datafile, template_path, revisions, props,
  ):
      # Copy things to not modify passed values
      props = props.copy()
      fields = copy.copy(props.get("fields"))
  
      if props.get("x") and fields:
          fields.add(props.get("x"))
  
      if props.get("y") and fields:
          fields.add(props.get("y"))
  
      template_datafiles, x_anchor, y_anchor = _parse_template(
          template_path, datafile
      )
      append_index = x_anchor and not props.get("x")
      if append_index:
          props["x"] = PlotData.INDEX_FIELD
  
      template_data = {}
      for template_datafile in template_datafiles:
          from .data import _load_from_revisions
  
          plot_datas = _load_from_revisions(repo, template_datafile, revisions)
          tmp_data = []
          for pd in plot_datas:
              rev_data_points = pd.to_datapoints(
                  fields=fields,
                  path=props.get("path"),
                  csv_header=props.get("csv_header", True),
                  append_index=append_index,
              )
  
              if y_anchor and not props.get("y"):
                  props["y"] = _infer_y_field(rev_data_points, props.get("x"))
              tmp_data.extend(rev_data_points)
  
          template_data[template_datafile] = tmp_data
  
      if len(template_data) == 0:
          raise NoDataForTemplateError(template_path)
  
      content = Template.fill(
          template_path, template_data, priority_datafile=datafile, props=props,
      )
  
      path = datafile or ",".join(template_datafiles)
  
      return path, content
  
  
  def _infer_y_field(rev_data_points, x_field):
      all_fields = list(first(rev_data_points).keys())
      all_fields.remove(PlotData.REVISION_FIELD)
      if x_field and x_field in all_fields:
          all_fields.remove(x_field)
      y_field = last(all_fields)
      return y_field
  
  
  def _show(repo, datafile=None, revs=None, props=None):
      if revs is None:
          revs = ["working tree"]
  
      if not datafile and not props.get("template"):
          raise NoDataOrTemplateProvided()
  
      template_path = _evaluate_templatepath(repo, props.get("template"))
  
      plot_datafile, plot_content = fill_template(
          repo, datafile, template_path, revs, props
      )
  
      return plot_datafile, plot_content
  
  
  def _parse_template(template_path, priority_datafile):
      with open(template_path) as fobj:
          tempalte_content = fobj.read()
  
      template_datafiles = Template.parse_data_anchors(tempalte_content)
      if priority_datafile:
          if len(template_datafiles) > 1:
              raise TooManyDataSourcesError(
                  priority_datafile, template_datafiles
              )
          template_datafiles = {priority_datafile}
  
      return (
          template_datafiles,
          Template.X_ANCHOR in tempalte_content,
          Template.Y_ANCHOR in tempalte_content,
      )
  
  
  def _collect_plots(repo, targets=None):
      from dvc.exceptions import OutputNotFoundError
      from contextlib import suppress
  
      def _targets_to_outs(targets):
          for t in targets:
              with suppress(OutputNotFoundError):
                  (out,) = repo.find_outs_by_path(t)
                  yield out
  
      if targets:
          outs = _targets_to_outs(targets)
      else:
          outs = (out for stage in repo.stages for out in stage.outs if out.plot)
  
      return {str(out): _plot_props(out) for out in outs}
  
  
  def _plot_props(out):
      if not out.plot:
          raise DvcException(
              f"'{out}' is not a plot. Use `dvc plots modify` to change that."
          )
      if isinstance(out.plot, list):
          raise DvcException("Multiple plots per data file not supported yet.")
      if isinstance(out.plot, bool):
          return {}
  
      return project(out.plot, PLOT_PROPS)
  
  
  def show(repo, targets=None, revs=None, props=None) -> dict:
      if isinstance(targets, str):
          targets = [targets]
      if props is None:
          props = {}
  
      # Collect plot data files with associated props
      plots = {}
      for rev in repo.brancher(revs=revs):
          if revs is not None and rev not in revs:
              continue
  
          for datafile, file_props in _collect_plots(repo, targets).items():
              # props from command line overwrite plot props from out definition
              full_props = {**file_props, **props}
  
              if datafile in plots:
                  saved_rev, saved_props = plots[datafile]
                  if saved_props != props:
                      logger.warning(
                          f"Inconsistent plot props for '{datafile}' in "
                          f"'{saved_rev}' and '{rev}'. "
                          f"Going to use ones from '{saved_rev}'"
                      )
              else:
                  plots[datafile] = rev, full_props
  
      if not plots:
          if targets:
              raise NoMetricInHistoryError(", ".join(targets))
  
          datafile, plot = _show(repo, datafile=None, revs=revs, props=props)
          return {datafile: plot}
  
      return {
          datafile: _show(repo, datafile=datafile, revs=revs, props=props)[1]
          for datafile, (_, props) in plots.items()
      }
  
  
  ### dvc/repo/brancher.py
  from funcy import group_by
  
  from dvc.scm.tree import WorkingTree
  
  
  def brancher(  # noqa: E302
      self, revs=None, all_branches=False, all_tags=False, all_commits=False
  ):
      """Generator that iterates over specified revisions.
  
      Args:
          revs (list): a list of revisions to iterate over.
          all_branches (bool): iterate over all available branches.
          all_commits (bool): iterate over all commits.
          all_tags (bool): iterate over all available tags.
  
      Yields:
          str: the display name for the currently selected tree, it could be:
              - a git revision identifier
              - empty string it there is no branches to iterate over
              - "Working Tree" if there are uncommitted changes in the SCM repo
      """
      if not any([revs, all_branches, all_tags, all_commits]):
          yield ""
          return
  
      saved_tree = self.tree
      revs = revs.copy() if revs else []
  
      scm = self.scm
  
      self.tree = WorkingTree(self.root_dir)
      yield "working tree"
  
      if revs and "working tree" in revs:
          revs.remove("working tree")
  
      if all_commits:
          revs = scm.list_all_commits()
      else:
          if all_branches:
              revs.extend(scm.list_branches())
  
          if all_tags:
              revs.extend(scm.list_tags())
  
      try:
          if revs:
              for sha, names in group_by(scm.resolve_rev, revs).items():
                  self.tree = scm.get_tree(sha)
                  # ignore revs that don't contain repo root
                  # (i.e. revs from before a subdir=True repo was init'ed)
                  if self.tree.exists(self.root_dir):
                      yield ", ".join(names)
      finally:
          self.tree = saved_tree
  
  
  ### dvc/command/gc.py
  import argparse
  import logging
  import os
  
  import dvc.prompt as prompt
  from dvc.command.base import CmdBase, append_doc_link
  
  logger = logging.getLogger(__name__)
  
  
  class CmdGC(CmdBase):
      def run(self):
          from dvc.repo.gc import _raise_error_if_all_disabled
  
          _raise_error_if_all_disabled(
              all_branches=self.args.all_branches,
              all_tags=self.args.all_tags,
              all_commits=self.args.all_commits,
              workspace=self.args.workspace,
          )
  
          msg = "This will remove all cache except items used in "
  
          msg += "the working tree"
          if self.args.all_commits:
              msg += " and all git commits"
          elif self.args.all_branches and self.args.all_tags:
              msg += " and all git branches and tags"
          elif self.args.all_branches:
              msg += " and all git branches"
          elif self.args.all_tags:
              msg += " and all git tags"
  
          if self.args.repos:
              msg += " of the current and the following repos:"
  
              for repo_path in self.args.repos:
                  msg += "\n  - %s" % os.path.abspath(repo_path)
          else:
              msg += " of the current repo."
  
          logger.warning(msg)
  
          msg = "Are you sure you want to proceed?"
          if not self.args.force and not prompt.confirm(msg):
              return 1
  
          self.repo.gc(
              all_branches=self.args.all_branches,
              all_tags=self.args.all_tags,
              all_commits=self.args.all_commits,
              cloud=self.args.cloud,
              remote=self.args.remote,
              force=self.args.force,
              jobs=self.args.jobs,
              repos=self.args.repos,
              workspace=self.args.workspace,
          )
          return 0
  
  
  def add_parser(subparsers, parent_parser):
      GC_HELP = "Garbage collect unused objects from cache or remote storage."
      GC_DESCRIPTION = (
          "Removes all files in the cache or a remote which are not in\n"
          "use by the specified Git revisions (defaults to just HEAD)."
      )
      gc_parser = subparsers.add_parser(
          "gc",
          parents=[parent_parser],
          description=append_doc_link(GC_DESCRIPTION, "gc"),
          help=GC_HELP,
          formatter_class=argparse.RawDescriptionHelpFormatter,
      )
      gc_parser.add_argument(
          "-w",
          "--workspace",
          action="store_true",
          default=False,
          help="Keep data files used in the current workspace (working tree).",
      )
      gc_parser.add_argument(
          "-a",
          "--all-branches",
          action="store_true",
          default=False,
          help="Keep data files for the tips of all Git branches.",
      )
      gc_parser.add_argument(
          "-T",
          "--all-tags",
          action="store_true",
          default=False,
          help="Keep data files for all Git tags.",
      )
      gc_parser.add_argument(
          "--all-commits",
          action="store_true",
          default=False,
          help="Keep data files for all Git commits.",
      )
      gc_parser.add_argument(
          "-c",
          "--cloud",
          action="store_true",
          default=False,
          help="Collect garbage in remote repository.",
      )
      gc_parser.add_argument(
          "-r",
          "--remote",
          help="Remote storage to collect garbage in",
          metavar="<name>",
      )
      gc_parser.add_argument(
          "-f",
          "--force",
          action="store_true",
          default=False,
          help="Force garbage collection - automatically agree to all prompts.",
      )
      gc_parser.add_argument(
          "-j",
          "--jobs",
          type=int,
          help="Number of jobs to run simultaneously.",
          metavar="<number>",
      )
      gc_parser.add_argument(
          "-p",
          "--projects",
          dest="repos",
          type=str,
          nargs="*",
          help="Keep data files required by these projects "
          "in addition to the current one. "
          "Useful if you share a single cache across repos.",
          metavar="<paths>",
      )
      gc_parser.set_defaults(func=CmdGC)
  
  
  ### dvc/repo/plots/data.py
  import csv
  import io
  import json
  import logging
  import os
  from collections import OrderedDict
  from copy import copy
  
  import yaml
  from funcy import first
  from yaml import SafeLoader
  
  from dvc.exceptions import DvcException, PathMissingError
  
  logger = logging.getLogger(__name__)
  
  
  class PlotMetricTypeError(DvcException):
      def __init__(self, file):
          super().__init__(
              "'{}' - file type error\n"
              "Only JSON, YAML, CSV and TSV formats are supported.".format(file)
          )
  
  
  class PlotDataStructureError(DvcException):
      def __init__(self):
          super().__init__(
              "Plot data extraction failed. Please see "
              "https://man.dvc.org/plot for supported data formats."
          )
  
  
  class JsonParsingError(DvcException):
      def __init__(self, file):
          super().__init__(
              "Failed to infer data structure from '{}'. Did you forget "
              "to specify JSONpath?".format(file)
          )
  
  
  class NoMetricOnRevisionError(DvcException):
      def __init__(self, path, revision):
          self.path = path
          self.revision = revision
          super().__init__(f"Could not find '{path}' on revision '{revision}'")
  
  
  class NoMetricInHistoryError(DvcException):
      def __init__(self, path):
          super().__init__(f"Could not find '{path}'.")
  
  
  def plot_data(filename, revision, content):
      _, extension = os.path.splitext(filename.lower())
      if extension == ".json":
          return JSONPlotData(filename, revision, content)
      elif extension == ".csv":
          return CSVPlotData(filename, revision, content)
      elif extension == ".tsv":
          return CSVPlotData(filename, revision, content, delimiter="\t")
      elif extension == ".yaml":
          return YAMLPlotData(filename, revision, content)
      raise PlotMetricTypeError(filename)
  
  
  def _filter_fields(data_points, filename, revision, fields=None, **kwargs):
      if not fields:
          return data_points
      assert isinstance(fields, set)
  
      new_data = []
      for data_point in data_points:
          new_dp = copy(data_point)
  
          keys = set(data_point.keys())
          if keys & fields != fields:
              raise DvcException(
                  "Could not find fields: '{}' for '{}' at '{}'.".format(
                      ", " "".join(fields), filename, revision
                  )
              )
  
          to_del = keys - fields
          for key in to_del:
              del new_dp[key]
          new_data.append(new_dp)
      return new_data
  
  
  def _apply_path(data, path=None, **kwargs):
      if not path or not isinstance(data, dict):
          return data
  
      import jsonpath_ng
  
      found = jsonpath_ng.parse(path).find(data)
      first_datum = first(found)
      if (
          len(found) == 1
          and isinstance(first_datum.value, list)
          and isinstance(first(first_datum.value), dict)
      ):
          data_points = first_datum.value
      elif len(first_datum.path.fields) == 1:
          field_name = first(first_datum.path.fields)
          data_points = [{field_name: datum.value} for datum in found]
      else:
          raise PlotDataStructureError()
  
      if not isinstance(data_points, list) or not (
          isinstance(first(data_points), dict)
      ):
          raise PlotDataStructureError()
  
      return data_points
  
  
  def _lists(dictionary):
      for _, value in dictionary.items():
          if isinstance(value, dict):
              yield from (_lists(value))
          elif isinstance(value, list):
              yield value
  
  
  def _find_data(data, fields=None, **kwargs):
      if not isinstance(data, dict):
          return data
  
      if not fields:
          # just look for first list of dicts
          fields = set()
  
      for l in _lists(data):
          if all(isinstance(dp, dict) for dp in l):
              if set(first(l).keys()) & fields == fields:
                  return l
      raise PlotDataStructureError()
  
  
  def _append_index(data_points, append_index=False, **kwargs):
      if not append_index:
          return data_points
  
      if PlotData.INDEX_FIELD in first(data_points).keys():
          raise DvcException(
              "Cannot append index. Field of same name ('{}') found in data. "
              "Use `-x` to specify x axis field.".format(PlotData.INDEX_FIELD)
          )
  
      for index, data_point in enumerate(data_points):
          data_point[PlotData.INDEX_FIELD] = index
      return data_points
  
  
  def _append_revision(data_points, revision, **kwargs):
      for data_point in data_points:
          data_point[PlotData.REVISION_FIELD] = revision
      return data_points
  
  
  class PlotData:
      REVISION_FIELD = "rev"
      INDEX_FIELD = "index"
  
      def __init__(self, filename, revision, content, **kwargs):
          self.filename = filename
          self.revision = revision
          self.content = content
  
      def raw(self, **kwargs):
          raise NotImplementedError
  
      def _processors(self):
          return [_filter_fields, _append_index, _append_revision]
  
      def to_datapoints(self, **kwargs):
          data = self.raw(**kwargs)
  
          for data_proc in self._processors():
              data = data_proc(
                  data, filename=self.filename, revision=self.revision, **kwargs
              )
          return data
  
  
  class JSONPlotData(PlotData):
      def raw(self, **kwargs):
          return json.loads(self.content, object_pairs_hook=OrderedDict)
  
      def _processors(self):
          parent_processors = super()._processors()
          return [_apply_path, _find_data] + parent_processors
  
  
  class CSVPlotData(PlotData):
      def __init__(self, filename, revision, content, delimiter=","):
          super().__init__(filename, revision, content)
          self.delimiter = delimiter
  
      def raw(self, csv_header=True, **kwargs):
          first_row = first(csv.reader(io.StringIO(self.content)))
  
          if csv_header:
              reader = csv.DictReader(
                  io.StringIO(self.content), delimiter=self.delimiter,
              )
          else:
              reader = csv.DictReader(
                  io.StringIO(self.content),
                  delimiter=self.delimiter,
                  fieldnames=[str(i) for i in range(len(first_row))],
              )
  
          fieldnames = reader.fieldnames
          data = [row for row in reader]
  
          return [
              OrderedDict([(field, data_point[field]) for field in fieldnames])
              for data_point in data
          ]
  
  
  class YAMLPlotData(PlotData):
      def raw(self, **kwargs):
          class OrderedLoader(SafeLoader):
              pass
  
          def construct_mapping(loader, node):
              loader.flatten_mapping(node)
              return OrderedDict(loader.construct_pairs(node))
  
          OrderedLoader.add_constructor(
              yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, construct_mapping
          )
  
          return yaml.load(self.content, OrderedLoader)
  
  
  def _load_from_revision(repo, datafile, revision):
      from dvc.repo.tree import RepoTree
  
      tree = RepoTree(repo)
  
      try:
          with tree.open(datafile) as fobj:
              datafile_content = fobj.read()
  
      except (FileNotFoundError, PathMissingError):
          raise NoMetricOnRevisionError(datafile, revision)
  
      return plot_data(datafile, revision, datafile_content)
  
  
  def _load_from_revisions(repo, datafile, revisions):
      data = []
      exceptions = []
  
      for rev in repo.brancher(revs=revisions):
          if rev == "working tree" and rev not in revisions:
              continue
  
          try:
              data.append(_load_from_revision(repo, datafile, rev))
          except NoMetricOnRevisionError as e:
              exceptions.append(e)
          except PlotMetricTypeError:
              raise
          except (yaml.error.YAMLError, json.decoder.JSONDecodeError, csv.Error):
              logger.error(f"Failed to parse '{datafile}' at '{rev}'.")
              raise
  
      if not data and exceptions:
          raise NoMetricInHistoryError(datafile)
      else:
          for e in exceptions:
              logger.warning(
                  "File '{}' was not found at: '{}'. It will not be "
                  "plotted.".format(e.path, e.revision)
              )
      return data
  
  
  ### dvc/repo/diff.py
  import os
  
  from dvc.repo import locked
  from dvc.scm.tree import is_working_tree
  
  
  @locked
  def diff(self, a_rev="HEAD", b_rev=None):
      """
      By default, it compares the working tree with the last commit's tree.
  
      This implementation differs from `git diff` since DVC doesn't have
      the concept of `index`, but it keeps the same interface, thus,
      `dvc diff` would be the same as `dvc diff HEAD`.
      """
  
      def _paths_checksums():
          """
          A dictionary of checksums addressed by relpaths collected from
          the current tree outputs.
  
          To help distinguish between a directory and a file output,
          the former one will come with a trailing slash in the path:
  
              directory: "data/"
              file:      "data"
          """
  
          def _to_path(output):
              return (
                  str(output)
                  if not output.is_dir_checksum
                  else os.path.join(str(output), "")
              )
  
          on_working_tree = is_working_tree(self.tree)
  
          def _to_checksum(output):
              if on_working_tree:
                  return self.cache.local.get_checksum(output.path_info)
              return output.checksum
  
          def _exists(output):
              if on_working_tree:
                  return output.exists
              return True
  
          return {
              _to_path(output): _to_checksum(output)
              for stage in self.stages
              for output in stage.outs
              if _exists(output)
          }
  
      if self.scm.no_commits:
          return {}
  
      working_tree = self.tree
      a_tree = self.scm.get_tree(a_rev)
      b_tree = self.scm.get_tree(b_rev) if b_rev else working_tree
  
      try:
          self.tree = a_tree
          old = _paths_checksums()
  
          self.tree = b_tree
          new = _paths_checksums()
      finally:
          self.tree = working_tree
  
      # Compare paths between the old and new tree.
      # set() efficiently converts dict keys to a set
      added = sorted(set(new) - set(old))
      deleted = sorted(set(old) - set(new))
      modified = sorted(set(old) & set(new))
  
      ret = {
          "added": [{"path": path, "hash": new[path]} for path in added],
          "deleted": [{"path": path, "hash": old[path]} for path in deleted],
          "modified": [
              {"path": path, "hash": {"old": old[path], "new": new[path]}}
              for path in modified
              if old[path] != new[path]
          ],
      }
  
      return ret if any(ret.values()) else {}
  
  
  ### dvc/repo/params/show.py
  import logging
  
  import yaml
  
  from dvc.dependency.param import ParamsDependency
  from dvc.exceptions import DvcException
  from dvc.path_info import PathInfo
  from dvc.repo import locked
  
  logger = logging.getLogger(__name__)
  
  
  class NoParamsError(DvcException):
      pass
  
  
  def _collect_configs(repo):
      configs = set()
      configs.add(PathInfo(repo.root_dir) / ParamsDependency.DEFAULT_PARAMS_FILE)
      for stage in repo.stages:
          for dep in stage.deps:
              if not isinstance(dep, ParamsDependency):
                  continue
  
              configs.add(dep.path_info)
      return list(configs)
  
  
  def _read_params(repo, configs, rev):
      res = {}
      for config in configs:
          if not repo.tree.exists(config):
              continue
  
          with repo.tree.open(config, "r") as fobj:
              try:
                  res[str(config)] = yaml.safe_load(fobj)
              except yaml.YAMLError:
                  logger.debug(
                      "failed to read '%s' on '%s'", config, rev, exc_info=True
                  )
                  continue
  
      return res
  
  
  @locked
  def show(repo, revs=None):
      res = {}
  
      for branch in repo.brancher(revs=revs):
          configs = _collect_configs(repo)
          params = _read_params(repo, configs, branch)
  
          if params:
              res[branch] = params
  
      if not res:
          raise NoParamsError("no parameter configs files in this repository")
  
      # Hide working tree params if they are the same as in the active branch
      try:
          active_branch = repo.scm.active_branch()
      except TypeError:
          pass  # Detached head
      else:
          if res.get("working tree") == res.get(active_branch):
              res.pop("working tree", None)
  
      return res
  
  
  ### dvc/repo/plots/diff.py
  def _revisions(revs, is_dirty):
      revisions = revs or []
      if len(revisions) <= 1:
          if len(revisions) == 0 and is_dirty:
              revisions.append("HEAD")
          revisions.append("working tree")
      return revisions
  
  
  def diff(repo, *args, revs=None, **kwargs):
      return repo.plots.show(
          *args, revs=_revisions(revs, repo.scm.is_dirty()), **kwargs
      )
  
  
  ### dvc/repo/metrics/show.py
  import logging
  import os
  
  import yaml
  
  from dvc.exceptions import NoMetricsError
  from dvc.path_info import PathInfo
  from dvc.repo import locked
  from dvc.repo.tree import RepoTree
  
  logger = logging.getLogger(__name__)
  
  
  def _collect_metrics(repo, targets, recursive):
      metrics = set()
  
      for stage in repo.stages:
          for out in stage.outs:
              if not out.metric:
                  continue
  
              metrics.add(out.path_info)
  
      if not targets:
          return list(metrics)
  
      target_infos = [PathInfo(os.path.abspath(target)) for target in targets]
  
      def _filter(path_info):
          for info in target_infos:
              func = path_info.isin_or_eq if recursive else path_info.__eq__
              if func(info):
                  return True
          return False
  
      return list(filter(_filter, metrics))
  
  
  def _extract_metrics(metrics):
      if isinstance(metrics, (int, float)):
          return metrics
  
      if not isinstance(metrics, dict):
          return None
  
      ret = {}
      for key, val in metrics.items():
          m = _extract_metrics(val)
          if m:
              ret[key] = m
  
      return ret
  
  
  def _read_metrics(repo, metrics, rev):
      tree = RepoTree(repo)
  
      res = {}
      for metric in metrics:
          if not tree.exists(metric):
              continue
  
          with tree.open(metric, "r") as fobj:
              try:
                  # NOTE this also supports JSON
                  val = yaml.safe_load(fobj)
              except yaml.YAMLError:
                  logger.debug(
                      "failed to read '%s' on '%s'", metric, rev, exc_info=True
                  )
                  continue
  
              val = _extract_metrics(val)
              if val:
                  res[str(metric)] = val
  
      return res
  
  
  @locked
  def show(
      repo,
      targets=None,
      all_branches=False,
      all_tags=False,
      recursive=False,
      revs=None,
      all_commits=False,
  ):
      res = {}
  
      for rev in repo.brancher(
          revs=revs,
          all_branches=all_branches,
          all_tags=all_tags,
          all_commits=all_commits,
      ):
          metrics = _collect_metrics(repo, targets, recursive)
          vals = _read_metrics(repo, metrics, rev)
  
          if vals:
              res[rev] = vals
  
      if not res:
          raise NoMetricsError()
  
      # Hide working tree metrics if they are the same as in the active branch
      try:
          active_branch = repo.scm.active_branch()
      except TypeError:
          pass  # Detached head
      else:
          if res.get("working tree") == res.get(active_branch):
              res.pop("working tree", None)
  
      return res
  
  ```
  --- END FILE ---
  
  Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.
  
  Every *SEARCH/REPLACE* edit must use this format:
  1. The file path
  2. The start of search block: <<<<<<< SEARCH
  3. A contiguous chunk of lines to search for in the existing source code
  4. The dividing line: =======
  5. The lines to replace into the source code
  6. The end of the replace block: >>>>>>> REPLACE
  
  Here is an example:
  
  ```python
  ### mathweb/flask/app.py
  <<<<<<< SEARCH
  from flask import Flask
  =======
  import math
  from flask import Flask
  >>>>>>> REPLACE
  ```
  
  Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
  Wrap the *SEARCH/REPLACE* edit in blocks ```python...```.
  
messages.role: user
verification_info: {"file_context": {"dvc/command/gc.py": "import argparse\nimport logging\nimport os\n\nimport dvc.prompt as prompt\nfrom dvc.command.base import CmdBase, append_doc_link\n\nlogger = logging.getLogger(__name__)\n\n\nclass CmdGC(CmdBase):\n    def run(self):\n        from dvc.repo.gc import _raise_error_if_all_disabled\n\n        _raise_error_if_all_disabled(\n            all_branches=self.args.all_branches,\n            all_tags=self.args.all_tags,\n            all_commits=self.args.all_commits,\n            workspace=self.args.workspace,\n        )\n\n        msg = \"This will remove all cache except items used in \"\n\n        msg += \"the working tree\"\n        if self.args.all_commits:\n            msg += \" and all git commits\"\n        elif self.args.all_branches and self.args.all_tags:\n            msg += \" and all git branches and tags\"\n        elif self.args.all_branches:\n            msg += \" and all git branches\"\n        elif self.args.all_tags:\n            msg += \" and all git tags\"\n\n        if self.args.repos:\n            msg += \" of the current and the following repos:\"\n\n            for repo_path in self.args.repos:\n                msg += \"\\n  - %s\" % os.path.abspath(repo_path)\n        else:\n            msg += \" of the current repo.\"\n\n        logger.warning(msg)\n\n        msg = \"Are you sure you want to proceed?\"\n        if not self.args.force and not prompt.confirm(msg):\n            return 1\n\n        self.repo.gc(\n            all_branches=self.args.all_branches,\n            all_tags=self.args.all_tags,\n            all_commits=self.args.all_commits,\n            cloud=self.args.cloud,\n            remote=self.args.remote,\n            force=self.args.force,\n            jobs=self.args.jobs,\n            repos=self.args.repos,\n            workspace=self.args.workspace,\n        )\n        return 0\n\n\ndef add_parser(subparsers, parent_parser):\n    GC_HELP = \"Garbage collect unused objects from cache or remote storage.\"\n    GC_DESCRIPTION = (\n        \"Removes all files in the cache or a remote which are not in\\n\"\n        \"use by the specified Git revisions (defaults to just HEAD).\"\n    )\n    gc_parser = subparsers.add_parser(\n        \"gc\",\n        parents=[parent_parser],\n        description=append_doc_link(GC_DESCRIPTION, \"gc\"),\n        help=GC_HELP,\n        formatter_class=argparse.RawDescriptionHelpFormatter,\n    )\n    gc_parser.add_argument(\n        \"-w\",\n        \"--workspace\",\n        action=\"store_true\",\n        default=False,\n        help=\"Keep data files used in the current workspace (working tree).\",\n    )\n    gc_parser.add_argument(\n        \"-a\",\n        \"--all-branches\",\n        action=\"store_true\",\n        default=False,\n        help=\"Keep data files for the tips of all Git branches.\",\n    )\n    gc_parser.add_argument(\n        \"-T\",\n        \"--all-tags\",\n        action=\"store_true\",\n        default=False,\n        help=\"Keep data files for all Git tags.\",\n    )\n    gc_parser.add_argument(\n        \"--all-commits\",\n        action=\"store_true\",\n        default=False,\n        help=\"Keep data files for all Git commits.\",\n    )\n    gc_parser.add_argument(\n        \"-c\",\n        \"--cloud\",\n        action=\"store_true\",\n        default=False,\n        help=\"Collect garbage in remote repository.\",\n    )\n    gc_parser.add_argument(\n        \"-r\",\n        \"--remote\",\n        help=\"Remote storage to collect garbage in\",\n        metavar=\"<name>\",\n    )\n    gc_parser.add_argument(\n        \"-f\",\n        \"--force\",\n        action=\"store_true\",\n        default=False,\n        help=\"Force garbage collection - automatically agree to all prompts.\",\n    )\n    gc_parser.add_argument(\n        \"-j\",\n        \"--jobs\",\n        type=int,\n        help=\"Number of jobs to run simultaneously.\",\n        metavar=\"<number>\",\n    )\n    gc_parser.add_argument(\n        \"-p\",\n        \"--projects\",\n        dest=\"repos\",\n        type=str,\n        nargs=\"*\",\n        help=\"Keep data files required by these projects \"\n        \"in addition to the current one. \"\n        \"Useful if you share a single cache across repos.\",\n        metavar=\"<paths>\",\n    )\n    gc_parser.set_defaults(func=CmdGC)\n", "dvc/repo/brancher.py": "from funcy import group_by\n\nfrom dvc.scm.tree import WorkingTree\n\n\ndef brancher(  # noqa: E302\n    self, revs=None, all_branches=False, all_tags=False, all_commits=False\n):\n    \"\"\"Generator that iterates over specified revisions.\n\n    Args:\n        revs (list): a list of revisions to iterate over.\n        all_branches (bool): iterate over all available branches.\n        all_commits (bool): iterate over all commits.\n        all_tags (bool): iterate over all available tags.\n\n    Yields:\n        str: the display name for the currently selected tree, it could be:\n            - a git revision identifier\n            - empty string it there is no branches to iterate over\n            - \"Working Tree\" if there are uncommitted changes in the SCM repo\n    \"\"\"\n    if not any([revs, all_branches, all_tags, all_commits]):\n        yield \"\"\n        return\n\n    saved_tree = self.tree\n    revs = revs.copy() if revs else []\n\n    scm = self.scm\n\n    self.tree = WorkingTree(self.root_dir)\n    yield \"working tree\"\n\n    if revs and \"working tree\" in revs:\n        revs.remove(\"working tree\")\n\n    if all_commits:\n        revs = scm.list_all_commits()\n    else:\n        if all_branches:\n            revs.extend(scm.list_branches())\n\n        if all_tags:\n            revs.extend(scm.list_tags())\n\n    try:\n        if revs:\n            for sha, names in group_by(scm.resolve_rev, revs).items():\n                self.tree = scm.get_tree(sha)\n                # ignore revs that don't contain repo root\n                # (i.e. revs from before a subdir=True repo was init'ed)\n                if self.tree.exists(self.root_dir):\n                    yield \", \".join(names)\n    finally:\n        self.tree = saved_tree\n", "dvc/repo/diff.py": "import os\n\nfrom dvc.repo import locked\nfrom dvc.scm.tree import is_working_tree\n\n\n@locked\ndef diff(self, a_rev=\"HEAD\", b_rev=None):\n    \"\"\"\n    By default, it compares the working tree with the last commit's tree.\n\n    This implementation differs from `git diff` since DVC doesn't have\n    the concept of `index`, but it keeps the same interface, thus,\n    `dvc diff` would be the same as `dvc diff HEAD`.\n    \"\"\"\n\n    def _paths_checksums():\n        \"\"\"\n        A dictionary of checksums addressed by relpaths collected from\n        the current tree outputs.\n\n        To help distinguish between a directory and a file output,\n        the former one will come with a trailing slash in the path:\n\n            directory: \"data/\"\n            file:      \"data\"\n        \"\"\"\n\n        def _to_path(output):\n            return (\n                str(output)\n                if not output.is_dir_checksum\n                else os.path.join(str(output), \"\")\n            )\n\n        on_working_tree = is_working_tree(self.tree)\n\n        def _to_checksum(output):\n            if on_working_tree:\n                return self.cache.local.get_checksum(output.path_info)\n            return output.checksum\n\n        def _exists(output):\n            if on_working_tree:\n                return output.exists\n            return True\n\n        return {\n            _to_path(output): _to_checksum(output)\n            for stage in self.stages\n            for output in stage.outs\n            if _exists(output)\n        }\n\n    if self.scm.no_commits:\n        return {}\n\n    working_tree = self.tree\n    a_tree = self.scm.get_tree(a_rev)\n    b_tree = self.scm.get_tree(b_rev) if b_rev else working_tree\n\n    try:\n        self.tree = a_tree\n        old = _paths_checksums()\n\n        self.tree = b_tree\n        new = _paths_checksums()\n    finally:\n        self.tree = working_tree\n\n    # Compare paths between the old and new tree.\n    # set() efficiently converts dict keys to a set\n    added = sorted(set(new) - set(old))\n    deleted = sorted(set(old) - set(new))\n    modified = sorted(set(old) & set(new))\n\n    ret = {\n        \"added\": [{\"path\": path, \"hash\": new[path]} for path in added],\n        \"deleted\": [{\"path\": path, \"hash\": old[path]} for path in deleted],\n        \"modified\": [\n            {\"path\": path, \"hash\": {\"old\": old[path], \"new\": new[path]}}\n            for path in modified\n            if old[path] != new[path]\n        ],\n    }\n\n    return ret if any(ret.values()) else {}\n", "dvc/repo/metrics/show.py": "import logging\nimport os\n\nimport yaml\n\nfrom dvc.exceptions import NoMetricsError\nfrom dvc.path_info import PathInfo\nfrom dvc.repo import locked\nfrom dvc.repo.tree import RepoTree\n\nlogger = logging.getLogger(__name__)\n\n\ndef _collect_metrics(repo, targets, recursive):\n    metrics = set()\n\n    for stage in repo.stages:\n        for out in stage.outs:\n            if not out.metric:\n                continue\n\n            metrics.add(out.path_info)\n\n    if not targets:\n        return list(metrics)\n\n    target_infos = [PathInfo(os.path.abspath(target)) for target in targets]\n\n    def _filter(path_info):\n        for info in target_infos:\n            func = path_info.isin_or_eq if recursive else path_info.__eq__\n            if func(info):\n                return True\n        return False\n\n    return list(filter(_filter, metrics))\n\n\ndef _extract_metrics(metrics):\n    if isinstance(metrics, (int, float)):\n        return metrics\n\n    if not isinstance(metrics, dict):\n        return None\n\n    ret = {}\n    for key, val in metrics.items():\n        m = _extract_metrics(val)\n        if m:\n            ret[key] = m\n\n    return ret\n\n\ndef _read_metrics(repo, metrics, rev):\n    tree = RepoTree(repo)\n\n    res = {}\n    for metric in metrics:\n        if not tree.exists(metric):\n            continue\n\n        with tree.open(metric, \"r\") as fobj:\n            try:\n                # NOTE this also supports JSON\n                val = yaml.safe_load(fobj)\n            except yaml.YAMLError:\n                logger.debug(\n                    \"failed to read '%s' on '%s'\", metric, rev, exc_info=True\n                )\n                continue\n\n            val = _extract_metrics(val)\n            if val:\n                res[str(metric)] = val\n\n    return res\n\n\n@locked\ndef show(\n    repo,\n    targets=None,\n    all_branches=False,\n    all_tags=False,\n    recursive=False,\n    revs=None,\n    all_commits=False,\n):\n    res = {}\n\n    for rev in repo.brancher(\n        revs=revs,\n        all_branches=all_branches,\n        all_tags=all_tags,\n        all_commits=all_commits,\n    ):\n        metrics = _collect_metrics(repo, targets, recursive)\n        vals = _read_metrics(repo, metrics, rev)\n\n        if vals:\n            res[rev] = vals\n\n    if not res:\n        raise NoMetricsError()\n\n    # Hide working tree metrics if they are the same as in the active branch\n    try:\n        active_branch = repo.scm.active_branch()\n    except TypeError:\n        pass  # Detached head\n    else:\n        if res.get(\"working tree\") == res.get(active_branch):\n            res.pop(\"working tree\", None)\n\n    return res\n", "dvc/repo/params/show.py": "import logging\n\nimport yaml\n\nfrom dvc.dependency.param import ParamsDependency\nfrom dvc.exceptions import DvcException\nfrom dvc.path_info import PathInfo\nfrom dvc.repo import locked\n\nlogger = logging.getLogger(__name__)\n\n\nclass NoParamsError(DvcException):\n    pass\n\n\ndef _collect_configs(repo):\n    configs = set()\n    configs.add(PathInfo(repo.root_dir) / ParamsDependency.DEFAULT_PARAMS_FILE)\n    for stage in repo.stages:\n        for dep in stage.deps:\n            if not isinstance(dep, ParamsDependency):\n                continue\n\n            configs.add(dep.path_info)\n    return list(configs)\n\n\ndef _read_params(repo, configs, rev):\n    res = {}\n    for config in configs:\n        if not repo.tree.exists(config):\n            continue\n\n        with repo.tree.open(config, \"r\") as fobj:\n            try:\n                res[str(config)] = yaml.safe_load(fobj)\n            except yaml.YAMLError:\n                logger.debug(\n                    \"failed to read '%s' on '%s'\", config, rev, exc_info=True\n                )\n                continue\n\n    return res\n\n\n@locked\ndef show(repo, revs=None):\n    res = {}\n\n    for branch in repo.brancher(revs=revs):\n        configs = _collect_configs(repo)\n        params = _read_params(repo, configs, branch)\n\n        if params:\n            res[branch] = params\n\n    if not res:\n        raise NoParamsError(\"no parameter configs files in this repository\")\n\n    # Hide working tree params if they are the same as in the active branch\n    try:\n        active_branch = repo.scm.active_branch()\n    except TypeError:\n        pass  # Detached head\n    else:\n        if res.get(\"working tree\") == res.get(active_branch):\n            res.pop(\"working tree\", None)\n\n    return res\n", "dvc/repo/plots/data.py": "import csv\nimport io\nimport json\nimport logging\nimport os\nfrom collections import OrderedDict\nfrom copy import copy\n\nimport yaml\nfrom funcy import first\nfrom yaml import SafeLoader\n\nfrom dvc.exceptions import DvcException, PathMissingError\n\nlogger = logging.getLogger(__name__)\n\n\nclass PlotMetricTypeError(DvcException):\n    def __init__(self, file):\n        super().__init__(\n            \"'{}' - file type error\\n\"\n            \"Only JSON, YAML, CSV and TSV formats are supported.\".format(file)\n        )\n\n\nclass PlotDataStructureError(DvcException):\n    def __init__(self):\n        super().__init__(\n            \"Plot data extraction failed. Please see \"\n            \"https://man.dvc.org/plot for supported data formats.\"\n        )\n\n\nclass JsonParsingError(DvcException):\n    def __init__(self, file):\n        super().__init__(\n            \"Failed to infer data structure from '{}'. Did you forget \"\n            \"to specify JSONpath?\".format(file)\n        )\n\n\nclass NoMetricOnRevisionError(DvcException):\n    def __init__(self, path, revision):\n        self.path = path\n        self.revision = revision\n        super().__init__(f\"Could not find '{path}' on revision '{revision}'\")\n\n\nclass NoMetricInHistoryError(DvcException):\n    def __init__(self, path):\n        super().__init__(f\"Could not find '{path}'.\")\n\n\ndef plot_data(filename, revision, content):\n    _, extension = os.path.splitext(filename.lower())\n    if extension == \".json\":\n        return JSONPlotData(filename, revision, content)\n    elif extension == \".csv\":\n        return CSVPlotData(filename, revision, content)\n    elif extension == \".tsv\":\n        return CSVPlotData(filename, revision, content, delimiter=\"\\t\")\n    elif extension == \".yaml\":\n        return YAMLPlotData(filename, revision, content)\n    raise PlotMetricTypeError(filename)\n\n\ndef _filter_fields(data_points, filename, revision, fields=None, **kwargs):\n    if not fields:\n        return data_points\n    assert isinstance(fields, set)\n\n    new_data = []\n    for data_point in data_points:\n        new_dp = copy(data_point)\n\n        keys = set(data_point.keys())\n        if keys & fields != fields:\n            raise DvcException(\n                \"Could not find fields: '{}' for '{}' at '{}'.\".format(\n                    \", \" \"\".join(fields), filename, revision\n                )\n            )\n\n        to_del = keys - fields\n        for key in to_del:\n            del new_dp[key]\n        new_data.append(new_dp)\n    return new_data\n\n\ndef _apply_path(data, path=None, **kwargs):\n    if not path or not isinstance(data, dict):\n        return data\n\n    import jsonpath_ng\n\n    found = jsonpath_ng.parse(path).find(data)\n    first_datum = first(found)\n    if (\n        len(found) == 1\n        and isinstance(first_datum.value, list)\n        and isinstance(first(first_datum.value), dict)\n    ):\n        data_points = first_datum.value\n    elif len(first_datum.path.fields) == 1:\n        field_name = first(first_datum.path.fields)\n        data_points = [{field_name: datum.value} for datum in found]\n    else:\n        raise PlotDataStructureError()\n\n    if not isinstance(data_points, list) or not (\n        isinstance(first(data_points), dict)\n    ):\n        raise PlotDataStructureError()\n\n    return data_points\n\n\ndef _lists(dictionary):\n    for _, value in dictionary.items():\n        if isinstance(value, dict):\n            yield from (_lists(value))\n        elif isinstance(value, list):\n            yield value\n\n\ndef _find_data(data, fields=None, **kwargs):\n    if not isinstance(data, dict):\n        return data\n\n    if not fields:\n        # just look for first list of dicts\n        fields = set()\n\n    for l in _lists(data):\n        if all(isinstance(dp, dict) for dp in l):\n            if set(first(l).keys()) & fields == fields:\n                return l\n    raise PlotDataStructureError()\n\n\ndef _append_index(data_points, append_index=False, **kwargs):\n    if not append_index:\n        return data_points\n\n    if PlotData.INDEX_FIELD in first(data_points).keys():\n        raise DvcException(\n            \"Cannot append index. Field of same name ('{}') found in data. \"\n            \"Use `-x` to specify x axis field.\".format(PlotData.INDEX_FIELD)\n        )\n\n    for index, data_point in enumerate(data_points):\n        data_point[PlotData.INDEX_FIELD] = index\n    return data_points\n\n\ndef _append_revision(data_points, revision, **kwargs):\n    for data_point in data_points:\n        data_point[PlotData.REVISION_FIELD] = revision\n    return data_points\n\n\nclass PlotData:\n    REVISION_FIELD = \"rev\"\n    INDEX_FIELD = \"index\"\n\n    def __init__(self, filename, revision, content, **kwargs):\n        self.filename = filename\n        self.revision = revision\n        self.content = content\n\n    def raw(self, **kwargs):\n        raise NotImplementedError\n\n    def _processors(self):\n        return [_filter_fields, _append_index, _append_revision]\n\n    def to_datapoints(self, **kwargs):\n        data = self.raw(**kwargs)\n\n        for data_proc in self._processors():\n            data = data_proc(\n                data, filename=self.filename, revision=self.revision, **kwargs\n            )\n        return data\n\n\nclass JSONPlotData(PlotData):\n    def raw(self, **kwargs):\n        return json.loads(self.content, object_pairs_hook=OrderedDict)\n\n    def _processors(self):\n        parent_processors = super()._processors()\n        return [_apply_path, _find_data] + parent_processors\n\n\nclass CSVPlotData(PlotData):\n    def __init__(self, filename, revision, content, delimiter=\",\"):\n        super().__init__(filename, revision, content)\n        self.delimiter = delimiter\n\n    def raw(self, csv_header=True, **kwargs):\n        first_row = first(csv.reader(io.StringIO(self.content)))\n\n        if csv_header:\n            reader = csv.DictReader(\n                io.StringIO(self.content), delimiter=self.delimiter,\n            )\n        else:\n            reader = csv.DictReader(\n                io.StringIO(self.content),\n                delimiter=self.delimiter,\n                fieldnames=[str(i) for i in range(len(first_row))],\n            )\n\n        fieldnames = reader.fieldnames\n        data = [row for row in reader]\n\n        return [\n            OrderedDict([(field, data_point[field]) for field in fieldnames])\n            for data_point in data\n        ]\n\n\nclass YAMLPlotData(PlotData):\n    def raw(self, **kwargs):\n        class OrderedLoader(SafeLoader):\n            pass\n\n        def construct_mapping(loader, node):\n            loader.flatten_mapping(node)\n            return OrderedDict(loader.construct_pairs(node))\n\n        OrderedLoader.add_constructor(\n            yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, construct_mapping\n        )\n\n        return yaml.load(self.content, OrderedLoader)\n\n\ndef _load_from_revision(repo, datafile, revision):\n    from dvc.repo.tree import RepoTree\n\n    tree = RepoTree(repo)\n\n    try:\n        with tree.open(datafile) as fobj:\n            datafile_content = fobj.read()\n\n    except (FileNotFoundError, PathMissingError):\n        raise NoMetricOnRevisionError(datafile, revision)\n\n    return plot_data(datafile, revision, datafile_content)\n\n\ndef _load_from_revisions(repo, datafile, revisions):\n    data = []\n    exceptions = []\n\n    for rev in repo.brancher(revs=revisions):\n        if rev == \"working tree\" and rev not in revisions:\n            continue\n\n        try:\n            data.append(_load_from_revision(repo, datafile, rev))\n        except NoMetricOnRevisionError as e:\n            exceptions.append(e)\n        except PlotMetricTypeError:\n            raise\n        except (yaml.error.YAMLError, json.decoder.JSONDecodeError, csv.Error):\n            logger.error(f\"Failed to parse '{datafile}' at '{rev}'.\")\n            raise\n\n    if not data and exceptions:\n        raise NoMetricInHistoryError(datafile)\n    else:\n        for e in exceptions:\n            logger.warning(\n                \"File '{}' was not found at: '{}'. It will not be \"\n                \"plotted.\".format(e.path, e.revision)\n            )\n    return data\n", "dvc/repo/plots/diff.py": "def _revisions(revs, is_dirty):\n    revisions = revs or []\n    if len(revisions) <= 1:\n        if len(revisions) == 0 and is_dirty:\n            revisions.append(\"HEAD\")\n        revisions.append(\"working tree\")\n    return revisions\n\n\ndef diff(repo, *args, revs=None, **kwargs):\n    return repo.plots.show(\n        *args, revs=_revisions(revs, repo.scm.is_dirty()), **kwargs\n    )\n", "dvc/repo/plots/show.py": "import copy\nimport logging\nimport os\n\nfrom funcy import first, last, project\n\nfrom dvc.exceptions import DvcException\nfrom dvc.repo import locked\nfrom dvc.schema import PLOT_PROPS\n\nfrom .data import NoMetricInHistoryError, PlotData\nfrom .template import NoDataForTemplateError, Template\n\nlogger = logging.getLogger(__name__)\n\n\nclass TooManyDataSourcesError(DvcException):\n    def __init__(self, datafile, template_datafiles):\n        super().__init__(\n            \"Unable to infer which of possible data sources: '{}' \"\n            \"should be replaced with '{}'.\".format(\n                \", \".join(template_datafiles), datafile\n            )\n        )\n\n\nclass NoDataOrTemplateProvided(DvcException):\n    def __init__(self):\n        super().__init__(\"Datafile or template is not specified.\")\n\n\nclass TooManyTemplatesError(DvcException):\n    pass\n\n\ndef _evaluate_templatepath(repo, template=None):\n    if not template:\n        return repo.plot_templates.default_template\n\n    if os.path.exists(template):\n        return template\n    return repo.plot_templates.get_template(template)\n\n\n@locked\ndef fill_template(\n    repo, datafile, template_path, revisions, props,\n):\n    # Copy things to not modify passed values\n    props = props.copy()\n    fields = copy.copy(props.get(\"fields\"))\n\n    if props.get(\"x\") and fields:\n        fields.add(props.get(\"x\"))\n\n    if props.get(\"y\") and fields:\n        fields.add(props.get(\"y\"))\n\n    template_datafiles, x_anchor, y_anchor = _parse_template(\n        template_path, datafile\n    )\n    append_index = x_anchor and not props.get(\"x\")\n    if append_index:\n        props[\"x\"] = PlotData.INDEX_FIELD\n\n    template_data = {}\n    for template_datafile in template_datafiles:\n        from .data import _load_from_revisions\n\n        plot_datas = _load_from_revisions(repo, template_datafile, revisions)\n        tmp_data = []\n        for pd in plot_datas:\n            rev_data_points = pd.to_datapoints(\n                fields=fields,\n                path=props.get(\"path\"),\n                csv_header=props.get(\"csv_header\", True),\n                append_index=append_index,\n            )\n\n            if y_anchor and not props.get(\"y\"):\n                props[\"y\"] = _infer_y_field(rev_data_points, props.get(\"x\"))\n            tmp_data.extend(rev_data_points)\n\n        template_data[template_datafile] = tmp_data\n\n    if len(template_data) == 0:\n        raise NoDataForTemplateError(template_path)\n\n    content = Template.fill(\n        template_path, template_data, priority_datafile=datafile, props=props,\n    )\n\n    path = datafile or \",\".join(template_datafiles)\n\n    return path, content\n\n\ndef _infer_y_field(rev_data_points, x_field):\n    all_fields = list(first(rev_data_points).keys())\n    all_fields.remove(PlotData.REVISION_FIELD)\n    if x_field and x_field in all_fields:\n        all_fields.remove(x_field)\n    y_field = last(all_fields)\n    return y_field\n\n\ndef _show(repo, datafile=None, revs=None, props=None):\n    if revs is None:\n        revs = [\"working tree\"]\n\n    if not datafile and not props.get(\"template\"):\n        raise NoDataOrTemplateProvided()\n\n    template_path = _evaluate_templatepath(repo, props.get(\"template\"))\n\n    plot_datafile, plot_content = fill_template(\n        repo, datafile, template_path, revs, props\n    )\n\n    return plot_datafile, plot_content\n\n\ndef _parse_template(template_path, priority_datafile):\n    with open(template_path) as fobj:\n        tempalte_content = fobj.read()\n\n    template_datafiles = Template.parse_data_anchors(tempalte_content)\n    if priority_datafile:\n        if len(template_datafiles) > 1:\n            raise TooManyDataSourcesError(\n                priority_datafile, template_datafiles\n            )\n        template_datafiles = {priority_datafile}\n\n    return (\n        template_datafiles,\n        Template.X_ANCHOR in tempalte_content,\n        Template.Y_ANCHOR in tempalte_content,\n    )\n\n\ndef _collect_plots(repo, targets=None):\n    from dvc.exceptions import OutputNotFoundError\n    from contextlib import suppress\n\n    def _targets_to_outs(targets):\n        for t in targets:\n            with suppress(OutputNotFoundError):\n                (out,) = repo.find_outs_by_path(t)\n                yield out\n\n    if targets:\n        outs = _targets_to_outs(targets)\n    else:\n        outs = (out for stage in repo.stages for out in stage.outs if out.plot)\n\n    return {str(out): _plot_props(out) for out in outs}\n\n\ndef _plot_props(out):\n    if not out.plot:\n        raise DvcException(\n            f\"'{out}' is not a plot. Use `dvc plots modify` to change that.\"\n        )\n    if isinstance(out.plot, list):\n        raise DvcException(\"Multiple plots per data file not supported yet.\")\n    if isinstance(out.plot, bool):\n        return {}\n\n    return project(out.plot, PLOT_PROPS)\n\n\ndef show(repo, targets=None, revs=None, props=None) -> dict:\n    if isinstance(targets, str):\n        targets = [targets]\n    if props is None:\n        props = {}\n\n    # Collect plot data files with associated props\n    plots = {}\n    for rev in repo.brancher(revs=revs):\n        if revs is not None and rev not in revs:\n            continue\n\n        for datafile, file_props in _collect_plots(repo, targets).items():\n            # props from command line overwrite plot props from out definition\n            full_props = {**file_props, **props}\n\n            if datafile in plots:\n                saved_rev, saved_props = plots[datafile]\n                if saved_props != props:\n                    logger.warning(\n                        f\"Inconsistent plot props for '{datafile}' in \"\n                        f\"'{saved_rev}' and '{rev}'. \"\n                        f\"Going to use ones from '{saved_rev}'\"\n                    )\n            else:\n                plots[datafile] = rev, full_props\n\n    if not plots:\n        if targets:\n            raise NoMetricInHistoryError(\", \".join(targets))\n\n        datafile, plot = _show(repo, datafile=None, revs=revs, props=props)\n        return {datafile: plot}\n\n    return {\n        datafile: _show(repo, datafile=datafile, revs=revs, props=props)[1]\n        for datafile, (_, props) in plots.items()\n    }\n"}, "filelevel_patches": {"dvc/command/gc.py": "@@ -21,7 +21,7 @@ def run(self):\n \n         msg = \"This will remove all cache except items used in \"\n \n-        msg += \"the working tree\"\n+        msg += \"the workspace\"\n         if self.args.all_commits:\n             msg += \" and all git commits\"\n         elif self.args.all_branches and self.args.all_tags:\n@@ -77,7 +77,7 @@ def add_parser(subparsers, parent_parser):\n         \"--workspace\",\n         action=\"store_true\",\n         default=False,\n-        help=\"Keep data files used in the current workspace (working tree).\",\n+        help=\"Keep data files used in the current workspace.\",\n     )\n     gc_parser.add_argument(\n         \"-a\",", "dvc/repo/brancher.py": "@@ -18,7 +18,7 @@ def brancher(  # noqa: E302\n         str: the display name for the currently selected tree, it could be:\n             - a git revision identifier\n             - empty string it there is no branches to iterate over\n-            - \"Working Tree\" if there are uncommitted changes in the SCM repo\n+            - \"workspace\" if there are uncommitted changes in the SCM repo\n     \"\"\"\n     if not any([revs, all_branches, all_tags, all_commits]):\n         yield \"\"\n@@ -30,10 +30,10 @@ def brancher(  # noqa: E302\n     scm = self.scm\n \n     self.tree = WorkingTree(self.root_dir)\n-    yield \"working tree\"\n+    yield \"workspace\"\n \n-    if revs and \"working tree\" in revs:\n-        revs.remove(\"working tree\")\n+    if revs and \"workspace\" in revs:\n+        revs.remove(\"workspace\")\n \n     if all_commits:\n         revs = scm.list_all_commits()", "dvc/repo/diff.py": "@@ -7,7 +7,7 @@\n @locked\n def diff(self, a_rev=\"HEAD\", b_rev=None):\n     \"\"\"\n-    By default, it compares the working tree with the last commit's tree.\n+    By default, it compares the workspace with the last commit's tree.\n \n     This implementation differs from `git diff` since DVC doesn't have\n     the concept of `index`, but it keeps the same interface, thus,", "dvc/repo/metrics/show.py": "@@ -104,13 +104,13 @@ def show(\n     if not res:\n         raise NoMetricsError()\n \n-    # Hide working tree metrics if they are the same as in the active branch\n+    # Hide workspace metrics if they are the same as in the active branch\n     try:\n         active_branch = repo.scm.active_branch()\n     except TypeError:\n         pass  # Detached head\n     else:\n-        if res.get(\"working tree\") == res.get(active_branch):\n-            res.pop(\"working tree\", None)\n+        if res.get(\"workspace\") == res.get(active_branch):\n+            res.pop(\"workspace\", None)\n \n     return res", "dvc/repo/params/show.py": "@@ -58,13 +58,13 @@ def show(repo, revs=None):\n     if not res:\n         raise NoParamsError(\"no parameter configs files in this repository\")\n \n-    # Hide working tree params if they are the same as in the active branch\n+    # Hide workspace params if they are the same as in the active branch\n     try:\n         active_branch = repo.scm.active_branch()\n     except TypeError:\n         pass  # Detached head\n     else:\n-        if res.get(\"working tree\") == res.get(active_branch):\n-            res.pop(\"working tree\", None)\n+        if res.get(\"workspace\") == res.get(active_branch):\n+            res.pop(\"workspace\", None)\n \n     return res", "dvc/repo/plots/data.py": "@@ -258,7 +258,7 @@ def _load_from_revisions(repo, datafile, revisions):\n     exceptions = []\n \n     for rev in repo.brancher(revs=revisions):\n-        if rev == \"working tree\" and rev not in revisions:\n+        if rev == \"workspace\" and rev not in revisions:\n             continue\n \n         try:", "dvc/repo/plots/diff.py": "@@ -3,7 +3,7 @@ def _revisions(revs, is_dirty):\n     if len(revisions) <= 1:\n         if len(revisions) == 0 and is_dirty:\n             revisions.append(\"HEAD\")\n-        revisions.append(\"working tree\")\n+        revisions.append(\"workspace\")\n     return revisions", "dvc/repo/plots/show.py": "@@ -106,7 +106,7 @@ def _infer_y_field(rev_data_points, x_field):\n \n def _show(repo, datafile=None, revs=None, props=None):\n     if revs is None:\n-        revs = [\"working tree\"]\n+        revs = [\"workspace\"]\n \n     if not datafile and not props.get(\"template\"):\n         raise NoDataOrTemplateProvided()"}}