@@ -107,38 +107,68 @@ def exclude_paths(root, patterns, dockerfile=None):
107
107
108
108
exclude_patterns = list (set (patterns ) - set (exceptions ))
109
109
110
- all_paths = get_paths (root )
111
-
112
- # Remove all paths that are matched by any exclusion pattern
113
- paths = [
114
- p for p in all_paths
115
- if not any (match_path (p , pattern ) for pattern in exclude_patterns )
116
- ]
117
-
118
- # Add back the set of paths that are matched by any inclusion pattern.
119
- # Include parent dirs - if we add back 'foo/bar', add 'foo' as well
120
- for p in all_paths :
121
- if any (match_path (p , pattern ) for pattern in include_patterns ):
122
- components = p .split ('/' )
123
- paths += [
124
- '/' .join (components [:end ])
125
- for end in range (1 , len (components ) + 1 )
126
- ]
110
+ paths = get_paths (root , exclude_patterns , include_patterns ,
111
+ has_exceptions = len (exceptions ) > 0 )
127
112
128
113
return set (paths )
129
114
130
115
131
- def get_paths (root ):
116
+ def should_include (path , exclude_patterns , include_patterns ):
117
+ """
118
+ Given a path, a list of exclude patterns, and a list of inclusion patterns:
119
+
120
+ 1. Returns True if the path doesn't match any exclusion pattern
121
+ 2. Returns False if the path matches an exclusion pattern and doesn't match
122
+ an inclusion pattern
123
+ 3. Returns true if the path matches an exclusion pattern and matches an
124
+ inclusion pattern
125
+ """
126
+ for pattern in exclude_patterns :
127
+ if match_path (path , pattern ):
128
+ for pattern in include_patterns :
129
+ if match_path (path , pattern ):
130
+ return True
131
+ return False
132
+ return True
133
+
134
+
135
+ def get_paths (root , exclude_patterns , include_patterns , has_exceptions = False ):
132
136
paths = []
133
137
134
- for parent , dirs , files in os .walk (root , followlinks = False ):
138
+ for parent , dirs , files in os .walk (root , topdown = True , followlinks = False ):
135
139
parent = os .path .relpath (parent , root )
136
140
if parent == '.' :
137
141
parent = ''
142
+
143
+ # If exception rules exist, we can't skip recursing into ignored
144
+ # directories, as we need to look for exceptions in them.
145
+ #
146
+ # It may be possible to optimize this further for exception patterns
147
+ # that *couldn't* match within ignored directores.
148
+ #
149
+ # This matches the current docker logic (as of 2015-11-24):
150
+ # https://github.com/docker/docker/blob/37ba67bf636b34dc5c0c0265d62a089d0492088f/pkg/archive/archive.go#L555-L557
151
+
152
+ if not has_exceptions :
153
+
154
+ # Remove excluded patterns from the list of directories to traverse
155
+ # by mutating the dirs we're iterating over.
156
+ # This looks strange, but is considered the correct way to skip
157
+ # traversal. See https://docs.python.org/2/library/os.html#os.walk
158
+
159
+ dirs [:] = [d for d in dirs if
160
+ should_include (os .path .join (parent , d ),
161
+ exclude_patterns , include_patterns )]
162
+
138
163
for path in dirs :
139
- paths .append (os .path .join (parent , path ))
164
+ if should_include (os .path .join (parent , path ),
165
+ exclude_patterns , include_patterns ):
166
+ paths .append (os .path .join (parent , path ))
167
+
140
168
for path in files :
141
- paths .append (os .path .join (parent , path ))
169
+ if should_include (os .path .join (parent , path ),
170
+ exclude_patterns , include_patterns ):
171
+ paths .append (os .path .join (parent , path ))
142
172
143
173
return paths
144
174
0 commit comments