| 23 | |
| 24 | |
| 25 | def _diff(old, new, data_keys, 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 | def meta_cmp_key(meta): |
| 38 | if not meta: |
| 39 | return meta |
| 40 | return meta.isdir |
| 41 | |
| 42 | for change in idiff( |
| 43 | old, |
| 44 | new, |
| 45 | with_renames=True, |
| 46 | meta_cmp_key=meta_cmp_key, |
| 47 | roots=data_keys, |
| 48 | # Include unknown entries from missing dir entry, so that they don't |
| 49 | # get reported as added/modified/deleted. |
| 50 | # Also return unchanged entries so that we can check if they are missing |
| 51 | # from cache. |
| 52 | with_unknown=True, |
| 53 | with_unchanged=with_missing, |
| 54 | ): |
| 55 | if (change.old and change.old.isdir and not change.old.hash_info) or ( |
| 56 | change.new and change.new.isdir and not change.new.hash_info |
| 57 | ): |
| 58 | continue |
| 59 | |
| 60 | if change.typ == ADD: |
| 61 | ret["added"].append({"path": _path(change.new), "hash": _hash(change.new)}) |
| 62 | elif change.typ == DELETE: |
| 63 | ret["deleted"].append( |
| 64 | {"path": _path(change.old), "hash": _hash(change.old)} |
| 65 | ) |
| 66 | elif change.typ == MODIFY: |
| 67 | ret["modified"].append( |
| 68 | { |
| 69 | "path": _path(change.old), |
| 70 | "hash": {"old": _hash(change.old), "new": _hash(change.new)}, |
| 71 | } |
| 72 | ) |
| 73 | elif change.typ == RENAME: |
| 74 | ret["renamed"].append( |
| 75 | { |
| 76 | "path": {"old": _path(change.old), "new": _path(change.new)}, |
| 77 | "hash": _hash(change.old), |
| 78 | } |
| 79 | ) |
| 80 | |
| 81 | if ( |
| 82 | with_missing |