We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
diff: unexpected error when rev_a & rev_b point to the same revision
## Description

`dvc diff <rev_a> <rev_b>` outputs `ERROR: unexpected error - '<rev_a>'` when <rev_a> and <rev_b> happen to point to the same revision.

### Reproduce

There is a Git repo with 4 branches:
<img width="400" src="https://github.com/iterative/dvc/assets/12026360/087d85eb-56e0-4f6c-8904-bc0817bceb22">
- *main* & *new-1* point to the same revision
- *new-main* & *new-2* point to the same revision

Run the following commands:

1. `dvc diff main new-main`
2. `dvc diff main new-1`
3. `dvc diff new-main new-2`

### Expected

All the commands should work.

However, only the 1st command work and the others fail with unexpected error:

```console
root@dc53a8a4f5c3:~/data/my-ds/VC-Project-Dev# dvc diff main new-main
Added:
    data/raw/new_dir/f1.txt

Deleted:
    data/raw/new_dir1/new.txt
    data/rsc/images/f1.txt

Modified:
    data/

files summary: 1 added, 2 deleted
root@dc53a8a4f5c3:~/data/my-ds/VC-Project-Dev# dvc diff main new-1
ERROR: unexpected error - 'main'                                      

Having any troubles? Hit us up at https://dvc.org/support, we are always happy to help!
root@dc53a8a4f5c3:~/data/my-ds/VC-Project-Dev# dvc diff new-main new-2
ERROR: unexpected error - 'new-main'                                  

Having any troubles? Hit us up at https://dvc.org/support, we are always happy to help!
```

With verbose output:

```console
# dvc diff -v new-main new-2
2023-06-14 10:03:55,347 DEBUG: v3.0.0 (pip), CPython 3.8.12 on Linux-5.4.0-84-generic-x86_64-with-glibc2.10
2023-06-14 10:03:55,347 DEBUG: command: /opt/conda/bin/dvc diff -v new-main new-2
2023-06-14 10:03:56,359 ERROR: unexpected error - 'new-main'          
Traceback (most recent call last):
  File "/opt/conda/lib/python3.8/site-packages/dvc/cli/__init__.py", line 209, in main
    ret = cmd.do_run()
  File "/opt/conda/lib/python3.8/site-packages/dvc/cli/command.py", line 26, in do_run
    return self.run()
  File "/opt/conda/lib/python3.8/site-packages/dvc/commands/diff.py", line 123, in run
    diff = self.repo.diff(self.args.a_rev, self.args.b_rev, self.args.targets)
  File "/opt/conda/lib/python3.8/site-packages/dvc/repo/__init__.py", line 64, in wrapper
    return f(repo, *args, **kwargs)
  File "/opt/conda/lib/python3.8/site-packages/dvc/repo/diff.py", line 159, in diff
    old = indexes[a_rev]
KeyError: 'new-main'

2023-06-14 10:03:56,573 DEBUG: Version info for developers:
DVC version: 3.0.0 (pip)
------------------------
Platform: Python 3.8.12 on Linux-5.4.0-84-generic-x86_64-with-glibc2.10
Subprojects:
        dvc_data = 1.11.0
        dvc_objects = 0.23.0
        dvc_render = 0.5.3
        dvc_task = 0.3.0
        scmrepo = 1.0.3
Supports:
        http (aiohttp = 3.8.4, aiohttp-retry = 2.8.3),
        https (aiohttp = 3.8.4, aiohttp-retry = 2.8.3),
        s3 (s3fs = 2023.6.0, boto3 = 1.26.76)
Config:
        Global: /mlsteam/.config/dvc
        System: /etc/xdg/dvc
Cache types: <https://error.dvc.org/no-dvc-cache>
Caches: local
Remotes: s3
Workspace directory: xfs on /dev/sdb1
Repo: dvc, git
Repo.site_cache_dir: /var/tmp/dvc/repo/91479b46e9e0a07ae16a57a5b4209cf5

Having any troubles? Hit us up at https://dvc.org/support, we are always happy to help!
2023-06-14 10:03:56,577 DEBUG: Analytics is disabled.
```

### Environment information

**Output of `dvc doctor`:**

