##// END OF EJS Templates
narrowspec: remove the unused _parsestoredpatterns() function...
Pulkit Goyal -
r38876:794afa91 default
parent child Browse files
Show More
@@ -1,216 +1,193 b''
1 # narrowspec.py - methods for working with a narrow view of a repository
1 # narrowspec.py - methods for working with a narrow view of a repository
2 #
2 #
3 # Copyright 2017 Google, Inc.
3 # Copyright 2017 Google, Inc.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11
11
12 from .i18n import _
12 from .i18n import _
13 from . import (
13 from . import (
14 error,
14 error,
15 match as matchmod,
15 match as matchmod,
16 sparse,
16 sparse,
17 util,
17 util,
18 )
18 )
19
19
20 FILENAME = 'narrowspec'
20 FILENAME = 'narrowspec'
21
21
22 def _parsestoredpatterns(text):
23 """Parses the narrowspec format that's stored on disk."""
24 patlist = None
25 includepats = []
26 excludepats = []
27 for l in text.splitlines():
28 if l == '[includes]':
29 if patlist is None:
30 patlist = includepats
31 else:
32 raise error.Abort(_('narrowspec includes section must appear '
33 'at most once, before excludes'))
34 elif l == '[excludes]':
35 if patlist is not excludepats:
36 patlist = excludepats
37 else:
38 raise error.Abort(_('narrowspec excludes section must appear '
39 'at most once'))
40 else:
41 patlist.append(l)
42
43 return set(includepats), set(excludepats)
44
45 def parseserverpatterns(text):
22 def parseserverpatterns(text):
46 """Parses the narrowspec format that's returned by the server."""
23 """Parses the narrowspec format that's returned by the server."""
47 includepats = set()
24 includepats = set()
48 excludepats = set()
25 excludepats = set()
49
26
50 # We get one entry per line, in the format "<key> <value>".
27 # We get one entry per line, in the format "<key> <value>".
51 # It's OK for value to contain other spaces.
28 # It's OK for value to contain other spaces.
52 for kp in (l.split(' ', 1) for l in text.splitlines()):
29 for kp in (l.split(' ', 1) for l in text.splitlines()):
53 if len(kp) != 2:
30 if len(kp) != 2:
54 raise error.Abort(_('Invalid narrowspec pattern line: "%s"') % kp)
31 raise error.Abort(_('Invalid narrowspec pattern line: "%s"') % kp)
55 key = kp[0]
32 key = kp[0]
56 pat = kp[1]
33 pat = kp[1]
57 if key == 'include':
34 if key == 'include':
58 includepats.add(pat)
35 includepats.add(pat)
59 elif key == 'exclude':
36 elif key == 'exclude':
60 excludepats.add(pat)
37 excludepats.add(pat)
61 else:
38 else:
62 raise error.Abort(_('Invalid key "%s" in server response') % key)
39 raise error.Abort(_('Invalid key "%s" in server response') % key)
63
40
64 return includepats, excludepats
41 return includepats, excludepats
65
42
66 def normalizesplitpattern(kind, pat):
43 def normalizesplitpattern(kind, pat):
67 """Returns the normalized version of a pattern and kind.
44 """Returns the normalized version of a pattern and kind.
68
45
69 Returns a tuple with the normalized kind and normalized pattern.
46 Returns a tuple with the normalized kind and normalized pattern.
70 """
47 """
71 pat = pat.rstrip('/')
48 pat = pat.rstrip('/')
72 _validatepattern(pat)
49 _validatepattern(pat)
73 return kind, pat
50 return kind, pat
74
51
75 def _numlines(s):
52 def _numlines(s):
76 """Returns the number of lines in s, including ending empty lines."""
53 """Returns the number of lines in s, including ending empty lines."""
77 # We use splitlines because it is Unicode-friendly and thus Python 3
54 # We use splitlines because it is Unicode-friendly and thus Python 3
78 # compatible. However, it does not count empty lines at the end, so trick
55 # compatible. However, it does not count empty lines at the end, so trick
79 # it by adding a character at the end.
56 # it by adding a character at the end.
80 return len((s + 'x').splitlines())
57 return len((s + 'x').splitlines())
81
58
82 def _validatepattern(pat):
59 def _validatepattern(pat):
83 """Validates the pattern and aborts if it is invalid.
60 """Validates the pattern and aborts if it is invalid.
84
61
85 Patterns are stored in the narrowspec as newline-separated
62 Patterns are stored in the narrowspec as newline-separated
86 POSIX-style bytestring paths. There's no escaping.
63 POSIX-style bytestring paths. There's no escaping.
87 """
64 """
88
65
89 # We use newlines as separators in the narrowspec file, so don't allow them
66 # We use newlines as separators in the narrowspec file, so don't allow them
90 # in patterns.
67 # in patterns.
91 if _numlines(pat) > 1:
68 if _numlines(pat) > 1:
92 raise error.Abort(_('newlines are not allowed in narrowspec paths'))
69 raise error.Abort(_('newlines are not allowed in narrowspec paths'))
93
70
94 components = pat.split('/')
71 components = pat.split('/')
95 if '.' in components or '..' in components:
72 if '.' in components or '..' in components:
96 raise error.Abort(_('"." and ".." are not allowed in narrowspec paths'))
73 raise error.Abort(_('"." and ".." are not allowed in narrowspec paths'))
97
74
98 def normalizepattern(pattern, defaultkind='path'):
75 def normalizepattern(pattern, defaultkind='path'):
99 """Returns the normalized version of a text-format pattern.
76 """Returns the normalized version of a text-format pattern.
100
77
101 If the pattern has no kind, the default will be added.
78 If the pattern has no kind, the default will be added.
102 """
79 """
103 kind, pat = matchmod._patsplit(pattern, defaultkind)
80 kind, pat = matchmod._patsplit(pattern, defaultkind)
104 return '%s:%s' % normalizesplitpattern(kind, pat)
81 return '%s:%s' % normalizesplitpattern(kind, pat)
105
82
106 def parsepatterns(pats):
83 def parsepatterns(pats):
107 """Parses a list of patterns into a typed pattern set."""
84 """Parses a list of patterns into a typed pattern set."""
108 return set(normalizepattern(p) for p in pats)
85 return set(normalizepattern(p) for p in pats)
109
86
110 def format(includes, excludes):
87 def format(includes, excludes):
111 output = '[include]\n'
88 output = '[include]\n'
112 for i in sorted(includes - excludes):
89 for i in sorted(includes - excludes):
113 output += i + '\n'
90 output += i + '\n'
114 output += '[exclude]\n'
91 output += '[exclude]\n'
115 for e in sorted(excludes):
92 for e in sorted(excludes):
116 output += e + '\n'
93 output += e + '\n'
117 return output
94 return output
118
95
119 def match(root, include=None, exclude=None):
96 def match(root, include=None, exclude=None):
120 if not include:
97 if not include:
121 # Passing empty include and empty exclude to matchmod.match()
98 # Passing empty include and empty exclude to matchmod.match()
122 # gives a matcher that matches everything, so explicitly use
99 # gives a matcher that matches everything, so explicitly use
123 # the nevermatcher.
100 # the nevermatcher.
124 return matchmod.never(root, '')
101 return matchmod.never(root, '')
125 return matchmod.match(root, '', [], include=include or [],
102 return matchmod.match(root, '', [], include=include or [],
126 exclude=exclude or [])
103 exclude=exclude or [])
127
104
128 def needsexpansion(includes):
105 def needsexpansion(includes):
129 return [i for i in includes if i.startswith('include:')]
106 return [i for i in includes if i.startswith('include:')]
130
107
131 def load(repo):
108 def load(repo):
132 try:
109 try:
133 spec = repo.vfs.read(FILENAME)
110 spec = repo.vfs.read(FILENAME)
134 except IOError as e:
111 except IOError as e:
135 # Treat "narrowspec does not exist" the same as "narrowspec file exists
112 # Treat "narrowspec does not exist" the same as "narrowspec file exists
136 # and is empty".
113 # and is empty".
137 if e.errno == errno.ENOENT:
114 if e.errno == errno.ENOENT:
138 # Without this the next call to load will use the cached
115 # Without this the next call to load will use the cached
139 # non-existence of the file, which can cause some odd issues.
116 # non-existence of the file, which can cause some odd issues.
140 repo.invalidate(clearfilecache=True)
117 repo.invalidate(clearfilecache=True)
141 return set(), set()
118 return set(), set()
142 raise
119 raise
143 # maybe we should care about the profiles returned too
120 # maybe we should care about the profiles returned too
144 includepats, excludepats, profiles = sparse.parseconfig(repo.ui, spec,
121 includepats, excludepats, profiles = sparse.parseconfig(repo.ui, spec,
145 'narrow')
122 'narrow')
146 if profiles:
123 if profiles:
147 raise error.Abort(_("including other spec files using '%include' is not"
124 raise error.Abort(_("including other spec files using '%include' is not"
148 " suported in narrowspec"))
125 " suported in narrowspec"))
149 return includepats, excludepats
126 return includepats, excludepats
150
127
151 def save(repo, includepats, excludepats):
128 def save(repo, includepats, excludepats):
152 spec = format(includepats, excludepats)
129 spec = format(includepats, excludepats)
153 repo.vfs.write(FILENAME, spec)
130 repo.vfs.write(FILENAME, spec)
154
131
155 def savebackup(vfs, backupname):
132 def savebackup(vfs, backupname):
156 vfs.tryunlink(backupname)
133 vfs.tryunlink(backupname)
157 util.copyfile(vfs.join(FILENAME), vfs.join(backupname), hardlink=True)
134 util.copyfile(vfs.join(FILENAME), vfs.join(backupname), hardlink=True)
158
135
159 def restorebackup(vfs, backupname):
136 def restorebackup(vfs, backupname):
160 vfs.rename(backupname, FILENAME, checkambig=True)
137 vfs.rename(backupname, FILENAME, checkambig=True)
161
138
162 def clearbackup(vfs, backupname):
139 def clearbackup(vfs, backupname):
163 vfs.unlink(backupname)
140 vfs.unlink(backupname)
164
141
165 def restrictpatterns(req_includes, req_excludes, repo_includes, repo_excludes):
142 def restrictpatterns(req_includes, req_excludes, repo_includes, repo_excludes):
166 r""" Restricts the patterns according to repo settings,
143 r""" Restricts the patterns according to repo settings,
167 results in a logical AND operation
144 results in a logical AND operation
168
145
169 :param req_includes: requested includes
146 :param req_includes: requested includes
170 :param req_excludes: requested excludes
147 :param req_excludes: requested excludes
171 :param repo_includes: repo includes
148 :param repo_includes: repo includes
172 :param repo_excludes: repo excludes
149 :param repo_excludes: repo excludes
173 :return: include patterns, exclude patterns, and invalid include patterns.
150 :return: include patterns, exclude patterns, and invalid include patterns.
174
151
175 >>> restrictpatterns({'f1','f2'}, {}, ['f1'], [])
152 >>> restrictpatterns({'f1','f2'}, {}, ['f1'], [])
176 (set(['f1']), {}, [])
153 (set(['f1']), {}, [])
177 >>> restrictpatterns({'f1'}, {}, ['f1','f2'], [])
154 >>> restrictpatterns({'f1'}, {}, ['f1','f2'], [])
178 (set(['f1']), {}, [])
155 (set(['f1']), {}, [])
179 >>> restrictpatterns({'f1/fc1', 'f3/fc3'}, {}, ['f1','f2'], [])
156 >>> restrictpatterns({'f1/fc1', 'f3/fc3'}, {}, ['f1','f2'], [])
180 (set(['f1/fc1']), {}, [])
157 (set(['f1/fc1']), {}, [])
181 >>> restrictpatterns({'f1_fc1'}, {}, ['f1','f2'], [])
158 >>> restrictpatterns({'f1_fc1'}, {}, ['f1','f2'], [])
182 ([], set(['path:.']), [])
159 ([], set(['path:.']), [])
183 >>> restrictpatterns({'f1/../f2/fc2'}, {}, ['f1','f2'], [])
160 >>> restrictpatterns({'f1/../f2/fc2'}, {}, ['f1','f2'], [])
184 (set(['f2/fc2']), {}, [])
161 (set(['f2/fc2']), {}, [])
185 >>> restrictpatterns({'f1/../f3/fc3'}, {}, ['f1','f2'], [])
162 >>> restrictpatterns({'f1/../f3/fc3'}, {}, ['f1','f2'], [])
186 ([], set(['path:.']), [])
163 ([], set(['path:.']), [])
187 >>> restrictpatterns({'f1/$non_exitent_var'}, {}, ['f1','f2'], [])
164 >>> restrictpatterns({'f1/$non_exitent_var'}, {}, ['f1','f2'], [])
188 (set(['f1/$non_exitent_var']), {}, [])
165 (set(['f1/$non_exitent_var']), {}, [])
189 """
166 """
190 res_excludes = set(req_excludes)
167 res_excludes = set(req_excludes)
191 res_excludes.update(repo_excludes)
168 res_excludes.update(repo_excludes)
192 invalid_includes = []
169 invalid_includes = []
193 if not req_includes:
170 if not req_includes:
194 res_includes = set(repo_includes)
171 res_includes = set(repo_includes)
195 elif 'path:.' not in repo_includes:
172 elif 'path:.' not in repo_includes:
196 res_includes = []
173 res_includes = []
197 for req_include in req_includes:
174 for req_include in req_includes:
198 req_include = util.expandpath(util.normpath(req_include))
175 req_include = util.expandpath(util.normpath(req_include))
199 if req_include in repo_includes:
176 if req_include in repo_includes:
200 res_includes.append(req_include)
177 res_includes.append(req_include)
201 continue
178 continue
202 valid = False
179 valid = False
203 for repo_include in repo_includes:
180 for repo_include in repo_includes:
204 if req_include.startswith(repo_include + '/'):
181 if req_include.startswith(repo_include + '/'):
205 valid = True
182 valid = True
206 res_includes.append(req_include)
183 res_includes.append(req_include)
207 break
184 break
208 if not valid:
185 if not valid:
209 invalid_includes.append(req_include)
186 invalid_includes.append(req_include)
210 if len(res_includes) == 0:
187 if len(res_includes) == 0:
211 res_excludes = {'path:.'}
188 res_excludes = {'path:.'}
212 else:
189 else:
213 res_includes = set(res_includes)
190 res_includes = set(res_includes)
214 else:
191 else:
215 res_includes = set(req_includes)
192 res_includes = set(req_includes)
216 return res_includes, res_excludes, invalid_includes
193 return res_includes, res_excludes, invalid_includes
General Comments 0
You need to be logged in to leave comments. Login now