5
5
from pathspec import PathSpec
6
6
from pathspec .patterns import GitWildMatchPattern
7
7
8
+ from dvc .path_info import PathInfo
8
9
from dvc .scm .tree import BaseTree
10
+ from dvc .utils import relpath
9
11
10
12
logger = logging .getLogger (__name__ )
11
13
@@ -125,19 +127,79 @@ def tree_root(self):
125
127
return self .tree .tree_root
126
128
127
129
def open (self , path , mode = "r" , encoding = "utf-8" ):
128
- return self .tree .open (path , mode , encoding )
130
+ if self .isfile (path ):
131
+ return self .tree .open (path , mode , encoding )
132
+ raise FileNotFoundError
129
133
130
134
def exists (self , path ):
131
- return self .tree .exists (path )
135
+ if self .tree .exists (path ) and self ._parents_exist (path ):
136
+ if self .tree .isdir (path ):
137
+ return self ._valid_dirname (path )
138
+ return self ._valid_filename (path )
139
+ return False
132
140
133
141
def isdir (self , path ):
134
- return self .tree .isdir (path )
142
+ return (
143
+ self .tree .isdir (path )
144
+ and self ._parents_exist (path )
145
+ and self ._valid_dirname (path )
146
+ )
147
+
148
+ def _valid_dirname (self , path ):
149
+ dirname , basename = os .path .split (os .path .normpath (path ))
150
+ dirs , _ = self .dvcignore (os .path .abspath (dirname ), [basename ], [])
151
+ if dirs :
152
+ return True
153
+ return False
135
154
136
155
def isfile (self , path ):
137
- return self .tree .isfile (path )
156
+ return (
157
+ self .tree .isfile (path )
158
+ and self ._parents_exist (path )
159
+ and self ._valid_filename (path )
160
+ )
161
+
162
+ def _valid_filename (self , path ):
163
+ dirname , basename = os .path .split (os .path .normpath (path ))
164
+ _ , files = self .dvcignore (os .path .abspath (dirname ), [], [basename ])
165
+ if files :
166
+ return True
167
+ return False
138
168
139
169
def isexec (self , path ):
140
- return self .tree .isexec (path )
170
+ return self .exists (path ) and self .tree .isexec (path )
171
+
172
+ def _parents_exist (self , path ):
173
+ from dvc .repo import Repo
174
+
175
+ path = PathInfo (path )
176
+
177
+ # if parent is tree_root or inside a .dvc dir we can skip this check
178
+ if path .parent == self .tree_root or Repo .DVC_DIR in path .parts :
179
+ return True
180
+
181
+ # if path is outside of tree, assume this is a local remote/local cache
182
+ # link/move operation where we do not need to filter ignores
183
+ path = relpath (path , self .tree_root )
184
+ if path .startswith (".." ) or (
185
+ os .name == "nt"
186
+ and not os .path .commonprefix (
187
+ [os .path .abspath (path ), self .tree_root ]
188
+ )
189
+ ):
190
+ return True
191
+
192
+ # check if parent directories are in our ignores, starting from
193
+ # tree_root
194
+ for parent_dir in reversed (PathInfo (path ).parents ):
195
+ dirname , basename = os .path .split (parent_dir )
196
+ if basename == "." :
197
+ # parent_dir == tree_root
198
+ continue
199
+ dirs , _ = self .dvcignore (os .path .abspath (dirname ), [basename ], [])
200
+ if not dirs :
201
+ return False
202
+ return True
141
203
142
204
def walk (self , top , topdown = True ):
143
205
for root , dirs , files in self .tree .walk (top , topdown ):
@@ -148,4 +210,6 @@ def walk(self, top, topdown=True):
148
210
yield root , dirs , files
149
211
150
212
def stat (self , path ):
151
- return self .tree .stat (path )
213
+ if self .exists (path ):
214
+ return self .tree .stat (path )
215
+ raise FileNotFoundError
0 commit comments