By default, it compares the workspace with the last commit's fs. 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`.
(
self,
a_rev: str = "HEAD",
b_rev: Optional[str] = None,
targets: Optional[list[str]] = None,
recursive: bool = False,
)
| 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 | data_keys = set() |
| 120 | for rev in self.brancher(revs=[a_rev, b_rev]): |
| 121 | if rev == "workspace" and b_rev != "workspace": |
| 122 | # brancher always returns workspace, but we only need to compute |
| 123 | # workspace paths/checksums if b_rev was None |
| 124 | continue |
| 125 | |
| 126 | def onerror(target, _exc): |
| 127 | missing_targets[rev].add(target) # noqa: B023 |
| 128 | |
| 129 | view = self.index.targets_view(targets, onerror=onerror, recursive=recursive) |
| 130 | |
| 131 | data_keys.update(view.data_keys.get("repo", set())) |
| 132 | |
| 133 | if rev == "workspace": |
| 134 | from .index import build_data_index |
| 135 | |
| 136 | with ui.status("Building workspace index"): |
| 137 | data = build_data_index(view, self.root_dir, self.fs, compute_hash=True) |
| 138 | else: |
| 139 | data = view.data["repo"] |
| 140 | |
| 141 | assert rev not in indexes |
| 142 | indexes[rev] = data |
| 143 | |
| 144 | if targets: |
| 145 | old_missing = missing_targets.get(a_rev, set()) |
| 146 | new_missing = missing_targets.get(b_rev, set()) |
| 147 | |
| 148 | # check for overlapping missing targets between a_rev and b_rev |
| 149 | for target in old_missing & new_missing: |
| 150 | raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), target) |
| 151 | |
| 152 | if len(indexes.keys()) == 1: |
no test coverage detected