##// END OF EJS Templates
narrowspec: remove parseserverpatterns() which isn't used anymore...
Yuya Nishihara -
r39592:c8ea5c7e default
parent child Browse files
Show More
@@ -1,244 +1,223 b''
1 1 # narrowspec.py - methods for working with a narrow view of a repository
2 2 #
3 3 # Copyright 2017 Google, Inc.
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import errno
11 11
12 12 from .i18n import _
13 13 from . import (
14 14 error,
15 15 match as matchmod,
16 16 repository,
17 17 sparse,
18 18 util,
19 19 )
20 20
21 21 FILENAME = 'narrowspec'
22 22
23 23 # Pattern prefixes that are allowed in narrow patterns. This list MUST
24 24 # only contain patterns that are fast and safe to evaluate. Keep in mind
25 25 # that patterns are supplied by clients and executed on remote servers
26 26 # as part of wire protocol commands.
27 27 VALID_PREFIXES = (
28 28 b'path:',
29 29 b'rootfilesin:',
30 30 )
31 31
32 def parseserverpatterns(text):
33 """Parses the narrowspec format that's returned by the server."""
34 includepats = set()
35 excludepats = set()
36
37 # We get one entry per line, in the format "<key> <value>".
38 # It's OK for value to contain other spaces.
39 for kp in (l.split(' ', 1) for l in text.splitlines()):
40 if len(kp) != 2:
41 raise error.Abort(_('Invalid narrowspec pattern line: "%s"') % kp)
42 key = kp[0]
43 pat = kp[1]
44 if key == 'include':
45 includepats.add(pat)
46 elif key == 'exclude':
47 excludepats.add(pat)
48 else:
49 raise error.Abort(_('Invalid key "%s" in server response') % key)
50
51 return includepats, excludepats
52
53 32 def normalizesplitpattern(kind, pat):
54 33 """Returns the normalized version of a pattern and kind.
55 34
56 35 Returns a tuple with the normalized kind and normalized pattern.
57 36 """
58 37 pat = pat.rstrip('/')
59 38 _validatepattern(pat)
60 39 return kind, pat
61 40
62 41 def _numlines(s):
63 42 """Returns the number of lines in s, including ending empty lines."""
64 43 # We use splitlines because it is Unicode-friendly and thus Python 3
65 44 # compatible. However, it does not count empty lines at the end, so trick
66 45 # it by adding a character at the end.
67 46 return len((s + 'x').splitlines())
68 47
69 48 def _validatepattern(pat):
70 49 """Validates the pattern and aborts if it is invalid.
71 50
72 51 Patterns are stored in the narrowspec as newline-separated
73 52 POSIX-style bytestring paths. There's no escaping.
74 53 """
75 54
76 55 # We use newlines as separators in the narrowspec file, so don't allow them
77 56 # in patterns.
78 57 if _numlines(pat) > 1:
79 58 raise error.Abort(_('newlines are not allowed in narrowspec paths'))
80 59
81 60 components = pat.split('/')
82 61 if '.' in components or '..' in components:
83 62 raise error.Abort(_('"." and ".." are not allowed in narrowspec paths'))
84 63
85 64 def normalizepattern(pattern, defaultkind='path'):
86 65 """Returns the normalized version of a text-format pattern.
87 66
88 67 If the pattern has no kind, the default will be added.
89 68 """
90 69 kind, pat = matchmod._patsplit(pattern, defaultkind)
91 70 return '%s:%s' % normalizesplitpattern(kind, pat)
92 71
93 72 def parsepatterns(pats):
94 73 """Parses an iterable of patterns into a typed pattern set.
95 74
96 75 Patterns are assumed to be ``path:`` if no prefix is present.
97 76 For safety and performance reasons, only some prefixes are allowed.
98 77 See ``validatepatterns()``.
99 78
100 79 This function should be used on patterns that come from the user to
101 80 normalize and validate them to the internal data structure used for
102 81 representing patterns.
103 82 """
104 83 res = {normalizepattern(orig) for orig in pats}
105 84 validatepatterns(res)
106 85 return res
107 86
108 87 def validatepatterns(pats):
109 88 """Validate that patterns are in the expected data structure and format.
110 89
111 90 And that is a set of normalized patterns beginning with ``path:`` or
112 91 ``rootfilesin:``.
113 92
114 93 This function should be used to validate internal data structures
115 94 and patterns that are loaded from sources that use the internal,
116 95 prefixed pattern representation (but can't necessarily be fully trusted).
117 96 """
118 97 if not isinstance(pats, set):
119 98 raise error.ProgrammingError('narrow patterns should be a set; '
120 99 'got %r' % pats)
121 100
122 101 for pat in pats:
123 102 if not pat.startswith(VALID_PREFIXES):
124 103 # Use a Mercurial exception because this can happen due to user
125 104 # bugs (e.g. manually updating spec file).
126 105 raise error.Abort(_('invalid prefix on narrow pattern: %s') % pat,
127 106 hint=_('narrow patterns must begin with one of '
128 107 'the following: %s') %
129 108 ', '.join(VALID_PREFIXES))
130 109
131 110 def format(includes, excludes):
132 111 output = '[include]\n'
133 112 for i in sorted(includes - excludes):
134 113 output += i + '\n'
135 114 output += '[exclude]\n'
136 115 for e in sorted(excludes):
137 116 output += e + '\n'
138 117 return output
139 118
140 119 def match(root, include=None, exclude=None):
141 120 if not include:
142 121 # Passing empty include and empty exclude to matchmod.match()
143 122 # gives a matcher that matches everything, so explicitly use
144 123 # the nevermatcher.
145 124 return matchmod.never(root, '')
146 125 return matchmod.match(root, '', [], include=include or [],
147 126 exclude=exclude or [])
148 127
149 128 def load(repo):
150 129 try:
151 130 spec = repo.svfs.read(FILENAME)
152 131 except IOError as e:
153 132 # Treat "narrowspec does not exist" the same as "narrowspec file exists
154 133 # and is empty".
155 134 if e.errno == errno.ENOENT:
156 135 return set(), set()
157 136 raise
158 137 # maybe we should care about the profiles returned too
159 138 includepats, excludepats, profiles = sparse.parseconfig(repo.ui, spec,
160 139 'narrow')
161 140 if profiles:
162 141 raise error.Abort(_("including other spec files using '%include' is not"
163 142 " supported in narrowspec"))
164 143
165 144 validatepatterns(includepats)
166 145 validatepatterns(excludepats)
167 146
168 147 return includepats, excludepats
169 148
170 149 def save(repo, includepats, excludepats):
171 150 validatepatterns(includepats)
172 151 validatepatterns(excludepats)
173 152 spec = format(includepats, excludepats)
174 153 repo.svfs.write(FILENAME, spec)
175 154
176 155 def savebackup(repo, backupname):
177 156 if repository.NARROW_REQUIREMENT not in repo.requirements:
178 157 return
179 158 vfs = repo.vfs
180 159 vfs.tryunlink(backupname)
181 160 util.copyfile(repo.svfs.join(FILENAME), vfs.join(backupname), hardlink=True)
182 161
183 162 def restorebackup(repo, backupname):
184 163 if repository.NARROW_REQUIREMENT not in repo.requirements:
185 164 return
186 165 util.rename(repo.vfs.join(backupname), repo.svfs.join(FILENAME))
187 166
188 167 def clearbackup(repo, backupname):
189 168 if repository.NARROW_REQUIREMENT not in repo.requirements:
190 169 return
191 170 repo.vfs.unlink(backupname)
192 171
193 172 def restrictpatterns(req_includes, req_excludes, repo_includes, repo_excludes):
194 173 r""" Restricts the patterns according to repo settings,
195 174 results in a logical AND operation
196 175
197 176 :param req_includes: requested includes
198 177 :param req_excludes: requested excludes
199 178 :param repo_includes: repo includes
200 179 :param repo_excludes: repo excludes
201 180 :return: include patterns, exclude patterns, and invalid include patterns.
202 181
203 182 >>> restrictpatterns({'f1','f2'}, {}, ['f1'], [])
204 183 (set(['f1']), {}, [])
205 184 >>> restrictpatterns({'f1'}, {}, ['f1','f2'], [])
206 185 (set(['f1']), {}, [])
207 186 >>> restrictpatterns({'f1/fc1', 'f3/fc3'}, {}, ['f1','f2'], [])
208 187 (set(['f1/fc1']), {}, [])
209 188 >>> restrictpatterns({'f1_fc1'}, {}, ['f1','f2'], [])
210 189 ([], set(['path:.']), [])
211 190 >>> restrictpatterns({'f1/../f2/fc2'}, {}, ['f1','f2'], [])
212 191 (set(['f2/fc2']), {}, [])
213 192 >>> restrictpatterns({'f1/../f3/fc3'}, {}, ['f1','f2'], [])
214 193 ([], set(['path:.']), [])
215 194 >>> restrictpatterns({'f1/$non_exitent_var'}, {}, ['f1','f2'], [])
216 195 (set(['f1/$non_exitent_var']), {}, [])
217 196 """
218 197 res_excludes = set(req_excludes)
219 198 res_excludes.update(repo_excludes)
220 199 invalid_includes = []
221 200 if not req_includes:
222 201 res_includes = set(repo_includes)
223 202 elif 'path:.' not in repo_includes:
224 203 res_includes = []
225 204 for req_include in req_includes:
226 205 req_include = util.expandpath(util.normpath(req_include))
227 206 if req_include in repo_includes:
228 207 res_includes.append(req_include)
229 208 continue
230 209 valid = False
231 210 for repo_include in repo_includes:
232 211 if req_include.startswith(repo_include + '/'):
233 212 valid = True
234 213 res_includes.append(req_include)
235 214 break
236 215 if not valid:
237 216 invalid_includes.append(req_include)
238 217 if len(res_includes) == 0:
239 218 res_excludes = {'path:.'}
240 219 else:
241 220 res_includes = set(res_includes)
242 221 else:
243 222 res_includes = set(req_includes)
244 223 return res_includes, res_excludes, invalid_includes
General Comments 0
You need to be logged in to leave comments. Login now