comparison mercurial/narrowspec.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents 268662aac075
children 687b865b95ad
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
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 from .i18n import _ 10 from .i18n import _
11 from .interfaces import ( 11 from .interfaces import repository
12 repository,
13 )
14 from . import ( 12 from . import (
15 error, 13 error,
16 match as matchmod, 14 match as matchmod,
17 merge, 15 merge,
18 scmutil, 16 scmutil,
34 VALID_PREFIXES = ( 32 VALID_PREFIXES = (
35 b'path:', 33 b'path:',
36 b'rootfilesin:', 34 b'rootfilesin:',
37 ) 35 )
38 36
37
39 def normalizesplitpattern(kind, pat): 38 def normalizesplitpattern(kind, pat):
40 """Returns the normalized version of a pattern and kind. 39 """Returns the normalized version of a pattern and kind.
41 40
42 Returns a tuple with the normalized kind and normalized pattern. 41 Returns a tuple with the normalized kind and normalized pattern.
43 """ 42 """
44 pat = pat.rstrip('/') 43 pat = pat.rstrip('/')
45 _validatepattern(pat) 44 _validatepattern(pat)
46 return kind, pat 45 return kind, pat
46
47 47
48 def _numlines(s): 48 def _numlines(s):
49 """Returns the number of lines in s, including ending empty lines.""" 49 """Returns the number of lines in s, including ending empty lines."""
50 # We use splitlines because it is Unicode-friendly and thus Python 3 50 # We use splitlines because it is Unicode-friendly and thus Python 3
51 # compatible. However, it does not count empty lines at the end, so trick 51 # compatible. However, it does not count empty lines at the end, so trick
52 # it by adding a character at the end. 52 # it by adding a character at the end.
53 return len((s + 'x').splitlines()) 53 return len((s + 'x').splitlines())
54 54
55
55 def _validatepattern(pat): 56 def _validatepattern(pat):
56 """Validates the pattern and aborts if it is invalid. 57 """Validates the pattern and aborts if it is invalid.
57 58
58 Patterns are stored in the narrowspec as newline-separated 59 Patterns are stored in the narrowspec as newline-separated
59 POSIX-style bytestring paths. There's no escaping. 60 POSIX-style bytestring paths. There's no escaping.
66 67
67 components = pat.split('/') 68 components = pat.split('/')
68 if '.' in components or '..' in components: 69 if '.' in components or '..' in components:
69 raise error.Abort(_('"." and ".." are not allowed in narrowspec paths')) 70 raise error.Abort(_('"." and ".." are not allowed in narrowspec paths'))
70 71
72
71 def normalizepattern(pattern, defaultkind='path'): 73 def normalizepattern(pattern, defaultkind='path'):
72 """Returns the normalized version of a text-format pattern. 74 """Returns the normalized version of a text-format pattern.
73 75
74 If the pattern has no kind, the default will be added. 76 If the pattern has no kind, the default will be added.
75 """ 77 """
76 kind, pat = matchmod._patsplit(pattern, defaultkind) 78 kind, pat = matchmod._patsplit(pattern, defaultkind)
77 return '%s:%s' % normalizesplitpattern(kind, pat) 79 return '%s:%s' % normalizesplitpattern(kind, pat)
80
78 81
79 def parsepatterns(pats): 82 def parsepatterns(pats):
80 """Parses an iterable of patterns into a typed pattern set. 83 """Parses an iterable of patterns into a typed pattern set.
81 84
82 Patterns are assumed to be ``path:`` if no prefix is present. 85 Patterns are assumed to be ``path:`` if no prefix is present.
89 """ 92 """
90 res = {normalizepattern(orig) for orig in pats} 93 res = {normalizepattern(orig) for orig in pats}
91 validatepatterns(res) 94 validatepatterns(res)
92 return res 95 return res
93 96
97
94 def validatepatterns(pats): 98 def validatepatterns(pats):
95 """Validate that patterns are in the expected data structure and format. 99 """Validate that patterns are in the expected data structure and format.
96 100
97 And that is a set of normalized patterns beginning with ``path:`` or 101 And that is a set of normalized patterns beginning with ``path:`` or
98 ``rootfilesin:``. 102 ``rootfilesin:``.
100 This function should be used to validate internal data structures 104 This function should be used to validate internal data structures
101 and patterns that are loaded from sources that use the internal, 105 and patterns that are loaded from sources that use the internal,
102 prefixed pattern representation (but can't necessarily be fully trusted). 106 prefixed pattern representation (but can't necessarily be fully trusted).
103 """ 107 """
104 if not isinstance(pats, set): 108 if not isinstance(pats, set):
105 raise error.ProgrammingError('narrow patterns should be a set; ' 109 raise error.ProgrammingError(
106 'got %r' % pats) 110 'narrow patterns should be a set; ' 'got %r' % pats
111 )
107 112
108 for pat in pats: 113 for pat in pats:
109 if not pat.startswith(VALID_PREFIXES): 114 if not pat.startswith(VALID_PREFIXES):
110 # Use a Mercurial exception because this can happen due to user 115 # Use a Mercurial exception because this can happen due to user
111 # bugs (e.g. manually updating spec file). 116 # bugs (e.g. manually updating spec file).
112 raise error.Abort(_('invalid prefix on narrow pattern: %s') % pat, 117 raise error.Abort(
113 hint=_('narrow patterns must begin with one of ' 118 _('invalid prefix on narrow pattern: %s') % pat,
114 'the following: %s') % 119 hint=_(
115 ', '.join(VALID_PREFIXES)) 120 'narrow patterns must begin with one of '
121 'the following: %s'
122 )
123 % ', '.join(VALID_PREFIXES),
124 )
125
116 126
117 def format(includes, excludes): 127 def format(includes, excludes):
118 output = '[include]\n' 128 output = '[include]\n'
119 for i in sorted(includes - excludes): 129 for i in sorted(includes - excludes):
120 output += i + '\n' 130 output += i + '\n'
121 output += '[exclude]\n' 131 output += '[exclude]\n'
122 for e in sorted(excludes): 132 for e in sorted(excludes):
123 output += e + '\n' 133 output += e + '\n'
124 return output 134 return output
125 135
136
126 def match(root, include=None, exclude=None): 137 def match(root, include=None, exclude=None):
127 if not include: 138 if not include:
128 # Passing empty include and empty exclude to matchmod.match() 139 # Passing empty include and empty exclude to matchmod.match()
129 # gives a matcher that matches everything, so explicitly use 140 # gives a matcher that matches everything, so explicitly use
130 # the nevermatcher. 141 # the nevermatcher.
131 return matchmod.never() 142 return matchmod.never()
132 return matchmod.match(root, '', [], include=include or [], 143 return matchmod.match(
133 exclude=exclude or []) 144 root, '', [], include=include or [], exclude=exclude or []
145 )
146
134 147
135 def parseconfig(ui, spec): 148 def parseconfig(ui, spec):
136 # maybe we should care about the profiles returned too 149 # maybe we should care about the profiles returned too
137 includepats, excludepats, profiles = sparse.parseconfig(ui, spec, 'narrow') 150 includepats, excludepats, profiles = sparse.parseconfig(ui, spec, 'narrow')
138 if profiles: 151 if profiles:
139 raise error.Abort(_("including other spec files using '%include' is not" 152 raise error.Abort(
140 " supported in narrowspec")) 153 _(
154 "including other spec files using '%include' is not"
155 " supported in narrowspec"
156 )
157 )
141 158
142 validatepatterns(includepats) 159 validatepatterns(includepats)
143 validatepatterns(excludepats) 160 validatepatterns(excludepats)
144 161
145 return includepats, excludepats 162 return includepats, excludepats
163
146 164
147 def load(repo): 165 def load(repo):
148 # Treat "narrowspec does not exist" the same as "narrowspec file exists 166 # Treat "narrowspec does not exist" the same as "narrowspec file exists
149 # and is empty". 167 # and is empty".
150 spec = repo.svfs.tryread(FILENAME) 168 spec = repo.svfs.tryread(FILENAME)
151 return parseconfig(repo.ui, spec) 169 return parseconfig(repo.ui, spec)
152 170
171
153 def save(repo, includepats, excludepats): 172 def save(repo, includepats, excludepats):
154 validatepatterns(includepats) 173 validatepatterns(includepats)
155 validatepatterns(excludepats) 174 validatepatterns(excludepats)
156 spec = format(includepats, excludepats) 175 spec = format(includepats, excludepats)
157 repo.svfs.write(FILENAME, spec) 176 repo.svfs.write(FILENAME, spec)
158 177
178
159 def copytoworkingcopy(repo): 179 def copytoworkingcopy(repo):
160 spec = repo.svfs.read(FILENAME) 180 spec = repo.svfs.read(FILENAME)
161 repo.vfs.write(DIRSTATE_FILENAME, spec) 181 repo.vfs.write(DIRSTATE_FILENAME, spec)
162 182
183
163 def savebackup(repo, backupname): 184 def savebackup(repo, backupname):
164 if repository.NARROW_REQUIREMENT not in repo.requirements: 185 if repository.NARROW_REQUIREMENT not in repo.requirements:
165 return 186 return
166 svfs = repo.svfs 187 svfs = repo.svfs
167 svfs.tryunlink(backupname) 188 svfs.tryunlink(backupname)
168 util.copyfile(svfs.join(FILENAME), svfs.join(backupname), hardlink=True) 189 util.copyfile(svfs.join(FILENAME), svfs.join(backupname), hardlink=True)
169 190
191
170 def restorebackup(repo, backupname): 192 def restorebackup(repo, backupname):
171 if repository.NARROW_REQUIREMENT not in repo.requirements: 193 if repository.NARROW_REQUIREMENT not in repo.requirements:
172 return 194 return
173 util.rename(repo.svfs.join(backupname), repo.svfs.join(FILENAME)) 195 util.rename(repo.svfs.join(backupname), repo.svfs.join(FILENAME))
196
174 197
175 def savewcbackup(repo, backupname): 198 def savewcbackup(repo, backupname):
176 if repository.NARROW_REQUIREMENT not in repo.requirements: 199 if repository.NARROW_REQUIREMENT not in repo.requirements:
177 return 200 return
178 vfs = repo.vfs 201 vfs = repo.vfs
179 vfs.tryunlink(backupname) 202 vfs.tryunlink(backupname)
180 # It may not exist in old repos 203 # It may not exist in old repos
181 if vfs.exists(DIRSTATE_FILENAME): 204 if vfs.exists(DIRSTATE_FILENAME):
182 util.copyfile(vfs.join(DIRSTATE_FILENAME), vfs.join(backupname), 205 util.copyfile(
183 hardlink=True) 206 vfs.join(DIRSTATE_FILENAME), vfs.join(backupname), hardlink=True
207 )
208
184 209
185 def restorewcbackup(repo, backupname): 210 def restorewcbackup(repo, backupname):
186 if repository.NARROW_REQUIREMENT not in repo.requirements: 211 if repository.NARROW_REQUIREMENT not in repo.requirements:
187 return 212 return
188 # It may not exist in old repos 213 # It may not exist in old repos
189 if repo.vfs.exists(backupname): 214 if repo.vfs.exists(backupname):
190 util.rename(repo.vfs.join(backupname), repo.vfs.join(DIRSTATE_FILENAME)) 215 util.rename(repo.vfs.join(backupname), repo.vfs.join(DIRSTATE_FILENAME))
191 216
217
192 def clearwcbackup(repo, backupname): 218 def clearwcbackup(repo, backupname):
193 if repository.NARROW_REQUIREMENT not in repo.requirements: 219 if repository.NARROW_REQUIREMENT not in repo.requirements:
194 return 220 return
195 repo.vfs.tryunlink(backupname) 221 repo.vfs.tryunlink(backupname)
222
196 223
197 def restrictpatterns(req_includes, req_excludes, repo_includes, repo_excludes): 224 def restrictpatterns(req_includes, req_excludes, repo_includes, repo_excludes):
198 r""" Restricts the patterns according to repo settings, 225 r""" Restricts the patterns according to repo settings,
199 results in a logical AND operation 226 results in a logical AND operation
200 227
245 res_includes = set(res_includes) 272 res_includes = set(res_includes)
246 else: 273 else:
247 res_includes = set(req_includes) 274 res_includes = set(req_includes)
248 return res_includes, res_excludes, invalid_includes 275 return res_includes, res_excludes, invalid_includes
249 276
277
250 # These two are extracted for extensions (specifically for Google's CitC file 278 # These two are extracted for extensions (specifically for Google's CitC file
251 # system) 279 # system)
252 def _deletecleanfiles(repo, files): 280 def _deletecleanfiles(repo, files):
253 for f in files: 281 for f in files:
254 repo.wvfs.unlinkpath(f) 282 repo.wvfs.unlinkpath(f)
283
255 284
256 def _writeaddedfiles(repo, pctx, files): 285 def _writeaddedfiles(repo, pctx, files):
257 actions = merge.emptyactions() 286 actions = merge.emptyactions()
258 addgaction = actions[merge.ACTION_GET].append 287 addgaction = actions[merge.ACTION_GET].append
259 mf = repo['.'].manifest() 288 mf = repo['.'].manifest()
260 for f in files: 289 for f in files:
261 if not repo.wvfs.exists(f): 290 if not repo.wvfs.exists(f):
262 addgaction((f, (mf.flags(f), False), "narrowspec updated")) 291 addgaction((f, (mf.flags(f), False), "narrowspec updated"))
263 merge.applyupdates(repo, actions, wctx=repo[None], 292 merge.applyupdates(
264 mctx=repo['.'], overwrite=False, wantfiledata=False) 293 repo,
294 actions,
295 wctx=repo[None],
296 mctx=repo['.'],
297 overwrite=False,
298 wantfiledata=False,
299 )
300
265 301
266 def checkworkingcopynarrowspec(repo): 302 def checkworkingcopynarrowspec(repo):
267 # Avoid infinite recursion when updating the working copy 303 # Avoid infinite recursion when updating the working copy
268 if getattr(repo, '_updatingnarrowspec', False): 304 if getattr(repo, '_updatingnarrowspec', False):
269 return 305 return
270 storespec = repo.svfs.tryread(FILENAME) 306 storespec = repo.svfs.tryread(FILENAME)
271 wcspec = repo.vfs.tryread(DIRSTATE_FILENAME) 307 wcspec = repo.vfs.tryread(DIRSTATE_FILENAME)
272 if wcspec != storespec: 308 if wcspec != storespec:
273 raise error.Abort(_("working copy's narrowspec is stale"), 309 raise error.Abort(
274 hint=_("run 'hg tracked --update-working-copy'")) 310 _("working copy's narrowspec is stale"),
311 hint=_("run 'hg tracked --update-working-copy'"),
312 )
313
275 314
276 def updateworkingcopy(repo, assumeclean=False): 315 def updateworkingcopy(repo, assumeclean=False):
277 """updates the working copy and dirstate from the store narrowspec 316 """updates the working copy and dirstate from the store narrowspec
278 317
279 When assumeclean=True, files that are not known to be clean will also 318 When assumeclean=True, files that are not known to be clean will also
289 newmatch = match(repo.root, include=newincludes, exclude=newexcludes) 328 newmatch = match(repo.root, include=newincludes, exclude=newexcludes)
290 addedmatch = matchmod.differencematcher(newmatch, oldmatch) 329 addedmatch = matchmod.differencematcher(newmatch, oldmatch)
291 removedmatch = matchmod.differencematcher(oldmatch, newmatch) 330 removedmatch = matchmod.differencematcher(oldmatch, newmatch)
292 331
293 ds = repo.dirstate 332 ds = repo.dirstate
294 lookup, status = ds.status(removedmatch, subrepos=[], ignored=True, 333 lookup, status = ds.status(
295 clean=True, unknown=True) 334 removedmatch, subrepos=[], ignored=True, clean=True, unknown=True
335 )
296 trackeddirty = status.modified + status.added 336 trackeddirty = status.modified + status.added
297 clean = status.clean 337 clean = status.clean
298 if assumeclean: 338 if assumeclean:
299 assert not trackeddirty 339 assert not trackeddirty
300 clean.extend(lookup) 340 clean.extend(lookup)