```console
# dvc doctor
DVC version: 3.0.0 (pip)
------------------------
Platform: Python 3.8.12 on Linux-5.4.0-84-generic-x86_64-with-glibc2.10
Subprojects:
        dvc_data = 1.11.0
        dvc_objects = 0.23.0
        dvc_render = 0.5.3
        dvc_task = 0.3.0
        scmrepo = 1.0.3
Supports:
        http (aiohttp = 3.8.4, aiohttp-retry = 2.8.3),
        https (aiohttp = 3.8.4, aiohttp-retry = 2.8.3),
        s3 (s3fs = 2023.6.0, boto3 = 1.26.76)
Config:
        Global: /mlsteam/.config/dvc
        System: /etc/xdg/dvc
Cache types: <https://error.dvc.org/no-dvc-cache>
Caches: local
Remotes: s3
Workspace directory: xfs on /dev/sdb1
Repo: dvc, git
Repo.site_cache_dir: /var/tmp/dvc/repo/91479b46e9e0a07ae16a57a5b4209cf5
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `dvc/repo/diff.py`
Content:
```
1 import errno
2 import logging
3 import os
4 from collections import defaultdict
5 from typing import Dict, List, Optional
6 
7 from dvc.repo import locked
8 from dvc.ui import ui
9 
10 logger = logging.getLogger(__name__)
11 
12 
13 def _path(entry):
14     if entry and entry.meta and entry.meta.isdir:
15         return os.path.join(*entry.key, "")
16     return os.path.join(*entry.key)
17 
18 
19 def _hash(entry):
20     if entry and entry.hash_info:
21         return entry.hash_info.value
22     return None
23 
24 
25 def _diff(old, new, with_missing=False):
26     from dvc_data.index.diff import ADD, DELETE, MODIFY, RENAME
27     from dvc_data.index.diff import diff as idiff
28 
29     ret: "Dict[str, List[Dict]]" = {
30         "added": [],
31         "deleted": [],
32         "modified": [],
33         "renamed": [],
34         "not in cache": [],
35     }
36 
37     for change in idiff(
38         old,
39         new,
40         with_renames=True,
41         hash_only=True,
42     ):
43         if change.typ == ADD:
44             ret["added"].append(
45                 {
46                     "path": _path(change.new),
47                     "hash": _hash(change.new),
48                 }
49             )
50         elif change.typ == DELETE:
51             ret["deleted"].append(
52                 {
53                     "path": _path(change.old),
54                     "hash": _hash(change.old),
55                 }
56             )
57         elif change.typ == MODIFY:
58             ret["modified"].append(
59                 {
60                     "path": _path(change.old),
61                     "hash": {
62                         "old": _hash(change.old),
63                         "new": _hash(change.new),
64                     },
65                 }
66             )
67         elif change.typ == RENAME:
68             ret["renamed"].append(
69                 {
70                     "path": {
71                         "old": _path(change.old),
72                         "new": _path(change.new),
73                     },
74                     "hash": _hash(change.old),
75                 }
76             )
77 
78         if (
79             with_missing
80             and change.old
81             and change.old.hash_info
82             and not old.storage_map.cache_exists(change.old)
83         ):
84             ret["not in cache"].append(
85                 {
86                     "path": _path(change.old),
87                     "hash": _hash(change.old),
88                 }
89             )
90 
91     return ret if any(ret.values()) else {}
92 
93 
94 @locked
95 def diff(
96     self,
97     a_rev: str = "HEAD",
98     b_rev: Optional[str] = None,
99     targets: Optional[List[str]] = None,
100     recursive: bool = False,
101 ):
102     """
103     By default, it compares the workspace with the last commit's fs.
104 
105     This implementation differs from `git diff` since DVC doesn't have
106     the concept of `index`, but it keeps the same interface, thus,
107     `dvc diff` would be the same as `dvc diff HEAD`.
108     """
109     if self.scm.no_commits:
110         return {}
111 
112     indexes = {}
113     missing_targets = defaultdict(set)
114     with_missing = False
115     if not b_rev:
116         b_rev = "workspace"
117         with_missing = True
118 
119     for rev in self.brancher(revs=[a_rev, b_rev]):
120         if rev == "workspace" and b_rev != "workspace":
121             # brancher always returns workspace, but we only need to compute
122             # workspace paths/checksums if b_rev was None
123             continue
124 
125         def onerror(target, _exc):
126             # pylint: disable-next=cell-var-from-loop
127             missing_targets[rev].add(target)  # noqa: B023
128 
129         view = self.index.targets_view(
130             targets,
131             onerror=onerror,
132             recursive=recursive,
133         )
134 
135         if rev == "workspace":
136             from .index import build_data_index
137 
138             with ui.status("Building workspace index"):
139                 data = build_data_index(
140                     view,
141                     self.root_dir,
142                     self.fs,
143                     compute_hash=True,
144                 )
145         else:
146             data = view.data["repo"]
147 
148         assert rev not in indexes
149         indexes[rev] = data
150 
151     if targets:
152         old_missing = missing_targets.get(a_rev, set())
153         new_missing = missing_targets.get(b_rev, set())
154 
155         # check for overlapping missing targets between a_rev and b_rev
156         for target in old_missing & new_missing:
157             raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), target)
158 
159     old = indexes[a_rev]
160     new = indexes[b_rev]
161 
162     with ui.status("Calculating diff"):
163         return _diff(old, new, with_missing=with_missing)
164
```
--- END FILES ---

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

Here is an example:

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

```

