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