Mercurial > hg
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) |