diff --git a/dvc/repo/diff.py b/dvc/repo/diff.py
--- a/dvc/repo/diff.py
+++ b/dvc/repo/diff.py
@@ -156,8 +156,13 @@
         for target in old_missing & new_missing:
             raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), target)
 
-    old = indexes[a_rev]
-    new = indexes[b_rev]
+    if len(indexes.keys()) == 1:
+        # both a_rev and b_rev point to the same sha, nothing to compare
+        old = None
+        new = None
+    else:
+        old = indexes[a_rev]
+        new = indexes[b_rev]
 
     with ui.status("Calculating diff"):
         return _diff(old, new, with_missing=with_missing)

{"golden_diff": "diff --git a/dvc/repo/diff.py b/dvc/repo/diff.py\n--- a/dvc/repo/diff.py\n+++ b/dvc/repo/diff.py\n@@ -156,8 +156,13 @@\n         for target in old_missing & new_missing:\n             raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), target)\n \n-    old = indexes[a_rev]\n-    new = indexes[b_rev]\n+    if len(indexes.keys()) == 1:\n+        # both a_rev and b_rev point to the same sha, nothing to compare\n+        old = None\n+        new = None\n+    else:\n+        old = indexes[a_rev]\n+        new = indexes[b_rev]\n \n     with ui.status(\"Calculating diff\"):\n         return _diff(old, new, with_missing=with_missing)\n", "issue": "diff: unexpected error when rev_a & rev_b point to the same revision\n## Description\r\n\r\n`dvc diff <rev_a> <rev_b>` outputs `ERROR: unexpected error - '<rev_a>'` when <rev_a> and <rev_b> happen to point to the same revision.\r\n\r\n### Reproduce\r\n\r\nThere is a Git repo with 4 branches:\r\n<img width=\"400\" src=\"https://github.com/iterative/dvc/assets/12026360/087d85eb-56e0-4f6c-8904-bc0817bceb22\">\r\n- *main* & *new-1* point to the same revision\r\n- *new-main* & *new-2* point to the same revision\r\n\r\nRun the following commands:\r\n\r\n1. `dvc diff main new-main`\r\n2. `dvc diff main new-1`\r\n3. `dvc diff new-main new-2`\r\n\r\n### Expected\r\n\r\nAll the commands should work.\r\n\r\nHowever, only the 1st command work and the others fail with unexpected error:\r\n\r\n```console\r\nroot@dc53a8a4f5c3:~/data/my-ds/VC-Project-Dev# dvc diff main new-main\r\nAdded:\r\n    data/raw/new_dir/f1.txt\r\n\r\nDeleted:\r\n    data/raw/new_dir1/new.txt\r\n    data/rsc/images/f1.txt\r\n\r\nModified:\r\n    data/\r\n\r\nfiles summary: 1 added, 2 deleted\r\nroot@dc53a8a4f5c3:~/data/my-ds/VC-Project-Dev# dvc diff main new-1\r\nERROR: unexpected error - 'main'                                      \r\n\r\nHaving any troubles? Hit us up at https://dvc.org/support, we are always happy to help!\r\nroot@dc53a8a4f5c3:~/data/my-ds/VC-Project-Dev# dvc diff new-main new-2\r\nERROR: unexpected error - 'new-main'                                  \r\n\r\nHaving any troubles? Hit us up at https://dvc.org/support, we are always happy to help!\r\n```\r\n\r\nWith verbose output:\r\n\r\n```console\r\n# dvc diff -v new-main new-2\r\n2023-06-14 10:03:55,347 DEBUG: v3.0.0 (pip), CPython 3.8.12 on Linux-5.4.0-84-generic-x86_64-with-glibc2.10\r\n2023-06-14 10:03:55,347 DEBUG: command: /opt/conda/bin/dvc diff -v new-main new-2\r\n2023-06-14 10:03:56,359 ERROR: unexpected error - 'new-main'          \r\nTraceback (most recent call last):\r\n  File \"/opt/conda/lib/python3.8/site-packages/dvc/cli/__init__.py\", line 209, in main\r\n    ret = cmd.do_run()\r\n  File \"/opt/conda/lib/python3.8/site-packages/dvc/cli/command.py\", line 26, in do_run\r\n    return self.run()\r\n  File \"/opt/conda/lib/python3.8/site-packages/dvc/commands/diff.py\", line 123, in run\r\n    diff = self.repo.diff(self.args.a_rev, self.args.b_rev, self.args.targets)\r\n  File \"/opt/conda/lib/python3.8/site-packages/dvc/repo/__init__.py\", line 64, in wrapper\r\n    return f(repo, *args, **kwargs)\r\n  File \"/opt/conda/lib/python3.8/site-packages/dvc/repo/diff.py\", line 159, in diff\r\n    old = indexes[a_rev]\r\nKeyError: 'new-main'\r\n\r\n2023-06-14 10:03:56,573 DEBUG: Version info for developers:\r\nDVC version: 3.0.0 (pip)\r\n------------------------\r\nPlatform: Python 3.8.12 on Linux-5.4.0-84-generic-x86_64-with-glibc2.10\r\nSubprojects:\r\n        dvc_data = 1.11.0\r\n        dvc_objects = 0.23.0\r\n        dvc_render = 0.5.3\r\n        dvc_task = 0.3.0\r\n        scmrepo = 1.0.3\r\nSupports:\r\n        http (aiohttp = 3.8.4, aiohttp-retry = 2.8.3),\r\n        https (aiohttp = 3.8.4, aiohttp-retry = 2.8.3),\r\n        s3 (s3fs = 2023.6.0, boto3 = 1.26.76)\r\nConfig:\r\n        Global: /mlsteam/.config/dvc\r\n        System: /etc/xdg/dvc\r\nCache types: <https://error.dvc.org/no-dvc-cache>\r\nCaches: local\r\nRemotes: s3\r\nWorkspace directory: xfs on /dev/sdb1\r\nRepo: dvc, git\r\nRepo.site_cache_dir: /var/tmp/dvc/repo/91479b46e9e0a07ae16a57a5b4209cf5\r\n\r\nHaving any troubles? Hit us up at https://dvc.org/support, we are always happy to help!\r\n2023-06-14 10:03:56,577 DEBUG: Analytics is disabled.\r\n```\r\n\r\n### Environment information\r\n\r\n**Output of `dvc doctor`:**\r\n\r\n```console\r\n# dvc doctor\r\nDVC version: 3.0.0 (pip)\r\n------------------------\r\nPlatform: Python 3.8.12 on Linux-5.4.0-84-generic-x86_64-with-glibc2.10\r\nSubprojects:\r\n        dvc_data = 1.11.0\r\n        dvc_objects = 0.23.0\r\n        dvc_render = 0.5.3\r\n        dvc_task = 0.3.0\r\n        scmrepo = 1.0.3\r\nSupports:\r\n        http (aiohttp = 3.8.4, aiohttp-retry = 2.8.3),\r\n        https (aiohttp = 3.8.4, aiohttp-retry = 2.8.3),\r\n        s3 (s3fs = 2023.6.0, boto3 = 1.26.76)\r\nConfig:\r\n        Global: /mlsteam/.config/dvc\r\n        System: /etc/xdg/dvc\r\nCache types: <https://error.dvc.org/no-dvc-cache>\r\nCaches: local\r\nRemotes: s3\r\nWorkspace directory: xfs on /dev/sdb1\r\nRepo: dvc, git\r\nRepo.site_cache_dir: /var/tmp/dvc/repo/91479b46e9e0a07ae16a57a5b4209cf5\r\n```\r\n\n", "before_files": [{"content": "import errno\nimport logging\nimport os\nfrom collections import defaultdict\nfrom typing import Dict, List, Optional\n\nfrom dvc.repo import locked\nfrom dvc.ui import ui\n\nlogger = logging.getLogger(__name__)\n\n\ndef _path(entry):\n    if entry and entry.meta and entry.meta.isdir:\n        return os.path.join(*entry.key, \"\")\n    return os.path.join(*entry.key)\n\n\ndef _hash(entry):\n    if entry and entry.hash_info:\n        return entry.hash_info.value\n    return None\n\n\ndef _diff(old, new, with_missing=False):\n    from dvc_data.index.diff import ADD, DELETE, MODIFY, RENAME\n    from dvc_data.index.diff import diff as idiff\n\n    ret: \"Dict[str, List[Dict]]\" = {\n        \"added\": [],\n        \"deleted\": [],\n        \"modified\": [],\n        \"renamed\": [],\n        \"not in cache\": [],\n    }\n\n    for change in idiff(\n        old,\n        new,\n        with_renames=True,\n        hash_only=True,\n    ):\n        if change.typ == ADD:\n            ret[\"added\"].append(\n                {\n                    \"path\": _path(change.new),\n                    \"hash\": _hash(change.new),\n                }\n            )\n        elif change.typ == DELETE:\n            ret[\"deleted\"].append(\n                {\n                    \"path\": _path(change.old),\n                    \"hash\": _hash(change.old),\n                }\n            )\n        elif change.typ == MODIFY:\n            ret[\"modified\"].append(\n                {\n                    \"path\": _path(change.old),\n                    \"hash\": {\n                        \"old\": _hash(change.old),\n                        \"new\": _hash(change.new),\n                    },\n                }\n            )\n        elif change.typ == RENAME:\n            ret[\"renamed\"].append(\n                {\n                    \"path\": {\n                        \"old\": _path(change.old),\n                        \"new\": _path(change.new),\n                    },\n                    \"hash\": _hash(change.old),\n                }\n            )\n\n        if (\n            with_missing\n            and change.old\n            and change.old.hash_info\n            and not old.storage_map.cache_exists(change.old)\n        ):\n            ret[\"not in cache\"].append(\n                {\n                    \"path\": _path(change.old),\n                    \"hash\": _hash(change.old),\n                }\n            )\n\n    return ret if any(ret.values()) else {}\n\n\n@locked\ndef diff(\n    self,\n    a_rev: str = \"HEAD\",\n    b_rev: Optional[str] = None,\n    targets: Optional[List[str]] = None,\n    recursive: bool = False,\n):\n    \"\"\"\n    By default, it compares the workspace with the last commit's fs.\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    if self.scm.no_commits:\n        return {}\n\n    indexes = {}\n    missing_targets = defaultdict(set)\n    with_missing = False\n    if not b_rev:\n        b_rev = \"workspace\"\n        with_missing = True\n\n    for rev in self.brancher(revs=[a_rev, b_rev]):\n        if rev == \"workspace\" and b_rev != \"workspace\":\n            # brancher always returns workspace, but we only need to compute\n            # workspace paths/checksums if b_rev was None\n            continue\n\n        def onerror(target, _exc):\n            # pylint: disable-next=cell-var-from-loop\n            missing_targets[rev].add(target)  # noqa: B023\n\n        view = self.index.targets_view(\n            targets,\n            onerror=onerror,\n            recursive=recursive,\n        )\n\n        if rev == \"workspace\":\n            from .index import build_data_index\n\n            with ui.status(\"Building workspace index\"):\n                data = build_data_index(\n                    view,\n                    self.root_dir,\n                    self.fs,\n                    compute_hash=True,\n                )\n        else:\n            data = view.data[\"repo\"]\n\n        assert rev not in indexes\n        indexes[rev] = data\n\n    if targets:\n        old_missing = missing_targets.get(a_rev, set())\n        new_missing = missing_targets.get(b_rev, set())\n\n        # check for overlapping missing targets between a_rev and b_rev\n        for target in old_missing & new_missing:\n            raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), target)\n\n    old = indexes[a_rev]\n    new = indexes[b_rev]\n\n    with ui.status(\"Calculating diff\"):\n        return _diff(old, new, with_missing=with_missing)\n", "path": "dvc/repo/diff.py"}], "after_files": [{"content": "import errno\nimport logging\nimport os\nfrom collections import defaultdict\nfrom typing import Dict, List, Optional\n\nfrom dvc.repo import locked\nfrom dvc.ui import ui\n\nlogger = logging.getLogger(__name__)\n\n\ndef _path(entry):\n    if entry and entry.meta and entry.meta.isdir:\n        return os.path.join(*entry.key, \"\")\n    return os.path.join(*entry.key)\n\n\ndef _hash(entry):\n    if entry and entry.hash_info:\n        return entry.hash_info.value\n    return None\n\n\ndef _diff(old, new, with_missing=False):\n    from dvc_data.index.diff import ADD, DELETE, MODIFY, RENAME\n    from dvc_data.index.diff import diff as idiff\n\n    ret: \"Dict[str, List[Dict]]\" = {\n        \"added\": [],\n        \"deleted\": [],\n        \"modified\": [],\n        \"renamed\": [],\n        \"not in cache\": [],\n    }\n\n    for change in idiff(\n        old,\n        new,\n        with_renames=True,\n        hash_only=True,\n    ):\n        if change.typ == ADD:\n            ret[\"added\"].append(\n                {\n                    \"path\": _path(change.new),\n                    \"hash\": _hash(change.new),\n                }\n            )\n        elif change.typ == DELETE:\n            ret[\"deleted\"].append(\n                {\n                    \"path\": _path(change.old),\n                    \"hash\": _hash(change.old),\n                }\n            )\n        elif change.typ == MODIFY:\n            ret[\"modified\"].append(\n                {\n                    \"path\": _path(change.old),\n                    \"hash\": {\n                        \"old\": _hash(change.old),\n                        \"new\": _hash(change.new),\n                    },\n                }\n            )\n        elif change.typ == RENAME:\n            ret[\"renamed\"].append(\n                {\n                    \"path\": {\n                        \"old\": _path(change.old),\n                        \"new\": _path(change.new),\n                    },\n                    \"hash\": _hash(change.old),\n                }\n            )\n\n        if (\n            with_missing\n            and change.old\n            and change.old.hash_info\n            and not old.storage_map.cache_exists(change.old)\n        ):\n            ret[\"not in cache\"].append(\n                {\n                    \"path\": _path(change.old),\n                    \"hash\": _hash(change.old),\n                }\n            )\n\n    return ret if any(ret.values()) else {}\n\n\n@locked\ndef diff(\n    self,\n    a_rev: str = \"HEAD\",\n    b_rev: Optional[str] = None,\n    targets: Optional[List[str]] = None,\n    recursive: bool = False,\n):\n    \"\"\"\n    By default, it compares the workspace with the last commit's fs.\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    if self.scm.no_commits:\n        return {}\n\n    indexes = {}\n    missing_targets = defaultdict(set)\n    with_missing = False\n    if not b_rev:\n        b_rev = \"workspace\"\n        with_missing = True\n\n    for rev in self.brancher(revs=[a_rev, b_rev]):\n        if rev == \"workspace\" and b_rev != \"workspace\":\n            # brancher always returns workspace, but we only need to compute\n            # workspace paths/checksums if b_rev was None\n            continue\n\n        def onerror(target, _exc):\n            # pylint: disable-next=cell-var-from-loop\n            missing_targets[rev].add(target)  # noqa: B023\n\n        view = self.index.targets_view(\n            targets,\n            onerror=onerror,\n            recursive=recursive,\n        )\n\n        if rev == \"workspace\":\n            from .index import build_data_index\n\n            with ui.status(\"Building workspace index\"):\n                data = build_data_index(\n                    view,\n                    self.root_dir,\n                    self.fs,\n                    compute_hash=True,\n                )\n        else:\n            data = view.data[\"repo\"]\n\n        assert rev not in indexes\n        indexes[rev] = data\n\n    if targets:\n        old_missing = missing_targets.get(a_rev, set())\n        new_missing = missing_targets.get(b_rev, set())\n\n        # check for overlapping missing targets between a_rev and b_rev\n        for target in old_missing & new_missing:\n            raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), target)\n\n    if len(indexes.keys()) == 1:\n        # both a_rev and b_rev point to the same sha, nothing to compare\n        old = None\n        new = None\n    else:\n        old = indexes[a_rev]\n        new = indexes[b_rev]\n\n    with ui.status(\"Calculating diff\"):\n        return _diff(old, new, with_missing=with_missing)\n", "path": "dvc/repo/diff.py"}]}