4
4
from typing import TYPE_CHECKING , Any , TypedDict , Union
5
5
6
6
from dvc .fs .callbacks import DEFAULT_CALLBACK
7
+ from dvc .repo .worktree import worktree_view
7
8
from dvc .ui import ui
8
9
9
10
if TYPE_CHECKING :
@@ -51,11 +52,8 @@ def _diff(
51
52
* ,
52
53
granular : bool = False ,
53
54
not_in_cache : bool = False ,
54
- not_in_remote : bool = False ,
55
- remote_refresh : bool = False ,
56
55
callback : "Callback" = DEFAULT_CALLBACK ,
57
56
) -> dict [str , list [str ]]:
58
- from dvc_data .index import StorageError
59
57
from dvc_data .index .diff import UNCHANGED , UNKNOWN , diff
60
58
61
59
ret : dict [str , list [str ]] = {}
@@ -97,19 +95,6 @@ def _add_change(typ, change):
97
95
# NOTE: emulating previous behaviour
98
96
_add_change ("not_in_cache" , change )
99
97
100
- try :
101
- if (
102
- not_in_remote
103
- and change .old
104
- and change .old .hash_info
105
- and not old .storage_map .remote_exists (
106
- change .old , refresh = remote_refresh
107
- )
108
- ):
109
- _add_change ("not_in_remote" , change )
110
- except StorageError :
111
- pass
112
-
113
98
_add_change (change .typ , change )
114
99
115
100
return ret
@@ -217,15 +202,57 @@ def _transform_git_paths_to_dvc(repo: "Repo", files: Iterable[str]) -> list[str]
217
202
return [repo .fs .relpath (file , start ) for file in files ]
218
203
219
204
220
- def status (repo : "Repo" , untracked_files : str = "no" , ** kwargs : Any ) -> Status :
205
+ def _get_entries_not_in_remote (
206
+ repo : "Repo" ,
207
+ granular : bool = False ,
208
+ remote_refresh : bool = False ,
209
+ ) -> list [str ]:
210
+ """Get entries that are not in remote storage."""
211
+ from dvc_data .index import StorageKeyError
212
+
213
+ # View into the index, with only pushable entries
214
+ view = worktree_view (repo .index , push = True ).data ["repo" ]
215
+
216
+ missing_entries = []
217
+ for key , entry in view .iteritems (shallow = not granular ):
218
+ if not (entry and entry .hash_info ):
219
+ continue
220
+
221
+ k = (* key , "" ) if entry .meta and entry .meta .isdir else key
222
+ try :
223
+ if not view .storage_map .remote_exists (entry , refresh = remote_refresh ):
224
+ missing_entries .append (os .path .sep .join (k ))
225
+ except StorageKeyError :
226
+ pass
227
+
228
+ return missing_entries
229
+
230
+
231
+ def status (
232
+ repo : "Repo" ,
233
+ untracked_files : str = "no" ,
234
+ not_in_remote : bool = False ,
235
+ remote_refresh : bool = False ,
236
+ granular : bool = False ,
237
+ head : str = "HEAD" ,
238
+ ) -> Status :
221
239
from dvc .scm import NoSCMError , SCMError
222
240
223
- head = kwargs .pop ("head" , "HEAD" )
224
- uncommitted_diff = _diff_index_to_wtree (repo , ** kwargs )
241
+ uncommitted_diff = _diff_index_to_wtree (repo , granular = granular )
225
242
unchanged = set (uncommitted_diff .pop ("unchanged" , []))
226
243
244
+ entries_not_in_remote = (
245
+ _get_entries_not_in_remote (
246
+ repo ,
247
+ granular = granular ,
248
+ remote_refresh = remote_refresh ,
249
+ )
250
+ if not_in_remote
251
+ else []
252
+ )
253
+
227
254
try :
228
- committed_diff = _diff_head_to_index (repo , head = head , ** kwargs )
255
+ committed_diff = _diff_head_to_index (repo , head = head , granular = granular )
229
256
except (SCMError , NoSCMError ):
230
257
committed_diff = {}
231
258
else :
@@ -234,10 +261,11 @@ def status(repo: "Repo", untracked_files: str = "no", **kwargs: Any) -> Status:
234
261
git_info = _git_info (repo .scm , untracked_files = untracked_files )
235
262
untracked = git_info .get ("untracked" , [])
236
263
untracked = _transform_git_paths_to_dvc (repo , untracked )
264
+
237
265
# order matters here
238
266
return Status (
239
267
not_in_cache = uncommitted_diff .pop ("not_in_cache" , []),
240
- not_in_remote = uncommitted_diff . pop ( "not_in_remote" , []) ,
268
+ not_in_remote = entries_not_in_remote ,
241
269
committed = committed_diff ,
242
270
uncommitted = uncommitted_diff ,
243
271
untracked = untracked ,
0 commit comments