19 |
21 |
20 CB_MANIFEST_FILE = b'clonebundles.manifest' |
22 CB_MANIFEST_FILE = b'clonebundles.manifest' |
21 |
23 |
22 |
24 |
23 @attr.s |
25 @attr.s |
24 class bundlespec(object): |
26 class bundlespec: |
25 compression = attr.ib() |
27 compression = attr.ib() |
26 wirecompression = attr.ib() |
28 wirecompression = attr.ib() |
27 version = attr.ib() |
29 version = attr.ib() |
28 wireversion = attr.ib() |
30 wireversion = attr.ib() |
29 params = attr.ib() |
31 # parameters explicitly overwritten by the config or the specification |
30 contentopts = attr.ib() |
32 _explicit_params = attr.ib() |
|
33 # default parameter for the version |
|
34 # |
|
35 # Keeping it separated is useful to check what was actually overwritten. |
|
36 _default_opts = attr.ib() |
|
37 |
|
38 @property |
|
39 def params(self): |
|
40 return collections.ChainMap(self._explicit_params, self._default_opts) |
|
41 |
|
42 @property |
|
43 def contentopts(self): |
|
44 # kept for Backward Compatibility concerns. |
|
45 return self.params |
|
46 |
|
47 def set_param(self, key, value, overwrite=True): |
|
48 """Set a bundle parameter value. |
|
49 |
|
50 Will only overwrite if overwrite is true""" |
|
51 if overwrite or key not in self._explicit_params: |
|
52 self._explicit_params[key] = value |
31 |
53 |
32 |
54 |
33 # Maps bundle version human names to changegroup versions. |
55 # Maps bundle version human names to changegroup versions. |
34 _bundlespeccgversions = { |
56 _bundlespeccgversions = { |
35 b'v1': b'01', |
57 b'v1': b'01', |
54 b'obsolescence': False, |
76 b'obsolescence': False, |
55 b'phases': False, |
77 b'phases': False, |
56 b'tagsfnodescache': True, |
78 b'tagsfnodescache': True, |
57 b'revbranchcache': True, |
79 b'revbranchcache': True, |
58 }, |
80 }, |
59 b'packed1': {b'cg.version': b's1'}, |
81 b'streamv2': { |
|
82 b'changegroup': False, |
|
83 b'cg.version': b'02', |
|
84 b'obsolescence': False, |
|
85 b'phases': False, |
|
86 b"streamv2": True, |
|
87 b'tagsfnodescache': False, |
|
88 b'revbranchcache': False, |
|
89 }, |
|
90 b'packed1': { |
|
91 b'cg.version': b's1', |
|
92 }, |
|
93 b'bundle2': { # legacy |
|
94 b'cg.version': b'02', |
|
95 }, |
60 } |
96 } |
61 _bundlespeccontentopts[b'bundle2'] = _bundlespeccontentopts[b'v2'] |
97 _bundlespeccontentopts[b'bundle2'] = _bundlespeccontentopts[b'v2'] |
62 |
98 |
63 _bundlespecvariants = { |
99 _bundlespecvariants = {b"streamv2": {}} |
64 b"streamv2": { |
|
65 b"changegroup": False, |
|
66 b"streamv2": True, |
|
67 b"tagsfnodescache": False, |
|
68 b"revbranchcache": False, |
|
69 } |
|
70 } |
|
71 |
100 |
72 # Compression engines allowed in version 1. THIS SHOULD NEVER CHANGE. |
101 # Compression engines allowed in version 1. THIS SHOULD NEVER CHANGE. |
73 _bundlespecv1compengines = {b'gzip', b'bzip2', b'none'} |
102 _bundlespecv1compengines = {b'gzip', b'bzip2', b'none'} |
|
103 |
|
104 |
|
105 def param_bool(key, value): |
|
106 """make a boolean out of a parameter value""" |
|
107 b = stringutil.parsebool(value) |
|
108 if b is None: |
|
109 msg = _(b"parameter %s should be a boolean ('%s')") |
|
110 msg %= (key, value) |
|
111 raise error.InvalidBundleSpecification(msg) |
|
112 return b |
|
113 |
|
114 |
|
115 # mapping of known parameter name need their value processed |
|
116 bundle_spec_param_processing = { |
|
117 b"obsolescence": param_bool, |
|
118 b"obsolescence-mandatory": param_bool, |
|
119 b"phases": param_bool, |
|
120 } |
|
121 |
|
122 |
|
123 def _parseparams(s): |
|
124 """parse bundlespec parameter section |
|
125 |
|
126 input: "comp-version;params" string |
|
127 |
|
128 return: (spec; {param_key: param_value}) |
|
129 """ |
|
130 if b';' not in s: |
|
131 return s, {} |
|
132 |
|
133 params = {} |
|
134 version, paramstr = s.split(b';', 1) |
|
135 |
|
136 err = _(b'invalid bundle specification: missing "=" in parameter: %s') |
|
137 for p in paramstr.split(b';'): |
|
138 if b'=' not in p: |
|
139 msg = err % p |
|
140 raise error.InvalidBundleSpecification(msg) |
|
141 |
|
142 key, value = p.split(b'=', 1) |
|
143 key = urlreq.unquote(key) |
|
144 value = urlreq.unquote(value) |
|
145 process = bundle_spec_param_processing.get(key) |
|
146 if process is not None: |
|
147 value = process(key, value) |
|
148 params[key] = value |
|
149 |
|
150 return version, params |
74 |
151 |
75 |
152 |
76 def parsebundlespec(repo, spec, strict=True): |
153 def parsebundlespec(repo, spec, strict=True): |
77 """Parse a bundle string specification into parts. |
154 """Parse a bundle string specification into parts. |
78 |
155 |
104 bundle type/version is not recognized. |
181 bundle type/version is not recognized. |
105 |
182 |
106 Note: this function will likely eventually return a more complex data |
183 Note: this function will likely eventually return a more complex data |
107 structure, including bundle2 part information. |
184 structure, including bundle2 part information. |
108 """ |
185 """ |
109 |
|
110 def parseparams(s): |
|
111 if b';' not in s: |
|
112 return s, {} |
|
113 |
|
114 params = {} |
|
115 version, paramstr = s.split(b';', 1) |
|
116 |
|
117 for p in paramstr.split(b';'): |
|
118 if b'=' not in p: |
|
119 raise error.InvalidBundleSpecification( |
|
120 _( |
|
121 b'invalid bundle specification: ' |
|
122 b'missing "=" in parameter: %s' |
|
123 ) |
|
124 % p |
|
125 ) |
|
126 |
|
127 key, value = p.split(b'=', 1) |
|
128 key = urlreq.unquote(key) |
|
129 value = urlreq.unquote(value) |
|
130 params[key] = value |
|
131 |
|
132 return version, params |
|
133 |
|
134 if strict and b'-' not in spec: |
186 if strict and b'-' not in spec: |
135 raise error.InvalidBundleSpecification( |
187 raise error.InvalidBundleSpecification( |
136 _( |
188 _( |
137 b'invalid bundle specification; ' |
189 b'invalid bundle specification; ' |
138 b'must be prefixed with compression: %s' |
190 b'must be prefixed with compression: %s' |
139 ) |
191 ) |
140 % spec |
192 % spec |
141 ) |
193 ) |
142 |
194 |
143 if b'-' in spec: |
195 pre_args = spec.split(b';', 1)[0] |
|
196 if b'-' in pre_args: |
144 compression, version = spec.split(b'-', 1) |
197 compression, version = spec.split(b'-', 1) |
145 |
198 |
146 if compression not in util.compengines.supportedbundlenames: |
199 if compression not in util.compengines.supportedbundlenames: |
147 raise error.UnsupportedBundleSpecification( |
200 raise error.UnsupportedBundleSpecification( |
148 _(b'%s compression is not supported') % compression |
201 _(b'%s compression is not supported') % compression |
149 ) |
202 ) |
150 |
203 |
151 version, params = parseparams(version) |
204 version, params = _parseparams(version) |
152 |
205 |
153 if version not in _bundlespeccgversions: |
206 if version not in _bundlespeccontentopts: |
154 raise error.UnsupportedBundleSpecification( |
207 raise error.UnsupportedBundleSpecification( |
155 _(b'%s is not a recognized bundle version') % version |
208 _(b'%s is not a recognized bundle version') % version |
156 ) |
209 ) |
157 else: |
210 else: |
158 # Value could be just the compression or just the version, in which |
211 # Value could be just the compression or just the version, in which |
159 # case some defaults are assumed (but only when not in strict mode). |
212 # case some defaults are assumed (but only when not in strict mode). |
160 assert not strict |
213 assert not strict |
161 |
214 |
162 spec, params = parseparams(spec) |
215 spec, params = _parseparams(spec) |
163 |
216 |
164 if spec in util.compengines.supportedbundlenames: |
217 if spec in util.compengines.supportedbundlenames: |
165 compression = spec |
218 compression = spec |
166 version = b'v1' |
219 version = b'v1' |
167 # Generaldelta repos require v2. |
220 # Generaldelta repos require v2. |
170 elif requirementsmod.REVLOGV2_REQUIREMENT in repo.requirements: |
223 elif requirementsmod.REVLOGV2_REQUIREMENT in repo.requirements: |
171 version = b'v2' |
224 version = b'v2' |
172 # Modern compression engines require v2. |
225 # Modern compression engines require v2. |
173 if compression not in _bundlespecv1compengines: |
226 if compression not in _bundlespecv1compengines: |
174 version = b'v2' |
227 version = b'v2' |
175 elif spec in _bundlespeccgversions: |
228 elif spec in _bundlespeccontentopts: |
176 if spec == b'packed1': |
229 if spec == b'packed1': |
177 compression = b'none' |
230 compression = b'none' |
178 else: |
231 else: |
179 compression = b'bzip2' |
232 compression = b'bzip2' |
180 version = spec |
233 version = spec |
201 _(b'missing support for repository features: %s') |
254 _(b'missing support for repository features: %s') |
202 % b', '.join(sorted(missingreqs)) |
255 % b', '.join(sorted(missingreqs)) |
203 ) |
256 ) |
204 |
257 |
205 # Compute contentopts based on the version |
258 # Compute contentopts based on the version |
|
259 if b"stream" in params and params[b"stream"] == b"v2": |
|
260 # That case is fishy as this mostly derails the version selection |
|
261 # mechanism. `stream` bundles are quite specific and used differently |
|
262 # as "normal" bundles. |
|
263 # |
|
264 # So we are pinning this to "v2", as this will likely be |
|
265 # compatible forever. (see the next conditional). |
|
266 # |
|
267 # (we should probably define a cleaner way to do this and raise a |
|
268 # warning when the old way is encounter) |
|
269 version = b"streamv2" |
206 contentopts = _bundlespeccontentopts.get(version, {}).copy() |
270 contentopts = _bundlespeccontentopts.get(version, {}).copy() |
207 |
271 if version == b"streamv2": |
208 # Process the variants |
272 # streamv2 have been reported as "v2" for a while. |
209 if b"stream" in params and params[b"stream"] == b"v2": |
273 version = b"v2" |
210 variant = _bundlespecvariants[b"streamv2"] |
|
211 contentopts.update(variant) |
|
212 |
274 |
213 engine = util.compengines.forbundlename(compression) |
275 engine = util.compengines.forbundlename(compression) |
214 compression, wirecompression = engine.bundletype() |
276 compression, wirecompression = engine.bundletype() |
215 wireversion = _bundlespeccgversions[version] |
277 wireversion = _bundlespeccontentopts[version][b'cg.version'] |
216 |
278 |
217 return bundlespec( |
279 return bundlespec( |
218 compression, wirecompression, version, wireversion, params, contentopts |
280 compression, wirecompression, version, wireversion, params, contentopts |
219 ) |
281 ) |
220 |
282 |