##// END OF EJS Templates
config: also gather effect-flags on experimental.evolution...
Boris Feld -
r34903:cc977ec0 default
parent child Browse files
Show More
@@ -1,1133 +1,1134 b''
1 # configitems.py - centralized declaration of configuration option
1 # configitems.py - centralized declaration of configuration option
2 #
2 #
3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
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 functools
10 import functools
11 import re
11 import re
12
12
13 from . import (
13 from . import (
14 encoding,
14 encoding,
15 error,
15 error,
16 )
16 )
17
17
18 def loadconfigtable(ui, extname, configtable):
18 def loadconfigtable(ui, extname, configtable):
19 """update config item known to the ui with the extension ones"""
19 """update config item known to the ui with the extension ones"""
20 for section, items in configtable.items():
20 for section, items in configtable.items():
21 knownitems = ui._knownconfig.setdefault(section, itemregister())
21 knownitems = ui._knownconfig.setdefault(section, itemregister())
22 knownkeys = set(knownitems)
22 knownkeys = set(knownitems)
23 newkeys = set(items)
23 newkeys = set(items)
24 for key in sorted(knownkeys & newkeys):
24 for key in sorted(knownkeys & newkeys):
25 msg = "extension '%s' overwrite config item '%s.%s'"
25 msg = "extension '%s' overwrite config item '%s.%s'"
26 msg %= (extname, section, key)
26 msg %= (extname, section, key)
27 ui.develwarn(msg, config='warn-config')
27 ui.develwarn(msg, config='warn-config')
28
28
29 knownitems.update(items)
29 knownitems.update(items)
30
30
31 class configitem(object):
31 class configitem(object):
32 """represent a known config item
32 """represent a known config item
33
33
34 :section: the official config section where to find this item,
34 :section: the official config section where to find this item,
35 :name: the official name within the section,
35 :name: the official name within the section,
36 :default: default value for this item,
36 :default: default value for this item,
37 :alias: optional list of tuples as alternatives,
37 :alias: optional list of tuples as alternatives,
38 :generic: this is a generic definition, match name using regular expression.
38 :generic: this is a generic definition, match name using regular expression.
39 """
39 """
40
40
41 def __init__(self, section, name, default=None, alias=(),
41 def __init__(self, section, name, default=None, alias=(),
42 generic=False, priority=0):
42 generic=False, priority=0):
43 self.section = section
43 self.section = section
44 self.name = name
44 self.name = name
45 self.default = default
45 self.default = default
46 self.alias = list(alias)
46 self.alias = list(alias)
47 self.generic = generic
47 self.generic = generic
48 self.priority = priority
48 self.priority = priority
49 self._re = None
49 self._re = None
50 if generic:
50 if generic:
51 self._re = re.compile(self.name)
51 self._re = re.compile(self.name)
52
52
53 class itemregister(dict):
53 class itemregister(dict):
54 """A specialized dictionary that can handle wild-card selection"""
54 """A specialized dictionary that can handle wild-card selection"""
55
55
56 def __init__(self):
56 def __init__(self):
57 super(itemregister, self).__init__()
57 super(itemregister, self).__init__()
58 self._generics = set()
58 self._generics = set()
59
59
60 def update(self, other):
60 def update(self, other):
61 super(itemregister, self).update(other)
61 super(itemregister, self).update(other)
62 self._generics.update(other._generics)
62 self._generics.update(other._generics)
63
63
64 def __setitem__(self, key, item):
64 def __setitem__(self, key, item):
65 super(itemregister, self).__setitem__(key, item)
65 super(itemregister, self).__setitem__(key, item)
66 if item.generic:
66 if item.generic:
67 self._generics.add(item)
67 self._generics.add(item)
68
68
69 def get(self, key):
69 def get(self, key):
70 baseitem = super(itemregister, self).get(key)
70 baseitem = super(itemregister, self).get(key)
71 if baseitem is not None and not baseitem.generic:
71 if baseitem is not None and not baseitem.generic:
72 return baseitem
72 return baseitem
73
73
74 # search for a matching generic item
74 # search for a matching generic item
75 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
75 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
76 for item in generics:
76 for item in generics:
77 # we use 'match' instead of 'search' to make the matching simpler
77 # we use 'match' instead of 'search' to make the matching simpler
78 # for people unfamiliar with regular expression. Having the match
78 # for people unfamiliar with regular expression. Having the match
79 # rooted to the start of the string will produce less surprising
79 # rooted to the start of the string will produce less surprising
80 # result for user writing simple regex for sub-attribute.
80 # result for user writing simple regex for sub-attribute.
81 #
81 #
82 # For example using "color\..*" match produces an unsurprising
82 # For example using "color\..*" match produces an unsurprising
83 # result, while using search could suddenly match apparently
83 # result, while using search could suddenly match apparently
84 # unrelated configuration that happens to contains "color."
84 # unrelated configuration that happens to contains "color."
85 # anywhere. This is a tradeoff where we favor requiring ".*" on
85 # anywhere. This is a tradeoff where we favor requiring ".*" on
86 # some match to avoid the need to prefix most pattern with "^".
86 # some match to avoid the need to prefix most pattern with "^".
87 # The "^" seems more error prone.
87 # The "^" seems more error prone.
88 if item._re.match(key):
88 if item._re.match(key):
89 return item
89 return item
90
90
91 return None
91 return None
92
92
93 coreitems = {}
93 coreitems = {}
94
94
95 def _register(configtable, *args, **kwargs):
95 def _register(configtable, *args, **kwargs):
96 item = configitem(*args, **kwargs)
96 item = configitem(*args, **kwargs)
97 section = configtable.setdefault(item.section, itemregister())
97 section = configtable.setdefault(item.section, itemregister())
98 if item.name in section:
98 if item.name in section:
99 msg = "duplicated config item registration for '%s.%s'"
99 msg = "duplicated config item registration for '%s.%s'"
100 raise error.ProgrammingError(msg % (item.section, item.name))
100 raise error.ProgrammingError(msg % (item.section, item.name))
101 section[item.name] = item
101 section[item.name] = item
102
102
103 # special value for case where the default is derived from other values
103 # special value for case where the default is derived from other values
104 dynamicdefault = object()
104 dynamicdefault = object()
105
105
106 # Registering actual config items
106 # Registering actual config items
107
107
108 def getitemregister(configtable):
108 def getitemregister(configtable):
109 return functools.partial(_register, configtable)
109 return functools.partial(_register, configtable)
110
110
111 coreconfigitem = getitemregister(coreitems)
111 coreconfigitem = getitemregister(coreitems)
112
112
113 coreconfigitem('alias', '.*',
113 coreconfigitem('alias', '.*',
114 default=None,
114 default=None,
115 generic=True,
115 generic=True,
116 )
116 )
117 coreconfigitem('annotate', 'nodates',
117 coreconfigitem('annotate', 'nodates',
118 default=False,
118 default=False,
119 )
119 )
120 coreconfigitem('annotate', 'showfunc',
120 coreconfigitem('annotate', 'showfunc',
121 default=False,
121 default=False,
122 )
122 )
123 coreconfigitem('annotate', 'unified',
123 coreconfigitem('annotate', 'unified',
124 default=None,
124 default=None,
125 )
125 )
126 coreconfigitem('annotate', 'git',
126 coreconfigitem('annotate', 'git',
127 default=False,
127 default=False,
128 )
128 )
129 coreconfigitem('annotate', 'ignorews',
129 coreconfigitem('annotate', 'ignorews',
130 default=False,
130 default=False,
131 )
131 )
132 coreconfigitem('annotate', 'ignorewsamount',
132 coreconfigitem('annotate', 'ignorewsamount',
133 default=False,
133 default=False,
134 )
134 )
135 coreconfigitem('annotate', 'ignoreblanklines',
135 coreconfigitem('annotate', 'ignoreblanklines',
136 default=False,
136 default=False,
137 )
137 )
138 coreconfigitem('annotate', 'ignorewseol',
138 coreconfigitem('annotate', 'ignorewseol',
139 default=False,
139 default=False,
140 )
140 )
141 coreconfigitem('annotate', 'nobinary',
141 coreconfigitem('annotate', 'nobinary',
142 default=False,
142 default=False,
143 )
143 )
144 coreconfigitem('annotate', 'noprefix',
144 coreconfigitem('annotate', 'noprefix',
145 default=False,
145 default=False,
146 )
146 )
147 coreconfigitem('auth', 'cookiefile',
147 coreconfigitem('auth', 'cookiefile',
148 default=None,
148 default=None,
149 )
149 )
150 # bookmarks.pushing: internal hack for discovery
150 # bookmarks.pushing: internal hack for discovery
151 coreconfigitem('bookmarks', 'pushing',
151 coreconfigitem('bookmarks', 'pushing',
152 default=list,
152 default=list,
153 )
153 )
154 # bundle.mainreporoot: internal hack for bundlerepo
154 # bundle.mainreporoot: internal hack for bundlerepo
155 coreconfigitem('bundle', 'mainreporoot',
155 coreconfigitem('bundle', 'mainreporoot',
156 default='',
156 default='',
157 )
157 )
158 # bundle.reorder: experimental config
158 # bundle.reorder: experimental config
159 coreconfigitem('bundle', 'reorder',
159 coreconfigitem('bundle', 'reorder',
160 default='auto',
160 default='auto',
161 )
161 )
162 coreconfigitem('censor', 'policy',
162 coreconfigitem('censor', 'policy',
163 default='abort',
163 default='abort',
164 )
164 )
165 coreconfigitem('chgserver', 'idletimeout',
165 coreconfigitem('chgserver', 'idletimeout',
166 default=3600,
166 default=3600,
167 )
167 )
168 coreconfigitem('chgserver', 'skiphash',
168 coreconfigitem('chgserver', 'skiphash',
169 default=False,
169 default=False,
170 )
170 )
171 coreconfigitem('cmdserver', 'log',
171 coreconfigitem('cmdserver', 'log',
172 default=None,
172 default=None,
173 )
173 )
174 coreconfigitem('color', '.*',
174 coreconfigitem('color', '.*',
175 default=None,
175 default=None,
176 generic=True,
176 generic=True,
177 )
177 )
178 coreconfigitem('color', 'mode',
178 coreconfigitem('color', 'mode',
179 default='auto',
179 default='auto',
180 )
180 )
181 coreconfigitem('color', 'pagermode',
181 coreconfigitem('color', 'pagermode',
182 default=dynamicdefault,
182 default=dynamicdefault,
183 )
183 )
184 coreconfigitem('commands', 'show.aliasprefix',
184 coreconfigitem('commands', 'show.aliasprefix',
185 default=list,
185 default=list,
186 )
186 )
187 coreconfigitem('commands', 'status.relative',
187 coreconfigitem('commands', 'status.relative',
188 default=False,
188 default=False,
189 )
189 )
190 coreconfigitem('commands', 'status.skipstates',
190 coreconfigitem('commands', 'status.skipstates',
191 default=[],
191 default=[],
192 )
192 )
193 coreconfigitem('commands', 'status.verbose',
193 coreconfigitem('commands', 'status.verbose',
194 default=False,
194 default=False,
195 )
195 )
196 coreconfigitem('commands', 'update.check',
196 coreconfigitem('commands', 'update.check',
197 default=None,
197 default=None,
198 # Deprecated, remove after 4.4 release
198 # Deprecated, remove after 4.4 release
199 alias=[('experimental', 'updatecheck')]
199 alias=[('experimental', 'updatecheck')]
200 )
200 )
201 coreconfigitem('commands', 'update.requiredest',
201 coreconfigitem('commands', 'update.requiredest',
202 default=False,
202 default=False,
203 )
203 )
204 coreconfigitem('committemplate', '.*',
204 coreconfigitem('committemplate', '.*',
205 default=None,
205 default=None,
206 generic=True,
206 generic=True,
207 )
207 )
208 coreconfigitem('debug', 'dirstate.delaywrite',
208 coreconfigitem('debug', 'dirstate.delaywrite',
209 default=0,
209 default=0,
210 )
210 )
211 coreconfigitem('defaults', '.*',
211 coreconfigitem('defaults', '.*',
212 default=None,
212 default=None,
213 generic=True,
213 generic=True,
214 )
214 )
215 coreconfigitem('devel', 'all-warnings',
215 coreconfigitem('devel', 'all-warnings',
216 default=False,
216 default=False,
217 )
217 )
218 coreconfigitem('devel', 'bundle2.debug',
218 coreconfigitem('devel', 'bundle2.debug',
219 default=False,
219 default=False,
220 )
220 )
221 coreconfigitem('devel', 'cache-vfs',
221 coreconfigitem('devel', 'cache-vfs',
222 default=None,
222 default=None,
223 )
223 )
224 coreconfigitem('devel', 'check-locks',
224 coreconfigitem('devel', 'check-locks',
225 default=False,
225 default=False,
226 )
226 )
227 coreconfigitem('devel', 'check-relroot',
227 coreconfigitem('devel', 'check-relroot',
228 default=False,
228 default=False,
229 )
229 )
230 coreconfigitem('devel', 'default-date',
230 coreconfigitem('devel', 'default-date',
231 default=None,
231 default=None,
232 )
232 )
233 coreconfigitem('devel', 'deprec-warn',
233 coreconfigitem('devel', 'deprec-warn',
234 default=False,
234 default=False,
235 )
235 )
236 coreconfigitem('devel', 'disableloaddefaultcerts',
236 coreconfigitem('devel', 'disableloaddefaultcerts',
237 default=False,
237 default=False,
238 )
238 )
239 coreconfigitem('devel', 'warn-empty-changegroup',
239 coreconfigitem('devel', 'warn-empty-changegroup',
240 default=False,
240 default=False,
241 )
241 )
242 coreconfigitem('devel', 'legacy.exchange',
242 coreconfigitem('devel', 'legacy.exchange',
243 default=list,
243 default=list,
244 )
244 )
245 coreconfigitem('devel', 'servercafile',
245 coreconfigitem('devel', 'servercafile',
246 default='',
246 default='',
247 )
247 )
248 coreconfigitem('devel', 'serverexactprotocol',
248 coreconfigitem('devel', 'serverexactprotocol',
249 default='',
249 default='',
250 )
250 )
251 coreconfigitem('devel', 'serverrequirecert',
251 coreconfigitem('devel', 'serverrequirecert',
252 default=False,
252 default=False,
253 )
253 )
254 coreconfigitem('devel', 'strip-obsmarkers',
254 coreconfigitem('devel', 'strip-obsmarkers',
255 default=True,
255 default=True,
256 )
256 )
257 coreconfigitem('devel', 'warn-config',
257 coreconfigitem('devel', 'warn-config',
258 default=None,
258 default=None,
259 )
259 )
260 coreconfigitem('devel', 'warn-config-default',
260 coreconfigitem('devel', 'warn-config-default',
261 default=None,
261 default=None,
262 )
262 )
263 coreconfigitem('devel', 'user.obsmarker',
263 coreconfigitem('devel', 'user.obsmarker',
264 default=None,
264 default=None,
265 )
265 )
266 coreconfigitem('devel', 'warn-config-unknown',
266 coreconfigitem('devel', 'warn-config-unknown',
267 default=None,
267 default=None,
268 )
268 )
269 coreconfigitem('diff', 'nodates',
269 coreconfigitem('diff', 'nodates',
270 default=False,
270 default=False,
271 )
271 )
272 coreconfigitem('diff', 'showfunc',
272 coreconfigitem('diff', 'showfunc',
273 default=False,
273 default=False,
274 )
274 )
275 coreconfigitem('diff', 'unified',
275 coreconfigitem('diff', 'unified',
276 default=None,
276 default=None,
277 )
277 )
278 coreconfigitem('diff', 'git',
278 coreconfigitem('diff', 'git',
279 default=False,
279 default=False,
280 )
280 )
281 coreconfigitem('diff', 'ignorews',
281 coreconfigitem('diff', 'ignorews',
282 default=False,
282 default=False,
283 )
283 )
284 coreconfigitem('diff', 'ignorewsamount',
284 coreconfigitem('diff', 'ignorewsamount',
285 default=False,
285 default=False,
286 )
286 )
287 coreconfigitem('diff', 'ignoreblanklines',
287 coreconfigitem('diff', 'ignoreblanklines',
288 default=False,
288 default=False,
289 )
289 )
290 coreconfigitem('diff', 'ignorewseol',
290 coreconfigitem('diff', 'ignorewseol',
291 default=False,
291 default=False,
292 )
292 )
293 coreconfigitem('diff', 'nobinary',
293 coreconfigitem('diff', 'nobinary',
294 default=False,
294 default=False,
295 )
295 )
296 coreconfigitem('diff', 'noprefix',
296 coreconfigitem('diff', 'noprefix',
297 default=False,
297 default=False,
298 )
298 )
299 coreconfigitem('email', 'bcc',
299 coreconfigitem('email', 'bcc',
300 default=None,
300 default=None,
301 )
301 )
302 coreconfigitem('email', 'cc',
302 coreconfigitem('email', 'cc',
303 default=None,
303 default=None,
304 )
304 )
305 coreconfigitem('email', 'charsets',
305 coreconfigitem('email', 'charsets',
306 default=list,
306 default=list,
307 )
307 )
308 coreconfigitem('email', 'from',
308 coreconfigitem('email', 'from',
309 default=None,
309 default=None,
310 )
310 )
311 coreconfigitem('email', 'method',
311 coreconfigitem('email', 'method',
312 default='smtp',
312 default='smtp',
313 )
313 )
314 coreconfigitem('email', 'reply-to',
314 coreconfigitem('email', 'reply-to',
315 default=None,
315 default=None,
316 )
316 )
317 coreconfigitem('experimental', 'archivemetatemplate',
317 coreconfigitem('experimental', 'archivemetatemplate',
318 default=dynamicdefault,
318 default=dynamicdefault,
319 )
319 )
320 coreconfigitem('experimental', 'bundle-phases',
320 coreconfigitem('experimental', 'bundle-phases',
321 default=False,
321 default=False,
322 )
322 )
323 coreconfigitem('experimental', 'bundle2-advertise',
323 coreconfigitem('experimental', 'bundle2-advertise',
324 default=True,
324 default=True,
325 )
325 )
326 coreconfigitem('experimental', 'bundle2-output-capture',
326 coreconfigitem('experimental', 'bundle2-output-capture',
327 default=False,
327 default=False,
328 )
328 )
329 coreconfigitem('experimental', 'bundle2.pushback',
329 coreconfigitem('experimental', 'bundle2.pushback',
330 default=False,
330 default=False,
331 )
331 )
332 coreconfigitem('experimental', 'bundle2lazylocking',
332 coreconfigitem('experimental', 'bundle2lazylocking',
333 default=False,
333 default=False,
334 )
334 )
335 coreconfigitem('experimental', 'bundlecomplevel',
335 coreconfigitem('experimental', 'bundlecomplevel',
336 default=None,
336 default=None,
337 )
337 )
338 coreconfigitem('experimental', 'changegroup3',
338 coreconfigitem('experimental', 'changegroup3',
339 default=False,
339 default=False,
340 )
340 )
341 coreconfigitem('experimental', 'clientcompressionengines',
341 coreconfigitem('experimental', 'clientcompressionengines',
342 default=list,
342 default=list,
343 )
343 )
344 coreconfigitem('experimental', 'copytrace',
344 coreconfigitem('experimental', 'copytrace',
345 default='on',
345 default='on',
346 )
346 )
347 coreconfigitem('experimental', 'copytrace.movecandidateslimit',
347 coreconfigitem('experimental', 'copytrace.movecandidateslimit',
348 default=100,
348 default=100,
349 )
349 )
350 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
350 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
351 default=100,
351 default=100,
352 )
352 )
353 coreconfigitem('experimental', 'crecordtest',
353 coreconfigitem('experimental', 'crecordtest',
354 default=None,
354 default=None,
355 )
355 )
356 coreconfigitem('experimental', 'editortmpinhg',
356 coreconfigitem('experimental', 'editortmpinhg',
357 default=False,
357 default=False,
358 )
358 )
359 coreconfigitem('experimental', 'evolution',
359 coreconfigitem('experimental', 'evolution',
360 default=list,
360 default=list,
361 )
361 )
362 coreconfigitem('experimental', 'evolution.allowdivergence',
362 coreconfigitem('experimental', 'evolution.allowdivergence',
363 default=False,
363 default=False,
364 alias=[('experimental', 'allowdivergence')]
364 alias=[('experimental', 'allowdivergence')]
365 )
365 )
366 coreconfigitem('experimental', 'evolution.allowunstable',
366 coreconfigitem('experimental', 'evolution.allowunstable',
367 default=None,
367 default=None,
368 )
368 )
369 coreconfigitem('experimental', 'evolution.createmarkers',
369 coreconfigitem('experimental', 'evolution.createmarkers',
370 default=None,
370 default=None,
371 )
371 )
372 coreconfigitem('experimental', 'evolution.effect-flags',
373 default=False,
374 alias=[('experimental', 'effect-flags')]
375 )
372 coreconfigitem('experimental', 'evolution.exchange',
376 coreconfigitem('experimental', 'evolution.exchange',
373 default=None,
377 default=None,
374 )
378 )
375 coreconfigitem('experimental', 'evolution.bundle-obsmarker',
379 coreconfigitem('experimental', 'evolution.bundle-obsmarker',
376 default=False,
380 default=False,
377 )
381 )
378 coreconfigitem('experimental', 'evolution.track-operation',
382 coreconfigitem('experimental', 'evolution.track-operation',
379 default=True,
383 default=True,
380 )
384 )
381 coreconfigitem('experimental', 'maxdeltachainspan',
385 coreconfigitem('experimental', 'maxdeltachainspan',
382 default=-1,
386 default=-1,
383 )
387 )
384 coreconfigitem('experimental', 'mmapindexthreshold',
388 coreconfigitem('experimental', 'mmapindexthreshold',
385 default=None,
389 default=None,
386 )
390 )
387 coreconfigitem('experimental', 'nonnormalparanoidcheck',
391 coreconfigitem('experimental', 'nonnormalparanoidcheck',
388 default=False,
392 default=False,
389 )
393 )
390 coreconfigitem('experimental', 'effect-flags',
391 default=False,
392 )
393 coreconfigitem('experimental', 'exportableenviron',
394 coreconfigitem('experimental', 'exportableenviron',
394 default=list,
395 default=list,
395 )
396 )
396 coreconfigitem('experimental', 'extendedheader.index',
397 coreconfigitem('experimental', 'extendedheader.index',
397 default=None,
398 default=None,
398 )
399 )
399 coreconfigitem('experimental', 'extendedheader.similarity',
400 coreconfigitem('experimental', 'extendedheader.similarity',
400 default=False,
401 default=False,
401 )
402 )
402 coreconfigitem('experimental', 'format.compression',
403 coreconfigitem('experimental', 'format.compression',
403 default='zlib',
404 default='zlib',
404 )
405 )
405 coreconfigitem('experimental', 'graphshorten',
406 coreconfigitem('experimental', 'graphshorten',
406 default=False,
407 default=False,
407 )
408 )
408 coreconfigitem('experimental', 'graphstyle.parent',
409 coreconfigitem('experimental', 'graphstyle.parent',
409 default=dynamicdefault,
410 default=dynamicdefault,
410 )
411 )
411 coreconfigitem('experimental', 'graphstyle.missing',
412 coreconfigitem('experimental', 'graphstyle.missing',
412 default=dynamicdefault,
413 default=dynamicdefault,
413 )
414 )
414 coreconfigitem('experimental', 'graphstyle.grandparent',
415 coreconfigitem('experimental', 'graphstyle.grandparent',
415 default=dynamicdefault,
416 default=dynamicdefault,
416 )
417 )
417 coreconfigitem('experimental', 'hook-track-tags',
418 coreconfigitem('experimental', 'hook-track-tags',
418 default=False,
419 default=False,
419 )
420 )
420 coreconfigitem('experimental', 'httppostargs',
421 coreconfigitem('experimental', 'httppostargs',
421 default=False,
422 default=False,
422 )
423 )
423 coreconfigitem('experimental', 'manifestv2',
424 coreconfigitem('experimental', 'manifestv2',
424 default=False,
425 default=False,
425 )
426 )
426 coreconfigitem('experimental', 'mergedriver',
427 coreconfigitem('experimental', 'mergedriver',
427 default=None,
428 default=None,
428 )
429 )
429 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
430 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
430 default=False,
431 default=False,
431 )
432 )
432 coreconfigitem('experimental', 'rebase.multidest',
433 coreconfigitem('experimental', 'rebase.multidest',
433 default=False,
434 default=False,
434 )
435 )
435 coreconfigitem('experimental', 'revertalternateinteractivemode',
436 coreconfigitem('experimental', 'revertalternateinteractivemode',
436 default=True,
437 default=True,
437 )
438 )
438 coreconfigitem('experimental', 'revlogv2',
439 coreconfigitem('experimental', 'revlogv2',
439 default=None,
440 default=None,
440 )
441 )
441 coreconfigitem('experimental', 'spacemovesdown',
442 coreconfigitem('experimental', 'spacemovesdown',
442 default=False,
443 default=False,
443 )
444 )
444 coreconfigitem('experimental', 'sparse-read',
445 coreconfigitem('experimental', 'sparse-read',
445 default=False,
446 default=False,
446 )
447 )
447 coreconfigitem('experimental', 'sparse-read.density-threshold',
448 coreconfigitem('experimental', 'sparse-read.density-threshold',
448 default=0.25,
449 default=0.25,
449 )
450 )
450 coreconfigitem('experimental', 'sparse-read.min-gap-size',
451 coreconfigitem('experimental', 'sparse-read.min-gap-size',
451 default='256K',
452 default='256K',
452 )
453 )
453 coreconfigitem('experimental', 'treemanifest',
454 coreconfigitem('experimental', 'treemanifest',
454 default=False,
455 default=False,
455 )
456 )
456 coreconfigitem('extensions', '.*',
457 coreconfigitem('extensions', '.*',
457 default=None,
458 default=None,
458 generic=True,
459 generic=True,
459 )
460 )
460 coreconfigitem('extdata', '.*',
461 coreconfigitem('extdata', '.*',
461 default=None,
462 default=None,
462 generic=True,
463 generic=True,
463 )
464 )
464 coreconfigitem('format', 'aggressivemergedeltas',
465 coreconfigitem('format', 'aggressivemergedeltas',
465 default=False,
466 default=False,
466 )
467 )
467 coreconfigitem('format', 'chunkcachesize',
468 coreconfigitem('format', 'chunkcachesize',
468 default=None,
469 default=None,
469 )
470 )
470 coreconfigitem('format', 'dotencode',
471 coreconfigitem('format', 'dotencode',
471 default=True,
472 default=True,
472 )
473 )
473 coreconfigitem('format', 'generaldelta',
474 coreconfigitem('format', 'generaldelta',
474 default=False,
475 default=False,
475 )
476 )
476 coreconfigitem('format', 'manifestcachesize',
477 coreconfigitem('format', 'manifestcachesize',
477 default=None,
478 default=None,
478 )
479 )
479 coreconfigitem('format', 'maxchainlen',
480 coreconfigitem('format', 'maxchainlen',
480 default=None,
481 default=None,
481 )
482 )
482 coreconfigitem('format', 'obsstore-version',
483 coreconfigitem('format', 'obsstore-version',
483 default=None,
484 default=None,
484 )
485 )
485 coreconfigitem('format', 'usefncache',
486 coreconfigitem('format', 'usefncache',
486 default=True,
487 default=True,
487 )
488 )
488 coreconfigitem('format', 'usegeneraldelta',
489 coreconfigitem('format', 'usegeneraldelta',
489 default=True,
490 default=True,
490 )
491 )
491 coreconfigitem('format', 'usestore',
492 coreconfigitem('format', 'usestore',
492 default=True,
493 default=True,
493 )
494 )
494 coreconfigitem('fsmonitor', 'warn_when_unused',
495 coreconfigitem('fsmonitor', 'warn_when_unused',
495 default=True,
496 default=True,
496 )
497 )
497 coreconfigitem('fsmonitor', 'warn_update_file_count',
498 coreconfigitem('fsmonitor', 'warn_update_file_count',
498 default=50000,
499 default=50000,
499 )
500 )
500 coreconfigitem('hooks', '.*',
501 coreconfigitem('hooks', '.*',
501 default=dynamicdefault,
502 default=dynamicdefault,
502 generic=True,
503 generic=True,
503 )
504 )
504 coreconfigitem('hgweb-paths', '.*',
505 coreconfigitem('hgweb-paths', '.*',
505 default=list,
506 default=list,
506 generic=True,
507 generic=True,
507 )
508 )
508 coreconfigitem('hostfingerprints', '.*',
509 coreconfigitem('hostfingerprints', '.*',
509 default=list,
510 default=list,
510 generic=True,
511 generic=True,
511 )
512 )
512 coreconfigitem('hostsecurity', 'ciphers',
513 coreconfigitem('hostsecurity', 'ciphers',
513 default=None,
514 default=None,
514 )
515 )
515 coreconfigitem('hostsecurity', 'disabletls10warning',
516 coreconfigitem('hostsecurity', 'disabletls10warning',
516 default=False,
517 default=False,
517 )
518 )
518 coreconfigitem('hostsecurity', 'minimumprotocol',
519 coreconfigitem('hostsecurity', 'minimumprotocol',
519 default=dynamicdefault,
520 default=dynamicdefault,
520 )
521 )
521 coreconfigitem('hostsecurity', '.*:minimumprotocol$',
522 coreconfigitem('hostsecurity', '.*:minimumprotocol$',
522 default=dynamicdefault,
523 default=dynamicdefault,
523 generic=True,
524 generic=True,
524 )
525 )
525 coreconfigitem('hostsecurity', '.*:ciphers$',
526 coreconfigitem('hostsecurity', '.*:ciphers$',
526 default=dynamicdefault,
527 default=dynamicdefault,
527 generic=True,
528 generic=True,
528 )
529 )
529 coreconfigitem('hostsecurity', '.*:fingerprints$',
530 coreconfigitem('hostsecurity', '.*:fingerprints$',
530 default=list,
531 default=list,
531 generic=True,
532 generic=True,
532 )
533 )
533 coreconfigitem('hostsecurity', '.*:verifycertsfile$',
534 coreconfigitem('hostsecurity', '.*:verifycertsfile$',
534 default=None,
535 default=None,
535 generic=True,
536 generic=True,
536 )
537 )
537
538
538 coreconfigitem('http_proxy', 'always',
539 coreconfigitem('http_proxy', 'always',
539 default=False,
540 default=False,
540 )
541 )
541 coreconfigitem('http_proxy', 'host',
542 coreconfigitem('http_proxy', 'host',
542 default=None,
543 default=None,
543 )
544 )
544 coreconfigitem('http_proxy', 'no',
545 coreconfigitem('http_proxy', 'no',
545 default=list,
546 default=list,
546 )
547 )
547 coreconfigitem('http_proxy', 'passwd',
548 coreconfigitem('http_proxy', 'passwd',
548 default=None,
549 default=None,
549 )
550 )
550 coreconfigitem('http_proxy', 'user',
551 coreconfigitem('http_proxy', 'user',
551 default=None,
552 default=None,
552 )
553 )
553 coreconfigitem('logtoprocess', 'commandexception',
554 coreconfigitem('logtoprocess', 'commandexception',
554 default=None,
555 default=None,
555 )
556 )
556 coreconfigitem('logtoprocess', 'commandfinish',
557 coreconfigitem('logtoprocess', 'commandfinish',
557 default=None,
558 default=None,
558 )
559 )
559 coreconfigitem('logtoprocess', 'command',
560 coreconfigitem('logtoprocess', 'command',
560 default=None,
561 default=None,
561 )
562 )
562 coreconfigitem('logtoprocess', 'develwarn',
563 coreconfigitem('logtoprocess', 'develwarn',
563 default=None,
564 default=None,
564 )
565 )
565 coreconfigitem('logtoprocess', 'uiblocked',
566 coreconfigitem('logtoprocess', 'uiblocked',
566 default=None,
567 default=None,
567 )
568 )
568 coreconfigitem('merge', 'checkunknown',
569 coreconfigitem('merge', 'checkunknown',
569 default='abort',
570 default='abort',
570 )
571 )
571 coreconfigitem('merge', 'checkignored',
572 coreconfigitem('merge', 'checkignored',
572 default='abort',
573 default='abort',
573 )
574 )
574 coreconfigitem('merge', 'followcopies',
575 coreconfigitem('merge', 'followcopies',
575 default=True,
576 default=True,
576 )
577 )
577 coreconfigitem('merge', 'on-failure',
578 coreconfigitem('merge', 'on-failure',
578 default='continue',
579 default='continue',
579 )
580 )
580 coreconfigitem('merge', 'preferancestor',
581 coreconfigitem('merge', 'preferancestor',
581 default=lambda: ['*'],
582 default=lambda: ['*'],
582 )
583 )
583 coreconfigitem('merge-tools', '.*',
584 coreconfigitem('merge-tools', '.*',
584 default=None,
585 default=None,
585 generic=True,
586 generic=True,
586 )
587 )
587 coreconfigitem('merge-tools', br'.*\.args$',
588 coreconfigitem('merge-tools', br'.*\.args$',
588 default="$local $base $other",
589 default="$local $base $other",
589 generic=True,
590 generic=True,
590 priority=-1,
591 priority=-1,
591 )
592 )
592 coreconfigitem('merge-tools', br'.*\.binary$',
593 coreconfigitem('merge-tools', br'.*\.binary$',
593 default=False,
594 default=False,
594 generic=True,
595 generic=True,
595 priority=-1,
596 priority=-1,
596 )
597 )
597 coreconfigitem('merge-tools', br'.*\.check$',
598 coreconfigitem('merge-tools', br'.*\.check$',
598 default=list,
599 default=list,
599 generic=True,
600 generic=True,
600 priority=-1,
601 priority=-1,
601 )
602 )
602 coreconfigitem('merge-tools', br'.*\.checkchanged$',
603 coreconfigitem('merge-tools', br'.*\.checkchanged$',
603 default=False,
604 default=False,
604 generic=True,
605 generic=True,
605 priority=-1,
606 priority=-1,
606 )
607 )
607 coreconfigitem('merge-tools', br'.*\.executable$',
608 coreconfigitem('merge-tools', br'.*\.executable$',
608 default=dynamicdefault,
609 default=dynamicdefault,
609 generic=True,
610 generic=True,
610 priority=-1,
611 priority=-1,
611 )
612 )
612 coreconfigitem('merge-tools', br'.*\.fixeol$',
613 coreconfigitem('merge-tools', br'.*\.fixeol$',
613 default=False,
614 default=False,
614 generic=True,
615 generic=True,
615 priority=-1,
616 priority=-1,
616 )
617 )
617 coreconfigitem('merge-tools', br'.*\.gui$',
618 coreconfigitem('merge-tools', br'.*\.gui$',
618 default=False,
619 default=False,
619 generic=True,
620 generic=True,
620 priority=-1,
621 priority=-1,
621 )
622 )
622 coreconfigitem('merge-tools', br'.*\.priority$',
623 coreconfigitem('merge-tools', br'.*\.priority$',
623 default=0,
624 default=0,
624 generic=True,
625 generic=True,
625 priority=-1,
626 priority=-1,
626 )
627 )
627 coreconfigitem('merge-tools', br'.*\.premerge$',
628 coreconfigitem('merge-tools', br'.*\.premerge$',
628 default=dynamicdefault,
629 default=dynamicdefault,
629 generic=True,
630 generic=True,
630 priority=-1,
631 priority=-1,
631 )
632 )
632 coreconfigitem('merge-tools', br'.*\.symlink$',
633 coreconfigitem('merge-tools', br'.*\.symlink$',
633 default=False,
634 default=False,
634 generic=True,
635 generic=True,
635 priority=-1,
636 priority=-1,
636 )
637 )
637 coreconfigitem('pager', 'attend-.*',
638 coreconfigitem('pager', 'attend-.*',
638 default=dynamicdefault,
639 default=dynamicdefault,
639 generic=True,
640 generic=True,
640 )
641 )
641 coreconfigitem('pager', 'ignore',
642 coreconfigitem('pager', 'ignore',
642 default=list,
643 default=list,
643 )
644 )
644 coreconfigitem('pager', 'pager',
645 coreconfigitem('pager', 'pager',
645 default=dynamicdefault,
646 default=dynamicdefault,
646 )
647 )
647 coreconfigitem('patch', 'eol',
648 coreconfigitem('patch', 'eol',
648 default='strict',
649 default='strict',
649 )
650 )
650 coreconfigitem('patch', 'fuzz',
651 coreconfigitem('patch', 'fuzz',
651 default=2,
652 default=2,
652 )
653 )
653 coreconfigitem('paths', 'default',
654 coreconfigitem('paths', 'default',
654 default=None,
655 default=None,
655 )
656 )
656 coreconfigitem('paths', 'default-push',
657 coreconfigitem('paths', 'default-push',
657 default=None,
658 default=None,
658 )
659 )
659 coreconfigitem('paths', '.*',
660 coreconfigitem('paths', '.*',
660 default=None,
661 default=None,
661 generic=True,
662 generic=True,
662 )
663 )
663 coreconfigitem('phases', 'checksubrepos',
664 coreconfigitem('phases', 'checksubrepos',
664 default='follow',
665 default='follow',
665 )
666 )
666 coreconfigitem('phases', 'new-commit',
667 coreconfigitem('phases', 'new-commit',
667 default='draft',
668 default='draft',
668 )
669 )
669 coreconfigitem('phases', 'publish',
670 coreconfigitem('phases', 'publish',
670 default=True,
671 default=True,
671 )
672 )
672 coreconfigitem('profiling', 'enabled',
673 coreconfigitem('profiling', 'enabled',
673 default=False,
674 default=False,
674 )
675 )
675 coreconfigitem('profiling', 'format',
676 coreconfigitem('profiling', 'format',
676 default='text',
677 default='text',
677 )
678 )
678 coreconfigitem('profiling', 'freq',
679 coreconfigitem('profiling', 'freq',
679 default=1000,
680 default=1000,
680 )
681 )
681 coreconfigitem('profiling', 'limit',
682 coreconfigitem('profiling', 'limit',
682 default=30,
683 default=30,
683 )
684 )
684 coreconfigitem('profiling', 'nested',
685 coreconfigitem('profiling', 'nested',
685 default=0,
686 default=0,
686 )
687 )
687 coreconfigitem('profiling', 'output',
688 coreconfigitem('profiling', 'output',
688 default=None,
689 default=None,
689 )
690 )
690 coreconfigitem('profiling', 'showmax',
691 coreconfigitem('profiling', 'showmax',
691 default=0.999,
692 default=0.999,
692 )
693 )
693 coreconfigitem('profiling', 'showmin',
694 coreconfigitem('profiling', 'showmin',
694 default=dynamicdefault,
695 default=dynamicdefault,
695 )
696 )
696 coreconfigitem('profiling', 'sort',
697 coreconfigitem('profiling', 'sort',
697 default='inlinetime',
698 default='inlinetime',
698 )
699 )
699 coreconfigitem('profiling', 'statformat',
700 coreconfigitem('profiling', 'statformat',
700 default='hotpath',
701 default='hotpath',
701 )
702 )
702 coreconfigitem('profiling', 'type',
703 coreconfigitem('profiling', 'type',
703 default='stat',
704 default='stat',
704 )
705 )
705 coreconfigitem('progress', 'assume-tty',
706 coreconfigitem('progress', 'assume-tty',
706 default=False,
707 default=False,
707 )
708 )
708 coreconfigitem('progress', 'changedelay',
709 coreconfigitem('progress', 'changedelay',
709 default=1,
710 default=1,
710 )
711 )
711 coreconfigitem('progress', 'clear-complete',
712 coreconfigitem('progress', 'clear-complete',
712 default=True,
713 default=True,
713 )
714 )
714 coreconfigitem('progress', 'debug',
715 coreconfigitem('progress', 'debug',
715 default=False,
716 default=False,
716 )
717 )
717 coreconfigitem('progress', 'delay',
718 coreconfigitem('progress', 'delay',
718 default=3,
719 default=3,
719 )
720 )
720 coreconfigitem('progress', 'disable',
721 coreconfigitem('progress', 'disable',
721 default=False,
722 default=False,
722 )
723 )
723 coreconfigitem('progress', 'estimateinterval',
724 coreconfigitem('progress', 'estimateinterval',
724 default=60.0,
725 default=60.0,
725 )
726 )
726 coreconfigitem('progress', 'format',
727 coreconfigitem('progress', 'format',
727 default=lambda: ['topic', 'bar', 'number', 'estimate'],
728 default=lambda: ['topic', 'bar', 'number', 'estimate'],
728 )
729 )
729 coreconfigitem('progress', 'refresh',
730 coreconfigitem('progress', 'refresh',
730 default=0.1,
731 default=0.1,
731 )
732 )
732 coreconfigitem('progress', 'width',
733 coreconfigitem('progress', 'width',
733 default=dynamicdefault,
734 default=dynamicdefault,
734 )
735 )
735 coreconfigitem('push', 'pushvars.server',
736 coreconfigitem('push', 'pushvars.server',
736 default=False,
737 default=False,
737 )
738 )
738 coreconfigitem('server', 'bundle1',
739 coreconfigitem('server', 'bundle1',
739 default=True,
740 default=True,
740 )
741 )
741 coreconfigitem('server', 'bundle1gd',
742 coreconfigitem('server', 'bundle1gd',
742 default=None,
743 default=None,
743 )
744 )
744 coreconfigitem('server', 'bundle1.pull',
745 coreconfigitem('server', 'bundle1.pull',
745 default=None,
746 default=None,
746 )
747 )
747 coreconfigitem('server', 'bundle1gd.pull',
748 coreconfigitem('server', 'bundle1gd.pull',
748 default=None,
749 default=None,
749 )
750 )
750 coreconfigitem('server', 'bundle1.push',
751 coreconfigitem('server', 'bundle1.push',
751 default=None,
752 default=None,
752 )
753 )
753 coreconfigitem('server', 'bundle1gd.push',
754 coreconfigitem('server', 'bundle1gd.push',
754 default=None,
755 default=None,
755 )
756 )
756 coreconfigitem('server', 'compressionengines',
757 coreconfigitem('server', 'compressionengines',
757 default=list,
758 default=list,
758 )
759 )
759 coreconfigitem('server', 'concurrent-push-mode',
760 coreconfigitem('server', 'concurrent-push-mode',
760 default='strict',
761 default='strict',
761 )
762 )
762 coreconfigitem('server', 'disablefullbundle',
763 coreconfigitem('server', 'disablefullbundle',
763 default=False,
764 default=False,
764 )
765 )
765 coreconfigitem('server', 'maxhttpheaderlen',
766 coreconfigitem('server', 'maxhttpheaderlen',
766 default=1024,
767 default=1024,
767 )
768 )
768 coreconfigitem('server', 'preferuncompressed',
769 coreconfigitem('server', 'preferuncompressed',
769 default=False,
770 default=False,
770 )
771 )
771 coreconfigitem('server', 'uncompressed',
772 coreconfigitem('server', 'uncompressed',
772 default=True,
773 default=True,
773 )
774 )
774 coreconfigitem('server', 'uncompressedallowsecret',
775 coreconfigitem('server', 'uncompressedallowsecret',
775 default=False,
776 default=False,
776 )
777 )
777 coreconfigitem('server', 'validate',
778 coreconfigitem('server', 'validate',
778 default=False,
779 default=False,
779 )
780 )
780 coreconfigitem('server', 'zliblevel',
781 coreconfigitem('server', 'zliblevel',
781 default=-1,
782 default=-1,
782 )
783 )
783 coreconfigitem('smtp', 'host',
784 coreconfigitem('smtp', 'host',
784 default=None,
785 default=None,
785 )
786 )
786 coreconfigitem('smtp', 'local_hostname',
787 coreconfigitem('smtp', 'local_hostname',
787 default=None,
788 default=None,
788 )
789 )
789 coreconfigitem('smtp', 'password',
790 coreconfigitem('smtp', 'password',
790 default=None,
791 default=None,
791 )
792 )
792 coreconfigitem('smtp', 'port',
793 coreconfigitem('smtp', 'port',
793 default=dynamicdefault,
794 default=dynamicdefault,
794 )
795 )
795 coreconfigitem('smtp', 'tls',
796 coreconfigitem('smtp', 'tls',
796 default='none',
797 default='none',
797 )
798 )
798 coreconfigitem('smtp', 'username',
799 coreconfigitem('smtp', 'username',
799 default=None,
800 default=None,
800 )
801 )
801 coreconfigitem('sparse', 'missingwarning',
802 coreconfigitem('sparse', 'missingwarning',
802 default=True,
803 default=True,
803 )
804 )
804 coreconfigitem('templates', '.*',
805 coreconfigitem('templates', '.*',
805 default=None,
806 default=None,
806 generic=True,
807 generic=True,
807 )
808 )
808 coreconfigitem('trusted', 'groups',
809 coreconfigitem('trusted', 'groups',
809 default=list,
810 default=list,
810 )
811 )
811 coreconfigitem('trusted', 'users',
812 coreconfigitem('trusted', 'users',
812 default=list,
813 default=list,
813 )
814 )
814 coreconfigitem('ui', '_usedassubrepo',
815 coreconfigitem('ui', '_usedassubrepo',
815 default=False,
816 default=False,
816 )
817 )
817 coreconfigitem('ui', 'allowemptycommit',
818 coreconfigitem('ui', 'allowemptycommit',
818 default=False,
819 default=False,
819 )
820 )
820 coreconfigitem('ui', 'archivemeta',
821 coreconfigitem('ui', 'archivemeta',
821 default=True,
822 default=True,
822 )
823 )
823 coreconfigitem('ui', 'askusername',
824 coreconfigitem('ui', 'askusername',
824 default=False,
825 default=False,
825 )
826 )
826 coreconfigitem('ui', 'clonebundlefallback',
827 coreconfigitem('ui', 'clonebundlefallback',
827 default=False,
828 default=False,
828 )
829 )
829 coreconfigitem('ui', 'clonebundleprefers',
830 coreconfigitem('ui', 'clonebundleprefers',
830 default=list,
831 default=list,
831 )
832 )
832 coreconfigitem('ui', 'clonebundles',
833 coreconfigitem('ui', 'clonebundles',
833 default=True,
834 default=True,
834 )
835 )
835 coreconfigitem('ui', 'color',
836 coreconfigitem('ui', 'color',
836 default='auto',
837 default='auto',
837 )
838 )
838 coreconfigitem('ui', 'commitsubrepos',
839 coreconfigitem('ui', 'commitsubrepos',
839 default=False,
840 default=False,
840 )
841 )
841 coreconfigitem('ui', 'debug',
842 coreconfigitem('ui', 'debug',
842 default=False,
843 default=False,
843 )
844 )
844 coreconfigitem('ui', 'debugger',
845 coreconfigitem('ui', 'debugger',
845 default=None,
846 default=None,
846 )
847 )
847 coreconfigitem('ui', 'fallbackencoding',
848 coreconfigitem('ui', 'fallbackencoding',
848 default=None,
849 default=None,
849 )
850 )
850 coreconfigitem('ui', 'forcecwd',
851 coreconfigitem('ui', 'forcecwd',
851 default=None,
852 default=None,
852 )
853 )
853 coreconfigitem('ui', 'forcemerge',
854 coreconfigitem('ui', 'forcemerge',
854 default=None,
855 default=None,
855 )
856 )
856 coreconfigitem('ui', 'formatdebug',
857 coreconfigitem('ui', 'formatdebug',
857 default=False,
858 default=False,
858 )
859 )
859 coreconfigitem('ui', 'formatjson',
860 coreconfigitem('ui', 'formatjson',
860 default=False,
861 default=False,
861 )
862 )
862 coreconfigitem('ui', 'formatted',
863 coreconfigitem('ui', 'formatted',
863 default=None,
864 default=None,
864 )
865 )
865 coreconfigitem('ui', 'graphnodetemplate',
866 coreconfigitem('ui', 'graphnodetemplate',
866 default=None,
867 default=None,
867 )
868 )
868 coreconfigitem('ui', 'http2debuglevel',
869 coreconfigitem('ui', 'http2debuglevel',
869 default=None,
870 default=None,
870 )
871 )
871 coreconfigitem('ui', 'interactive',
872 coreconfigitem('ui', 'interactive',
872 default=None,
873 default=None,
873 )
874 )
874 coreconfigitem('ui', 'interface',
875 coreconfigitem('ui', 'interface',
875 default=None,
876 default=None,
876 )
877 )
877 coreconfigitem('ui', 'interface.chunkselector',
878 coreconfigitem('ui', 'interface.chunkselector',
878 default=None,
879 default=None,
879 )
880 )
880 coreconfigitem('ui', 'logblockedtimes',
881 coreconfigitem('ui', 'logblockedtimes',
881 default=False,
882 default=False,
882 )
883 )
883 coreconfigitem('ui', 'logtemplate',
884 coreconfigitem('ui', 'logtemplate',
884 default=None,
885 default=None,
885 )
886 )
886 coreconfigitem('ui', 'merge',
887 coreconfigitem('ui', 'merge',
887 default=None,
888 default=None,
888 )
889 )
889 coreconfigitem('ui', 'mergemarkers',
890 coreconfigitem('ui', 'mergemarkers',
890 default='basic',
891 default='basic',
891 )
892 )
892 coreconfigitem('ui', 'mergemarkertemplate',
893 coreconfigitem('ui', 'mergemarkertemplate',
893 default=('{node|short} '
894 default=('{node|short} '
894 '{ifeq(tags, "tip", "", '
895 '{ifeq(tags, "tip", "", '
895 'ifeq(tags, "", "", "{tags} "))}'
896 'ifeq(tags, "", "", "{tags} "))}'
896 '{if(bookmarks, "{bookmarks} ")}'
897 '{if(bookmarks, "{bookmarks} ")}'
897 '{ifeq(branch, "default", "", "{branch} ")}'
898 '{ifeq(branch, "default", "", "{branch} ")}'
898 '- {author|user}: {desc|firstline}')
899 '- {author|user}: {desc|firstline}')
899 )
900 )
900 coreconfigitem('ui', 'nontty',
901 coreconfigitem('ui', 'nontty',
901 default=False,
902 default=False,
902 )
903 )
903 coreconfigitem('ui', 'origbackuppath',
904 coreconfigitem('ui', 'origbackuppath',
904 default=None,
905 default=None,
905 )
906 )
906 coreconfigitem('ui', 'paginate',
907 coreconfigitem('ui', 'paginate',
907 default=True,
908 default=True,
908 )
909 )
909 coreconfigitem('ui', 'patch',
910 coreconfigitem('ui', 'patch',
910 default=None,
911 default=None,
911 )
912 )
912 coreconfigitem('ui', 'portablefilenames',
913 coreconfigitem('ui', 'portablefilenames',
913 default='warn',
914 default='warn',
914 )
915 )
915 coreconfigitem('ui', 'promptecho',
916 coreconfigitem('ui', 'promptecho',
916 default=False,
917 default=False,
917 )
918 )
918 coreconfigitem('ui', 'quiet',
919 coreconfigitem('ui', 'quiet',
919 default=False,
920 default=False,
920 )
921 )
921 coreconfigitem('ui', 'quietbookmarkmove',
922 coreconfigitem('ui', 'quietbookmarkmove',
922 default=False,
923 default=False,
923 )
924 )
924 coreconfigitem('ui', 'remotecmd',
925 coreconfigitem('ui', 'remotecmd',
925 default='hg',
926 default='hg',
926 )
927 )
927 coreconfigitem('ui', 'report_untrusted',
928 coreconfigitem('ui', 'report_untrusted',
928 default=True,
929 default=True,
929 )
930 )
930 coreconfigitem('ui', 'rollback',
931 coreconfigitem('ui', 'rollback',
931 default=True,
932 default=True,
932 )
933 )
933 coreconfigitem('ui', 'slash',
934 coreconfigitem('ui', 'slash',
934 default=False,
935 default=False,
935 )
936 )
936 coreconfigitem('ui', 'ssh',
937 coreconfigitem('ui', 'ssh',
937 default='ssh',
938 default='ssh',
938 )
939 )
939 coreconfigitem('ui', 'statuscopies',
940 coreconfigitem('ui', 'statuscopies',
940 default=False,
941 default=False,
941 )
942 )
942 coreconfigitem('ui', 'strict',
943 coreconfigitem('ui', 'strict',
943 default=False,
944 default=False,
944 )
945 )
945 coreconfigitem('ui', 'style',
946 coreconfigitem('ui', 'style',
946 default='',
947 default='',
947 )
948 )
948 coreconfigitem('ui', 'supportcontact',
949 coreconfigitem('ui', 'supportcontact',
949 default=None,
950 default=None,
950 )
951 )
951 coreconfigitem('ui', 'textwidth',
952 coreconfigitem('ui', 'textwidth',
952 default=78,
953 default=78,
953 )
954 )
954 coreconfigitem('ui', 'timeout',
955 coreconfigitem('ui', 'timeout',
955 default='600',
956 default='600',
956 )
957 )
957 coreconfigitem('ui', 'traceback',
958 coreconfigitem('ui', 'traceback',
958 default=False,
959 default=False,
959 )
960 )
960 coreconfigitem('ui', 'tweakdefaults',
961 coreconfigitem('ui', 'tweakdefaults',
961 default=False,
962 default=False,
962 )
963 )
963 coreconfigitem('ui', 'usehttp2',
964 coreconfigitem('ui', 'usehttp2',
964 default=False,
965 default=False,
965 )
966 )
966 coreconfigitem('ui', 'username',
967 coreconfigitem('ui', 'username',
967 alias=[('ui', 'user')]
968 alias=[('ui', 'user')]
968 )
969 )
969 coreconfigitem('ui', 'verbose',
970 coreconfigitem('ui', 'verbose',
970 default=False,
971 default=False,
971 )
972 )
972 coreconfigitem('verify', 'skipflags',
973 coreconfigitem('verify', 'skipflags',
973 default=None,
974 default=None,
974 )
975 )
975 coreconfigitem('web', 'allowbz2',
976 coreconfigitem('web', 'allowbz2',
976 default=False,
977 default=False,
977 )
978 )
978 coreconfigitem('web', 'allowgz',
979 coreconfigitem('web', 'allowgz',
979 default=False,
980 default=False,
980 )
981 )
981 coreconfigitem('web', 'allowpull',
982 coreconfigitem('web', 'allowpull',
982 default=True,
983 default=True,
983 )
984 )
984 coreconfigitem('web', 'allow_push',
985 coreconfigitem('web', 'allow_push',
985 default=list,
986 default=list,
986 )
987 )
987 coreconfigitem('web', 'allowzip',
988 coreconfigitem('web', 'allowzip',
988 default=False,
989 default=False,
989 )
990 )
990 coreconfigitem('web', 'archivesubrepos',
991 coreconfigitem('web', 'archivesubrepos',
991 default=False,
992 default=False,
992 )
993 )
993 coreconfigitem('web', 'cache',
994 coreconfigitem('web', 'cache',
994 default=True,
995 default=True,
995 )
996 )
996 coreconfigitem('web', 'contact',
997 coreconfigitem('web', 'contact',
997 default=None,
998 default=None,
998 )
999 )
999 coreconfigitem('web', 'deny_push',
1000 coreconfigitem('web', 'deny_push',
1000 default=list,
1001 default=list,
1001 )
1002 )
1002 coreconfigitem('web', 'guessmime',
1003 coreconfigitem('web', 'guessmime',
1003 default=False,
1004 default=False,
1004 )
1005 )
1005 coreconfigitem('web', 'hidden',
1006 coreconfigitem('web', 'hidden',
1006 default=False,
1007 default=False,
1007 )
1008 )
1008 coreconfigitem('web', 'labels',
1009 coreconfigitem('web', 'labels',
1009 default=list,
1010 default=list,
1010 )
1011 )
1011 coreconfigitem('web', 'logoimg',
1012 coreconfigitem('web', 'logoimg',
1012 default='hglogo.png',
1013 default='hglogo.png',
1013 )
1014 )
1014 coreconfigitem('web', 'logourl',
1015 coreconfigitem('web', 'logourl',
1015 default='https://mercurial-scm.org/',
1016 default='https://mercurial-scm.org/',
1016 )
1017 )
1017 coreconfigitem('web', 'accesslog',
1018 coreconfigitem('web', 'accesslog',
1018 default='-',
1019 default='-',
1019 )
1020 )
1020 coreconfigitem('web', 'address',
1021 coreconfigitem('web', 'address',
1021 default='',
1022 default='',
1022 )
1023 )
1023 coreconfigitem('web', 'allow_archive',
1024 coreconfigitem('web', 'allow_archive',
1024 default=list,
1025 default=list,
1025 )
1026 )
1026 coreconfigitem('web', 'allow_read',
1027 coreconfigitem('web', 'allow_read',
1027 default=list,
1028 default=list,
1028 )
1029 )
1029 coreconfigitem('web', 'baseurl',
1030 coreconfigitem('web', 'baseurl',
1030 default=None,
1031 default=None,
1031 )
1032 )
1032 coreconfigitem('web', 'cacerts',
1033 coreconfigitem('web', 'cacerts',
1033 default=None,
1034 default=None,
1034 )
1035 )
1035 coreconfigitem('web', 'certificate',
1036 coreconfigitem('web', 'certificate',
1036 default=None,
1037 default=None,
1037 )
1038 )
1038 coreconfigitem('web', 'collapse',
1039 coreconfigitem('web', 'collapse',
1039 default=False,
1040 default=False,
1040 )
1041 )
1041 coreconfigitem('web', 'csp',
1042 coreconfigitem('web', 'csp',
1042 default=None,
1043 default=None,
1043 )
1044 )
1044 coreconfigitem('web', 'deny_read',
1045 coreconfigitem('web', 'deny_read',
1045 default=list,
1046 default=list,
1046 )
1047 )
1047 coreconfigitem('web', 'descend',
1048 coreconfigitem('web', 'descend',
1048 default=True,
1049 default=True,
1049 )
1050 )
1050 coreconfigitem('web', 'description',
1051 coreconfigitem('web', 'description',
1051 default="",
1052 default="",
1052 )
1053 )
1053 coreconfigitem('web', 'encoding',
1054 coreconfigitem('web', 'encoding',
1054 default=lambda: encoding.encoding,
1055 default=lambda: encoding.encoding,
1055 )
1056 )
1056 coreconfigitem('web', 'errorlog',
1057 coreconfigitem('web', 'errorlog',
1057 default='-',
1058 default='-',
1058 )
1059 )
1059 coreconfigitem('web', 'ipv6',
1060 coreconfigitem('web', 'ipv6',
1060 default=False,
1061 default=False,
1061 )
1062 )
1062 coreconfigitem('web', 'maxchanges',
1063 coreconfigitem('web', 'maxchanges',
1063 default=10,
1064 default=10,
1064 )
1065 )
1065 coreconfigitem('web', 'maxfiles',
1066 coreconfigitem('web', 'maxfiles',
1066 default=10,
1067 default=10,
1067 )
1068 )
1068 coreconfigitem('web', 'maxshortchanges',
1069 coreconfigitem('web', 'maxshortchanges',
1069 default=60,
1070 default=60,
1070 )
1071 )
1071 coreconfigitem('web', 'motd',
1072 coreconfigitem('web', 'motd',
1072 default='',
1073 default='',
1073 )
1074 )
1074 coreconfigitem('web', 'name',
1075 coreconfigitem('web', 'name',
1075 default=dynamicdefault,
1076 default=dynamicdefault,
1076 )
1077 )
1077 coreconfigitem('web', 'port',
1078 coreconfigitem('web', 'port',
1078 default=8000,
1079 default=8000,
1079 )
1080 )
1080 coreconfigitem('web', 'prefix',
1081 coreconfigitem('web', 'prefix',
1081 default='',
1082 default='',
1082 )
1083 )
1083 coreconfigitem('web', 'push_ssl',
1084 coreconfigitem('web', 'push_ssl',
1084 default=True,
1085 default=True,
1085 )
1086 )
1086 coreconfigitem('web', 'refreshinterval',
1087 coreconfigitem('web', 'refreshinterval',
1087 default=20,
1088 default=20,
1088 )
1089 )
1089 coreconfigitem('web', 'staticurl',
1090 coreconfigitem('web', 'staticurl',
1090 default=None,
1091 default=None,
1091 )
1092 )
1092 coreconfigitem('web', 'stripes',
1093 coreconfigitem('web', 'stripes',
1093 default=1,
1094 default=1,
1094 )
1095 )
1095 coreconfigitem('web', 'style',
1096 coreconfigitem('web', 'style',
1096 default='paper',
1097 default='paper',
1097 )
1098 )
1098 coreconfigitem('web', 'templates',
1099 coreconfigitem('web', 'templates',
1099 default=None,
1100 default=None,
1100 )
1101 )
1101 coreconfigitem('web', 'view',
1102 coreconfigitem('web', 'view',
1102 default='served',
1103 default='served',
1103 )
1104 )
1104 coreconfigitem('worker', 'backgroundclose',
1105 coreconfigitem('worker', 'backgroundclose',
1105 default=dynamicdefault,
1106 default=dynamicdefault,
1106 )
1107 )
1107 # Windows defaults to a limit of 512 open files. A buffer of 128
1108 # Windows defaults to a limit of 512 open files. A buffer of 128
1108 # should give us enough headway.
1109 # should give us enough headway.
1109 coreconfigitem('worker', 'backgroundclosemaxqueue',
1110 coreconfigitem('worker', 'backgroundclosemaxqueue',
1110 default=384,
1111 default=384,
1111 )
1112 )
1112 coreconfigitem('worker', 'backgroundcloseminfilecount',
1113 coreconfigitem('worker', 'backgroundcloseminfilecount',
1113 default=2048,
1114 default=2048,
1114 )
1115 )
1115 coreconfigitem('worker', 'backgroundclosethreadcount',
1116 coreconfigitem('worker', 'backgroundclosethreadcount',
1116 default=4,
1117 default=4,
1117 )
1118 )
1118 coreconfigitem('worker', 'numcpus',
1119 coreconfigitem('worker', 'numcpus',
1119 default=None,
1120 default=None,
1120 )
1121 )
1121
1122
1122 # Rebase related configuration moved to core because other extension are doing
1123 # Rebase related configuration moved to core because other extension are doing
1123 # strange things. For example, shelve import the extensions to reuse some bit
1124 # strange things. For example, shelve import the extensions to reuse some bit
1124 # without formally loading it.
1125 # without formally loading it.
1125 coreconfigitem('commands', 'rebase.requiredest',
1126 coreconfigitem('commands', 'rebase.requiredest',
1126 default=False,
1127 default=False,
1127 )
1128 )
1128 coreconfigitem('experimental', 'rebaseskipobsolete',
1129 coreconfigitem('experimental', 'rebaseskipobsolete',
1129 default=True,
1130 default=True,
1130 )
1131 )
1131 coreconfigitem('rebase', 'singletransaction',
1132 coreconfigitem('rebase', 'singletransaction',
1132 default=False,
1133 default=False,
1133 )
1134 )
@@ -1,1126 +1,1126 b''
1 # obsolete.py - obsolete markers handling
1 # obsolete.py - obsolete markers handling
2 #
2 #
3 # Copyright 2012 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
3 # Copyright 2012 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
4 # Logilab SA <contact@logilab.fr>
4 # Logilab SA <contact@logilab.fr>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 """Obsolete marker handling
9 """Obsolete marker handling
10
10
11 An obsolete marker maps an old changeset to a list of new
11 An obsolete marker maps an old changeset to a list of new
12 changesets. If the list of new changesets is empty, the old changeset
12 changesets. If the list of new changesets is empty, the old changeset
13 is said to be "killed". Otherwise, the old changeset is being
13 is said to be "killed". Otherwise, the old changeset is being
14 "replaced" by the new changesets.
14 "replaced" by the new changesets.
15
15
16 Obsolete markers can be used to record and distribute changeset graph
16 Obsolete markers can be used to record and distribute changeset graph
17 transformations performed by history rewrite operations, and help
17 transformations performed by history rewrite operations, and help
18 building new tools to reconcile conflicting rewrite actions. To
18 building new tools to reconcile conflicting rewrite actions. To
19 facilitate conflict resolution, markers include various annotations
19 facilitate conflict resolution, markers include various annotations
20 besides old and news changeset identifiers, such as creation date or
20 besides old and news changeset identifiers, such as creation date or
21 author name.
21 author name.
22
22
23 The old obsoleted changeset is called a "predecessor" and possible
23 The old obsoleted changeset is called a "predecessor" and possible
24 replacements are called "successors". Markers that used changeset X as
24 replacements are called "successors". Markers that used changeset X as
25 a predecessor are called "successor markers of X" because they hold
25 a predecessor are called "successor markers of X" because they hold
26 information about the successors of X. Markers that use changeset Y as
26 information about the successors of X. Markers that use changeset Y as
27 a successors are call "predecessor markers of Y" because they hold
27 a successors are call "predecessor markers of Y" because they hold
28 information about the predecessors of Y.
28 information about the predecessors of Y.
29
29
30 Examples:
30 Examples:
31
31
32 - When changeset A is replaced by changeset A', one marker is stored:
32 - When changeset A is replaced by changeset A', one marker is stored:
33
33
34 (A, (A',))
34 (A, (A',))
35
35
36 - When changesets A and B are folded into a new changeset C, two markers are
36 - When changesets A and B are folded into a new changeset C, two markers are
37 stored:
37 stored:
38
38
39 (A, (C,)) and (B, (C,))
39 (A, (C,)) and (B, (C,))
40
40
41 - When changeset A is simply "pruned" from the graph, a marker is created:
41 - When changeset A is simply "pruned" from the graph, a marker is created:
42
42
43 (A, ())
43 (A, ())
44
44
45 - When changeset A is split into B and C, a single marker is used:
45 - When changeset A is split into B and C, a single marker is used:
46
46
47 (A, (B, C))
47 (A, (B, C))
48
48
49 We use a single marker to distinguish the "split" case from the "divergence"
49 We use a single marker to distinguish the "split" case from the "divergence"
50 case. If two independent operations rewrite the same changeset A in to A' and
50 case. If two independent operations rewrite the same changeset A in to A' and
51 A'', we have an error case: divergent rewriting. We can detect it because
51 A'', we have an error case: divergent rewriting. We can detect it because
52 two markers will be created independently:
52 two markers will be created independently:
53
53
54 (A, (B,)) and (A, (C,))
54 (A, (B,)) and (A, (C,))
55
55
56 Format
56 Format
57 ------
57 ------
58
58
59 Markers are stored in an append-only file stored in
59 Markers are stored in an append-only file stored in
60 '.hg/store/obsstore'.
60 '.hg/store/obsstore'.
61
61
62 The file starts with a version header:
62 The file starts with a version header:
63
63
64 - 1 unsigned byte: version number, starting at zero.
64 - 1 unsigned byte: version number, starting at zero.
65
65
66 The header is followed by the markers. Marker format depend of the version. See
66 The header is followed by the markers. Marker format depend of the version. See
67 comment associated with each format for details.
67 comment associated with each format for details.
68
68
69 """
69 """
70 from __future__ import absolute_import
70 from __future__ import absolute_import
71
71
72 import errno
72 import errno
73 import struct
73 import struct
74
74
75 from .i18n import _
75 from .i18n import _
76 from . import (
76 from . import (
77 error,
77 error,
78 node,
78 node,
79 obsutil,
79 obsutil,
80 phases,
80 phases,
81 policy,
81 policy,
82 util,
82 util,
83 )
83 )
84
84
85 parsers = policy.importmod(r'parsers')
85 parsers = policy.importmod(r'parsers')
86
86
87 _pack = struct.pack
87 _pack = struct.pack
88 _unpack = struct.unpack
88 _unpack = struct.unpack
89 _calcsize = struct.calcsize
89 _calcsize = struct.calcsize
90 propertycache = util.propertycache
90 propertycache = util.propertycache
91
91
92 # the obsolete feature is not mature enough to be enabled by default.
92 # the obsolete feature is not mature enough to be enabled by default.
93 # you have to rely on third party extension extension to enable this.
93 # you have to rely on third party extension extension to enable this.
94 _enabled = False
94 _enabled = False
95
95
96 # Options for obsolescence
96 # Options for obsolescence
97 createmarkersopt = 'createmarkers'
97 createmarkersopt = 'createmarkers'
98 allowunstableopt = 'allowunstable'
98 allowunstableopt = 'allowunstable'
99 exchangeopt = 'exchange'
99 exchangeopt = 'exchange'
100
100
101 def _getoptionvalue(repo, option):
101 def _getoptionvalue(repo, option):
102 """Returns True if the given repository has the given obsolete option
102 """Returns True if the given repository has the given obsolete option
103 enabled.
103 enabled.
104 """
104 """
105 configkey = 'evolution.%s' % option
105 configkey = 'evolution.%s' % option
106 newconfig = repo.ui.configbool('experimental', configkey)
106 newconfig = repo.ui.configbool('experimental', configkey)
107
107
108 # Return the value only if defined
108 # Return the value only if defined
109 if newconfig is not None:
109 if newconfig is not None:
110 return newconfig
110 return newconfig
111
111
112 # Fallback on generic option
112 # Fallback on generic option
113 try:
113 try:
114 return repo.ui.configbool('experimental', 'evolution')
114 return repo.ui.configbool('experimental', 'evolution')
115 except (error.ConfigError, AttributeError):
115 except (error.ConfigError, AttributeError):
116 # Fallback on old-fashion config
116 # Fallback on old-fashion config
117 # inconsistent config: experimental.evolution
117 # inconsistent config: experimental.evolution
118 result = set(repo.ui.configlist('experimental', 'evolution'))
118 result = set(repo.ui.configlist('experimental', 'evolution'))
119
119
120 if 'all' in result:
120 if 'all' in result:
121 return True
121 return True
122
122
123 # For migration purposes, temporarily return true if the config hasn't
123 # For migration purposes, temporarily return true if the config hasn't
124 # been set but _enabled is true.
124 # been set but _enabled is true.
125 if len(result) == 0 and _enabled:
125 if len(result) == 0 and _enabled:
126 return True
126 return True
127
127
128 # Temporary hack for next check
128 # Temporary hack for next check
129 newconfig = repo.ui.config('experimental', 'evolution.createmarkers')
129 newconfig = repo.ui.config('experimental', 'evolution.createmarkers')
130 if newconfig:
130 if newconfig:
131 result.add('createmarkers')
131 result.add('createmarkers')
132
132
133 return option in result
133 return option in result
134
134
135 def isenabled(repo, option):
135 def isenabled(repo, option):
136 """Returns True if the given repository has the given obsolete option
136 """Returns True if the given repository has the given obsolete option
137 enabled.
137 enabled.
138 """
138 """
139 createmarkersvalue = _getoptionvalue(repo, createmarkersopt)
139 createmarkersvalue = _getoptionvalue(repo, createmarkersopt)
140 unstabluevalue = _getoptionvalue(repo, allowunstableopt)
140 unstabluevalue = _getoptionvalue(repo, allowunstableopt)
141 exchangevalue = _getoptionvalue(repo, exchangeopt)
141 exchangevalue = _getoptionvalue(repo, exchangeopt)
142
142
143 # createmarkers must be enabled if other options are enabled
143 # createmarkers must be enabled if other options are enabled
144 if ((unstabluevalue or exchangevalue) and not createmarkersvalue):
144 if ((unstabluevalue or exchangevalue) and not createmarkersvalue):
145 raise error.Abort(_("'createmarkers' obsolete option must be enabled "
145 raise error.Abort(_("'createmarkers' obsolete option must be enabled "
146 "if other obsolete options are enabled"))
146 "if other obsolete options are enabled"))
147
147
148 return _getoptionvalue(repo, option)
148 return _getoptionvalue(repo, option)
149
149
150 ### obsolescence marker flag
150 ### obsolescence marker flag
151
151
152 ## bumpedfix flag
152 ## bumpedfix flag
153 #
153 #
154 # When a changeset A' succeed to a changeset A which became public, we call A'
154 # When a changeset A' succeed to a changeset A which became public, we call A'
155 # "bumped" because it's a successors of a public changesets
155 # "bumped" because it's a successors of a public changesets
156 #
156 #
157 # o A' (bumped)
157 # o A' (bumped)
158 # |`:
158 # |`:
159 # | o A
159 # | o A
160 # |/
160 # |/
161 # o Z
161 # o Z
162 #
162 #
163 # The way to solve this situation is to create a new changeset Ad as children
163 # The way to solve this situation is to create a new changeset Ad as children
164 # of A. This changeset have the same content than A'. So the diff from A to A'
164 # of A. This changeset have the same content than A'. So the diff from A to A'
165 # is the same than the diff from A to Ad. Ad is marked as a successors of A'
165 # is the same than the diff from A to Ad. Ad is marked as a successors of A'
166 #
166 #
167 # o Ad
167 # o Ad
168 # |`:
168 # |`:
169 # | x A'
169 # | x A'
170 # |'|
170 # |'|
171 # o | A
171 # o | A
172 # |/
172 # |/
173 # o Z
173 # o Z
174 #
174 #
175 # But by transitivity Ad is also a successors of A. To avoid having Ad marked
175 # But by transitivity Ad is also a successors of A. To avoid having Ad marked
176 # as bumped too, we add the `bumpedfix` flag to the marker. <A', (Ad,)>.
176 # as bumped too, we add the `bumpedfix` flag to the marker. <A', (Ad,)>.
177 # This flag mean that the successors express the changes between the public and
177 # This flag mean that the successors express the changes between the public and
178 # bumped version and fix the situation, breaking the transitivity of
178 # bumped version and fix the situation, breaking the transitivity of
179 # "bumped" here.
179 # "bumped" here.
180 bumpedfix = 1
180 bumpedfix = 1
181 usingsha256 = 2
181 usingsha256 = 2
182
182
183 ## Parsing and writing of version "0"
183 ## Parsing and writing of version "0"
184 #
184 #
185 # The header is followed by the markers. Each marker is made of:
185 # The header is followed by the markers. Each marker is made of:
186 #
186 #
187 # - 1 uint8 : number of new changesets "N", can be zero.
187 # - 1 uint8 : number of new changesets "N", can be zero.
188 #
188 #
189 # - 1 uint32: metadata size "M" in bytes.
189 # - 1 uint32: metadata size "M" in bytes.
190 #
190 #
191 # - 1 byte: a bit field. It is reserved for flags used in common
191 # - 1 byte: a bit field. It is reserved for flags used in common
192 # obsolete marker operations, to avoid repeated decoding of metadata
192 # obsolete marker operations, to avoid repeated decoding of metadata
193 # entries.
193 # entries.
194 #
194 #
195 # - 20 bytes: obsoleted changeset identifier.
195 # - 20 bytes: obsoleted changeset identifier.
196 #
196 #
197 # - N*20 bytes: new changesets identifiers.
197 # - N*20 bytes: new changesets identifiers.
198 #
198 #
199 # - M bytes: metadata as a sequence of nul-terminated strings. Each
199 # - M bytes: metadata as a sequence of nul-terminated strings. Each
200 # string contains a key and a value, separated by a colon ':', without
200 # string contains a key and a value, separated by a colon ':', without
201 # additional encoding. Keys cannot contain '\0' or ':' and values
201 # additional encoding. Keys cannot contain '\0' or ':' and values
202 # cannot contain '\0'.
202 # cannot contain '\0'.
203 _fm0version = 0
203 _fm0version = 0
204 _fm0fixed = '>BIB20s'
204 _fm0fixed = '>BIB20s'
205 _fm0node = '20s'
205 _fm0node = '20s'
206 _fm0fsize = _calcsize(_fm0fixed)
206 _fm0fsize = _calcsize(_fm0fixed)
207 _fm0fnodesize = _calcsize(_fm0node)
207 _fm0fnodesize = _calcsize(_fm0node)
208
208
209 def _fm0readmarkers(data, off, stop):
209 def _fm0readmarkers(data, off, stop):
210 # Loop on markers
210 # Loop on markers
211 while off < stop:
211 while off < stop:
212 # read fixed part
212 # read fixed part
213 cur = data[off:off + _fm0fsize]
213 cur = data[off:off + _fm0fsize]
214 off += _fm0fsize
214 off += _fm0fsize
215 numsuc, mdsize, flags, pre = _unpack(_fm0fixed, cur)
215 numsuc, mdsize, flags, pre = _unpack(_fm0fixed, cur)
216 # read replacement
216 # read replacement
217 sucs = ()
217 sucs = ()
218 if numsuc:
218 if numsuc:
219 s = (_fm0fnodesize * numsuc)
219 s = (_fm0fnodesize * numsuc)
220 cur = data[off:off + s]
220 cur = data[off:off + s]
221 sucs = _unpack(_fm0node * numsuc, cur)
221 sucs = _unpack(_fm0node * numsuc, cur)
222 off += s
222 off += s
223 # read metadata
223 # read metadata
224 # (metadata will be decoded on demand)
224 # (metadata will be decoded on demand)
225 metadata = data[off:off + mdsize]
225 metadata = data[off:off + mdsize]
226 if len(metadata) != mdsize:
226 if len(metadata) != mdsize:
227 raise error.Abort(_('parsing obsolete marker: metadata is too '
227 raise error.Abort(_('parsing obsolete marker: metadata is too '
228 'short, %d bytes expected, got %d')
228 'short, %d bytes expected, got %d')
229 % (mdsize, len(metadata)))
229 % (mdsize, len(metadata)))
230 off += mdsize
230 off += mdsize
231 metadata = _fm0decodemeta(metadata)
231 metadata = _fm0decodemeta(metadata)
232 try:
232 try:
233 when, offset = metadata.pop('date', '0 0').split(' ')
233 when, offset = metadata.pop('date', '0 0').split(' ')
234 date = float(when), int(offset)
234 date = float(when), int(offset)
235 except ValueError:
235 except ValueError:
236 date = (0., 0)
236 date = (0., 0)
237 parents = None
237 parents = None
238 if 'p2' in metadata:
238 if 'p2' in metadata:
239 parents = (metadata.pop('p1', None), metadata.pop('p2', None))
239 parents = (metadata.pop('p1', None), metadata.pop('p2', None))
240 elif 'p1' in metadata:
240 elif 'p1' in metadata:
241 parents = (metadata.pop('p1', None),)
241 parents = (metadata.pop('p1', None),)
242 elif 'p0' in metadata:
242 elif 'p0' in metadata:
243 parents = ()
243 parents = ()
244 if parents is not None:
244 if parents is not None:
245 try:
245 try:
246 parents = tuple(node.bin(p) for p in parents)
246 parents = tuple(node.bin(p) for p in parents)
247 # if parent content is not a nodeid, drop the data
247 # if parent content is not a nodeid, drop the data
248 for p in parents:
248 for p in parents:
249 if len(p) != 20:
249 if len(p) != 20:
250 parents = None
250 parents = None
251 break
251 break
252 except TypeError:
252 except TypeError:
253 # if content cannot be translated to nodeid drop the data.
253 # if content cannot be translated to nodeid drop the data.
254 parents = None
254 parents = None
255
255
256 metadata = tuple(sorted(metadata.iteritems()))
256 metadata = tuple(sorted(metadata.iteritems()))
257
257
258 yield (pre, sucs, flags, metadata, date, parents)
258 yield (pre, sucs, flags, metadata, date, parents)
259
259
260 def _fm0encodeonemarker(marker):
260 def _fm0encodeonemarker(marker):
261 pre, sucs, flags, metadata, date, parents = marker
261 pre, sucs, flags, metadata, date, parents = marker
262 if flags & usingsha256:
262 if flags & usingsha256:
263 raise error.Abort(_('cannot handle sha256 with old obsstore format'))
263 raise error.Abort(_('cannot handle sha256 with old obsstore format'))
264 metadata = dict(metadata)
264 metadata = dict(metadata)
265 time, tz = date
265 time, tz = date
266 metadata['date'] = '%r %i' % (time, tz)
266 metadata['date'] = '%r %i' % (time, tz)
267 if parents is not None:
267 if parents is not None:
268 if not parents:
268 if not parents:
269 # mark that we explicitly recorded no parents
269 # mark that we explicitly recorded no parents
270 metadata['p0'] = ''
270 metadata['p0'] = ''
271 for i, p in enumerate(parents, 1):
271 for i, p in enumerate(parents, 1):
272 metadata['p%i' % i] = node.hex(p)
272 metadata['p%i' % i] = node.hex(p)
273 metadata = _fm0encodemeta(metadata)
273 metadata = _fm0encodemeta(metadata)
274 numsuc = len(sucs)
274 numsuc = len(sucs)
275 format = _fm0fixed + (_fm0node * numsuc)
275 format = _fm0fixed + (_fm0node * numsuc)
276 data = [numsuc, len(metadata), flags, pre]
276 data = [numsuc, len(metadata), flags, pre]
277 data.extend(sucs)
277 data.extend(sucs)
278 return _pack(format, *data) + metadata
278 return _pack(format, *data) + metadata
279
279
280 def _fm0encodemeta(meta):
280 def _fm0encodemeta(meta):
281 """Return encoded metadata string to string mapping.
281 """Return encoded metadata string to string mapping.
282
282
283 Assume no ':' in key and no '\0' in both key and value."""
283 Assume no ':' in key and no '\0' in both key and value."""
284 for key, value in meta.iteritems():
284 for key, value in meta.iteritems():
285 if ':' in key or '\0' in key:
285 if ':' in key or '\0' in key:
286 raise ValueError("':' and '\0' are forbidden in metadata key'")
286 raise ValueError("':' and '\0' are forbidden in metadata key'")
287 if '\0' in value:
287 if '\0' in value:
288 raise ValueError("':' is forbidden in metadata value'")
288 raise ValueError("':' is forbidden in metadata value'")
289 return '\0'.join(['%s:%s' % (k, meta[k]) for k in sorted(meta)])
289 return '\0'.join(['%s:%s' % (k, meta[k]) for k in sorted(meta)])
290
290
291 def _fm0decodemeta(data):
291 def _fm0decodemeta(data):
292 """Return string to string dictionary from encoded version."""
292 """Return string to string dictionary from encoded version."""
293 d = {}
293 d = {}
294 for l in data.split('\0'):
294 for l in data.split('\0'):
295 if l:
295 if l:
296 key, value = l.split(':')
296 key, value = l.split(':')
297 d[key] = value
297 d[key] = value
298 return d
298 return d
299
299
300 ## Parsing and writing of version "1"
300 ## Parsing and writing of version "1"
301 #
301 #
302 # The header is followed by the markers. Each marker is made of:
302 # The header is followed by the markers. Each marker is made of:
303 #
303 #
304 # - uint32: total size of the marker (including this field)
304 # - uint32: total size of the marker (including this field)
305 #
305 #
306 # - float64: date in seconds since epoch
306 # - float64: date in seconds since epoch
307 #
307 #
308 # - int16: timezone offset in minutes
308 # - int16: timezone offset in minutes
309 #
309 #
310 # - uint16: a bit field. It is reserved for flags used in common
310 # - uint16: a bit field. It is reserved for flags used in common
311 # obsolete marker operations, to avoid repeated decoding of metadata
311 # obsolete marker operations, to avoid repeated decoding of metadata
312 # entries.
312 # entries.
313 #
313 #
314 # - uint8: number of successors "N", can be zero.
314 # - uint8: number of successors "N", can be zero.
315 #
315 #
316 # - uint8: number of parents "P", can be zero.
316 # - uint8: number of parents "P", can be zero.
317 #
317 #
318 # 0: parents data stored but no parent,
318 # 0: parents data stored but no parent,
319 # 1: one parent stored,
319 # 1: one parent stored,
320 # 2: two parents stored,
320 # 2: two parents stored,
321 # 3: no parent data stored
321 # 3: no parent data stored
322 #
322 #
323 # - uint8: number of metadata entries M
323 # - uint8: number of metadata entries M
324 #
324 #
325 # - 20 or 32 bytes: predecessor changeset identifier.
325 # - 20 or 32 bytes: predecessor changeset identifier.
326 #
326 #
327 # - N*(20 or 32) bytes: successors changesets identifiers.
327 # - N*(20 or 32) bytes: successors changesets identifiers.
328 #
328 #
329 # - P*(20 or 32) bytes: parents of the predecessors changesets.
329 # - P*(20 or 32) bytes: parents of the predecessors changesets.
330 #
330 #
331 # - M*(uint8, uint8): size of all metadata entries (key and value)
331 # - M*(uint8, uint8): size of all metadata entries (key and value)
332 #
332 #
333 # - remaining bytes: the metadata, each (key, value) pair after the other.
333 # - remaining bytes: the metadata, each (key, value) pair after the other.
334 _fm1version = 1
334 _fm1version = 1
335 _fm1fixed = '>IdhHBBB20s'
335 _fm1fixed = '>IdhHBBB20s'
336 _fm1nodesha1 = '20s'
336 _fm1nodesha1 = '20s'
337 _fm1nodesha256 = '32s'
337 _fm1nodesha256 = '32s'
338 _fm1nodesha1size = _calcsize(_fm1nodesha1)
338 _fm1nodesha1size = _calcsize(_fm1nodesha1)
339 _fm1nodesha256size = _calcsize(_fm1nodesha256)
339 _fm1nodesha256size = _calcsize(_fm1nodesha256)
340 _fm1fsize = _calcsize(_fm1fixed)
340 _fm1fsize = _calcsize(_fm1fixed)
341 _fm1parentnone = 3
341 _fm1parentnone = 3
342 _fm1parentshift = 14
342 _fm1parentshift = 14
343 _fm1parentmask = (_fm1parentnone << _fm1parentshift)
343 _fm1parentmask = (_fm1parentnone << _fm1parentshift)
344 _fm1metapair = 'BB'
344 _fm1metapair = 'BB'
345 _fm1metapairsize = _calcsize(_fm1metapair)
345 _fm1metapairsize = _calcsize(_fm1metapair)
346
346
347 def _fm1purereadmarkers(data, off, stop):
347 def _fm1purereadmarkers(data, off, stop):
348 # make some global constants local for performance
348 # make some global constants local for performance
349 noneflag = _fm1parentnone
349 noneflag = _fm1parentnone
350 sha2flag = usingsha256
350 sha2flag = usingsha256
351 sha1size = _fm1nodesha1size
351 sha1size = _fm1nodesha1size
352 sha2size = _fm1nodesha256size
352 sha2size = _fm1nodesha256size
353 sha1fmt = _fm1nodesha1
353 sha1fmt = _fm1nodesha1
354 sha2fmt = _fm1nodesha256
354 sha2fmt = _fm1nodesha256
355 metasize = _fm1metapairsize
355 metasize = _fm1metapairsize
356 metafmt = _fm1metapair
356 metafmt = _fm1metapair
357 fsize = _fm1fsize
357 fsize = _fm1fsize
358 unpack = _unpack
358 unpack = _unpack
359
359
360 # Loop on markers
360 # Loop on markers
361 ufixed = struct.Struct(_fm1fixed).unpack
361 ufixed = struct.Struct(_fm1fixed).unpack
362
362
363 while off < stop:
363 while off < stop:
364 # read fixed part
364 # read fixed part
365 o1 = off + fsize
365 o1 = off + fsize
366 t, secs, tz, flags, numsuc, numpar, nummeta, prec = ufixed(data[off:o1])
366 t, secs, tz, flags, numsuc, numpar, nummeta, prec = ufixed(data[off:o1])
367
367
368 if flags & sha2flag:
368 if flags & sha2flag:
369 # FIXME: prec was read as a SHA1, needs to be amended
369 # FIXME: prec was read as a SHA1, needs to be amended
370
370
371 # read 0 or more successors
371 # read 0 or more successors
372 if numsuc == 1:
372 if numsuc == 1:
373 o2 = o1 + sha2size
373 o2 = o1 + sha2size
374 sucs = (data[o1:o2],)
374 sucs = (data[o1:o2],)
375 else:
375 else:
376 o2 = o1 + sha2size * numsuc
376 o2 = o1 + sha2size * numsuc
377 sucs = unpack(sha2fmt * numsuc, data[o1:o2])
377 sucs = unpack(sha2fmt * numsuc, data[o1:o2])
378
378
379 # read parents
379 # read parents
380 if numpar == noneflag:
380 if numpar == noneflag:
381 o3 = o2
381 o3 = o2
382 parents = None
382 parents = None
383 elif numpar == 1:
383 elif numpar == 1:
384 o3 = o2 + sha2size
384 o3 = o2 + sha2size
385 parents = (data[o2:o3],)
385 parents = (data[o2:o3],)
386 else:
386 else:
387 o3 = o2 + sha2size * numpar
387 o3 = o2 + sha2size * numpar
388 parents = unpack(sha2fmt * numpar, data[o2:o3])
388 parents = unpack(sha2fmt * numpar, data[o2:o3])
389 else:
389 else:
390 # read 0 or more successors
390 # read 0 or more successors
391 if numsuc == 1:
391 if numsuc == 1:
392 o2 = o1 + sha1size
392 o2 = o1 + sha1size
393 sucs = (data[o1:o2],)
393 sucs = (data[o1:o2],)
394 else:
394 else:
395 o2 = o1 + sha1size * numsuc
395 o2 = o1 + sha1size * numsuc
396 sucs = unpack(sha1fmt * numsuc, data[o1:o2])
396 sucs = unpack(sha1fmt * numsuc, data[o1:o2])
397
397
398 # read parents
398 # read parents
399 if numpar == noneflag:
399 if numpar == noneflag:
400 o3 = o2
400 o3 = o2
401 parents = None
401 parents = None
402 elif numpar == 1:
402 elif numpar == 1:
403 o3 = o2 + sha1size
403 o3 = o2 + sha1size
404 parents = (data[o2:o3],)
404 parents = (data[o2:o3],)
405 else:
405 else:
406 o3 = o2 + sha1size * numpar
406 o3 = o2 + sha1size * numpar
407 parents = unpack(sha1fmt * numpar, data[o2:o3])
407 parents = unpack(sha1fmt * numpar, data[o2:o3])
408
408
409 # read metadata
409 # read metadata
410 off = o3 + metasize * nummeta
410 off = o3 + metasize * nummeta
411 metapairsize = unpack('>' + (metafmt * nummeta), data[o3:off])
411 metapairsize = unpack('>' + (metafmt * nummeta), data[o3:off])
412 metadata = []
412 metadata = []
413 for idx in xrange(0, len(metapairsize), 2):
413 for idx in xrange(0, len(metapairsize), 2):
414 o1 = off + metapairsize[idx]
414 o1 = off + metapairsize[idx]
415 o2 = o1 + metapairsize[idx + 1]
415 o2 = o1 + metapairsize[idx + 1]
416 metadata.append((data[off:o1], data[o1:o2]))
416 metadata.append((data[off:o1], data[o1:o2]))
417 off = o2
417 off = o2
418
418
419 yield (prec, sucs, flags, tuple(metadata), (secs, tz * 60), parents)
419 yield (prec, sucs, flags, tuple(metadata), (secs, tz * 60), parents)
420
420
421 def _fm1encodeonemarker(marker):
421 def _fm1encodeonemarker(marker):
422 pre, sucs, flags, metadata, date, parents = marker
422 pre, sucs, flags, metadata, date, parents = marker
423 # determine node size
423 # determine node size
424 _fm1node = _fm1nodesha1
424 _fm1node = _fm1nodesha1
425 if flags & usingsha256:
425 if flags & usingsha256:
426 _fm1node = _fm1nodesha256
426 _fm1node = _fm1nodesha256
427 numsuc = len(sucs)
427 numsuc = len(sucs)
428 numextranodes = numsuc
428 numextranodes = numsuc
429 if parents is None:
429 if parents is None:
430 numpar = _fm1parentnone
430 numpar = _fm1parentnone
431 else:
431 else:
432 numpar = len(parents)
432 numpar = len(parents)
433 numextranodes += numpar
433 numextranodes += numpar
434 formatnodes = _fm1node * numextranodes
434 formatnodes = _fm1node * numextranodes
435 formatmeta = _fm1metapair * len(metadata)
435 formatmeta = _fm1metapair * len(metadata)
436 format = _fm1fixed + formatnodes + formatmeta
436 format = _fm1fixed + formatnodes + formatmeta
437 # tz is stored in minutes so we divide by 60
437 # tz is stored in minutes so we divide by 60
438 tz = date[1]//60
438 tz = date[1]//60
439 data = [None, date[0], tz, flags, numsuc, numpar, len(metadata), pre]
439 data = [None, date[0], tz, flags, numsuc, numpar, len(metadata), pre]
440 data.extend(sucs)
440 data.extend(sucs)
441 if parents is not None:
441 if parents is not None:
442 data.extend(parents)
442 data.extend(parents)
443 totalsize = _calcsize(format)
443 totalsize = _calcsize(format)
444 for key, value in metadata:
444 for key, value in metadata:
445 lk = len(key)
445 lk = len(key)
446 lv = len(value)
446 lv = len(value)
447 if lk > 255:
447 if lk > 255:
448 msg = ('obsstore metadata key cannot be longer than 255 bytes'
448 msg = ('obsstore metadata key cannot be longer than 255 bytes'
449 ' (key "%s" is %u bytes)') % (key, lk)
449 ' (key "%s" is %u bytes)') % (key, lk)
450 raise error.ProgrammingError(msg)
450 raise error.ProgrammingError(msg)
451 if lv > 255:
451 if lv > 255:
452 msg = ('obsstore metadata value cannot be longer than 255 bytes'
452 msg = ('obsstore metadata value cannot be longer than 255 bytes'
453 ' (value "%s" for key "%s" is %u bytes)') % (value, key, lv)
453 ' (value "%s" for key "%s" is %u bytes)') % (value, key, lv)
454 raise error.ProgrammingError(msg)
454 raise error.ProgrammingError(msg)
455 data.append(lk)
455 data.append(lk)
456 data.append(lv)
456 data.append(lv)
457 totalsize += lk + lv
457 totalsize += lk + lv
458 data[0] = totalsize
458 data[0] = totalsize
459 data = [_pack(format, *data)]
459 data = [_pack(format, *data)]
460 for key, value in metadata:
460 for key, value in metadata:
461 data.append(key)
461 data.append(key)
462 data.append(value)
462 data.append(value)
463 return ''.join(data)
463 return ''.join(data)
464
464
465 def _fm1readmarkers(data, off, stop):
465 def _fm1readmarkers(data, off, stop):
466 native = getattr(parsers, 'fm1readmarkers', None)
466 native = getattr(parsers, 'fm1readmarkers', None)
467 if not native:
467 if not native:
468 return _fm1purereadmarkers(data, off, stop)
468 return _fm1purereadmarkers(data, off, stop)
469 return native(data, off, stop)
469 return native(data, off, stop)
470
470
471 # mapping to read/write various marker formats
471 # mapping to read/write various marker formats
472 # <version> -> (decoder, encoder)
472 # <version> -> (decoder, encoder)
473 formats = {_fm0version: (_fm0readmarkers, _fm0encodeonemarker),
473 formats = {_fm0version: (_fm0readmarkers, _fm0encodeonemarker),
474 _fm1version: (_fm1readmarkers, _fm1encodeonemarker)}
474 _fm1version: (_fm1readmarkers, _fm1encodeonemarker)}
475
475
476 def _readmarkerversion(data):
476 def _readmarkerversion(data):
477 return _unpack('>B', data[0:1])[0]
477 return _unpack('>B', data[0:1])[0]
478
478
479 @util.nogc
479 @util.nogc
480 def _readmarkers(data, off=None, stop=None):
480 def _readmarkers(data, off=None, stop=None):
481 """Read and enumerate markers from raw data"""
481 """Read and enumerate markers from raw data"""
482 diskversion = _readmarkerversion(data)
482 diskversion = _readmarkerversion(data)
483 if not off:
483 if not off:
484 off = 1 # skip 1 byte version number
484 off = 1 # skip 1 byte version number
485 if stop is None:
485 if stop is None:
486 stop = len(data)
486 stop = len(data)
487 if diskversion not in formats:
487 if diskversion not in formats:
488 msg = _('parsing obsolete marker: unknown version %r') % diskversion
488 msg = _('parsing obsolete marker: unknown version %r') % diskversion
489 raise error.UnknownVersion(msg, version=diskversion)
489 raise error.UnknownVersion(msg, version=diskversion)
490 return diskversion, formats[diskversion][0](data, off, stop)
490 return diskversion, formats[diskversion][0](data, off, stop)
491
491
492 def encodeheader(version=_fm0version):
492 def encodeheader(version=_fm0version):
493 return _pack('>B', version)
493 return _pack('>B', version)
494
494
495 def encodemarkers(markers, addheader=False, version=_fm0version):
495 def encodemarkers(markers, addheader=False, version=_fm0version):
496 # Kept separate from flushmarkers(), it will be reused for
496 # Kept separate from flushmarkers(), it will be reused for
497 # markers exchange.
497 # markers exchange.
498 encodeone = formats[version][1]
498 encodeone = formats[version][1]
499 if addheader:
499 if addheader:
500 yield encodeheader(version)
500 yield encodeheader(version)
501 for marker in markers:
501 for marker in markers:
502 yield encodeone(marker)
502 yield encodeone(marker)
503
503
504 @util.nogc
504 @util.nogc
505 def _addsuccessors(successors, markers):
505 def _addsuccessors(successors, markers):
506 for mark in markers:
506 for mark in markers:
507 successors.setdefault(mark[0], set()).add(mark)
507 successors.setdefault(mark[0], set()).add(mark)
508
508
509 def _addprecursors(*args, **kwargs):
509 def _addprecursors(*args, **kwargs):
510 msg = ("'obsolete._addprecursors' is deprecated, "
510 msg = ("'obsolete._addprecursors' is deprecated, "
511 "use 'obsolete._addpredecessors'")
511 "use 'obsolete._addpredecessors'")
512 util.nouideprecwarn(msg, '4.4')
512 util.nouideprecwarn(msg, '4.4')
513
513
514 return _addpredecessors(*args, **kwargs)
514 return _addpredecessors(*args, **kwargs)
515
515
516 @util.nogc
516 @util.nogc
517 def _addpredecessors(predecessors, markers):
517 def _addpredecessors(predecessors, markers):
518 for mark in markers:
518 for mark in markers:
519 for suc in mark[1]:
519 for suc in mark[1]:
520 predecessors.setdefault(suc, set()).add(mark)
520 predecessors.setdefault(suc, set()).add(mark)
521
521
522 @util.nogc
522 @util.nogc
523 def _addchildren(children, markers):
523 def _addchildren(children, markers):
524 for mark in markers:
524 for mark in markers:
525 parents = mark[5]
525 parents = mark[5]
526 if parents is not None:
526 if parents is not None:
527 for p in parents:
527 for p in parents:
528 children.setdefault(p, set()).add(mark)
528 children.setdefault(p, set()).add(mark)
529
529
530 def _checkinvalidmarkers(markers):
530 def _checkinvalidmarkers(markers):
531 """search for marker with invalid data and raise error if needed
531 """search for marker with invalid data and raise error if needed
532
532
533 Exist as a separated function to allow the evolve extension for a more
533 Exist as a separated function to allow the evolve extension for a more
534 subtle handling.
534 subtle handling.
535 """
535 """
536 for mark in markers:
536 for mark in markers:
537 if node.nullid in mark[1]:
537 if node.nullid in mark[1]:
538 raise error.Abort(_('bad obsolescence marker detected: '
538 raise error.Abort(_('bad obsolescence marker detected: '
539 'invalid successors nullid'))
539 'invalid successors nullid'))
540
540
541 class obsstore(object):
541 class obsstore(object):
542 """Store obsolete markers
542 """Store obsolete markers
543
543
544 Markers can be accessed with two mappings:
544 Markers can be accessed with two mappings:
545 - predecessors[x] -> set(markers on predecessors edges of x)
545 - predecessors[x] -> set(markers on predecessors edges of x)
546 - successors[x] -> set(markers on successors edges of x)
546 - successors[x] -> set(markers on successors edges of x)
547 - children[x] -> set(markers on predecessors edges of children(x)
547 - children[x] -> set(markers on predecessors edges of children(x)
548 """
548 """
549
549
550 fields = ('prec', 'succs', 'flag', 'meta', 'date', 'parents')
550 fields = ('prec', 'succs', 'flag', 'meta', 'date', 'parents')
551 # prec: nodeid, predecessors changesets
551 # prec: nodeid, predecessors changesets
552 # succs: tuple of nodeid, successor changesets (0-N length)
552 # succs: tuple of nodeid, successor changesets (0-N length)
553 # flag: integer, flag field carrying modifier for the markers (see doc)
553 # flag: integer, flag field carrying modifier for the markers (see doc)
554 # meta: binary blob, encoded metadata dictionary
554 # meta: binary blob, encoded metadata dictionary
555 # date: (float, int) tuple, date of marker creation
555 # date: (float, int) tuple, date of marker creation
556 # parents: (tuple of nodeid) or None, parents of predecessors
556 # parents: (tuple of nodeid) or None, parents of predecessors
557 # None is used when no data has been recorded
557 # None is used when no data has been recorded
558
558
559 def __init__(self, svfs, defaultformat=_fm1version, readonly=False):
559 def __init__(self, svfs, defaultformat=_fm1version, readonly=False):
560 # caches for various obsolescence related cache
560 # caches for various obsolescence related cache
561 self.caches = {}
561 self.caches = {}
562 self.svfs = svfs
562 self.svfs = svfs
563 self._defaultformat = defaultformat
563 self._defaultformat = defaultformat
564 self._readonly = readonly
564 self._readonly = readonly
565
565
566 def __iter__(self):
566 def __iter__(self):
567 return iter(self._all)
567 return iter(self._all)
568
568
569 def __len__(self):
569 def __len__(self):
570 return len(self._all)
570 return len(self._all)
571
571
572 def __nonzero__(self):
572 def __nonzero__(self):
573 if not self._cached('_all'):
573 if not self._cached('_all'):
574 try:
574 try:
575 return self.svfs.stat('obsstore').st_size > 1
575 return self.svfs.stat('obsstore').st_size > 1
576 except OSError as inst:
576 except OSError as inst:
577 if inst.errno != errno.ENOENT:
577 if inst.errno != errno.ENOENT:
578 raise
578 raise
579 # just build an empty _all list if no obsstore exists, which
579 # just build an empty _all list if no obsstore exists, which
580 # avoids further stat() syscalls
580 # avoids further stat() syscalls
581 return bool(self._all)
581 return bool(self._all)
582
582
583 __bool__ = __nonzero__
583 __bool__ = __nonzero__
584
584
585 @property
585 @property
586 def readonly(self):
586 def readonly(self):
587 """True if marker creation is disabled
587 """True if marker creation is disabled
588
588
589 Remove me in the future when obsolete marker is always on."""
589 Remove me in the future when obsolete marker is always on."""
590 return self._readonly
590 return self._readonly
591
591
592 def create(self, transaction, prec, succs=(), flag=0, parents=None,
592 def create(self, transaction, prec, succs=(), flag=0, parents=None,
593 date=None, metadata=None, ui=None):
593 date=None, metadata=None, ui=None):
594 """obsolete: add a new obsolete marker
594 """obsolete: add a new obsolete marker
595
595
596 * ensuring it is hashable
596 * ensuring it is hashable
597 * check mandatory metadata
597 * check mandatory metadata
598 * encode metadata
598 * encode metadata
599
599
600 If you are a human writing code creating marker you want to use the
600 If you are a human writing code creating marker you want to use the
601 `createmarkers` function in this module instead.
601 `createmarkers` function in this module instead.
602
602
603 return True if a new marker have been added, False if the markers
603 return True if a new marker have been added, False if the markers
604 already existed (no op).
604 already existed (no op).
605 """
605 """
606 if metadata is None:
606 if metadata is None:
607 metadata = {}
607 metadata = {}
608 if date is None:
608 if date is None:
609 if 'date' in metadata:
609 if 'date' in metadata:
610 # as a courtesy for out-of-tree extensions
610 # as a courtesy for out-of-tree extensions
611 date = util.parsedate(metadata.pop('date'))
611 date = util.parsedate(metadata.pop('date'))
612 elif ui is not None:
612 elif ui is not None:
613 date = ui.configdate('devel', 'default-date')
613 date = ui.configdate('devel', 'default-date')
614 if date is None:
614 if date is None:
615 date = util.makedate()
615 date = util.makedate()
616 else:
616 else:
617 date = util.makedate()
617 date = util.makedate()
618 if len(prec) != 20:
618 if len(prec) != 20:
619 raise ValueError(prec)
619 raise ValueError(prec)
620 for succ in succs:
620 for succ in succs:
621 if len(succ) != 20:
621 if len(succ) != 20:
622 raise ValueError(succ)
622 raise ValueError(succ)
623 if prec in succs:
623 if prec in succs:
624 raise ValueError(_('in-marker cycle with %s') % node.hex(prec))
624 raise ValueError(_('in-marker cycle with %s') % node.hex(prec))
625
625
626 metadata = tuple(sorted(metadata.iteritems()))
626 metadata = tuple(sorted(metadata.iteritems()))
627
627
628 marker = (bytes(prec), tuple(succs), int(flag), metadata, date, parents)
628 marker = (bytes(prec), tuple(succs), int(flag), metadata, date, parents)
629 return bool(self.add(transaction, [marker]))
629 return bool(self.add(transaction, [marker]))
630
630
631 def add(self, transaction, markers):
631 def add(self, transaction, markers):
632 """Add new markers to the store
632 """Add new markers to the store
633
633
634 Take care of filtering duplicate.
634 Take care of filtering duplicate.
635 Return the number of new marker."""
635 Return the number of new marker."""
636 if self._readonly:
636 if self._readonly:
637 raise error.Abort(_('creating obsolete markers is not enabled on '
637 raise error.Abort(_('creating obsolete markers is not enabled on '
638 'this repo'))
638 'this repo'))
639 known = set()
639 known = set()
640 getsuccessors = self.successors.get
640 getsuccessors = self.successors.get
641 new = []
641 new = []
642 for m in markers:
642 for m in markers:
643 if m not in getsuccessors(m[0], ()) and m not in known:
643 if m not in getsuccessors(m[0], ()) and m not in known:
644 known.add(m)
644 known.add(m)
645 new.append(m)
645 new.append(m)
646 if new:
646 if new:
647 f = self.svfs('obsstore', 'ab')
647 f = self.svfs('obsstore', 'ab')
648 try:
648 try:
649 offset = f.tell()
649 offset = f.tell()
650 transaction.add('obsstore', offset)
650 transaction.add('obsstore', offset)
651 # offset == 0: new file - add the version header
651 # offset == 0: new file - add the version header
652 data = b''.join(encodemarkers(new, offset == 0, self._version))
652 data = b''.join(encodemarkers(new, offset == 0, self._version))
653 f.write(data)
653 f.write(data)
654 finally:
654 finally:
655 # XXX: f.close() == filecache invalidation == obsstore rebuilt.
655 # XXX: f.close() == filecache invalidation == obsstore rebuilt.
656 # call 'filecacheentry.refresh()' here
656 # call 'filecacheentry.refresh()' here
657 f.close()
657 f.close()
658 addedmarkers = transaction.changes.get('obsmarkers')
658 addedmarkers = transaction.changes.get('obsmarkers')
659 if addedmarkers is not None:
659 if addedmarkers is not None:
660 addedmarkers.update(new)
660 addedmarkers.update(new)
661 self._addmarkers(new, data)
661 self._addmarkers(new, data)
662 # new marker *may* have changed several set. invalidate the cache.
662 # new marker *may* have changed several set. invalidate the cache.
663 self.caches.clear()
663 self.caches.clear()
664 # records the number of new markers for the transaction hooks
664 # records the number of new markers for the transaction hooks
665 previous = int(transaction.hookargs.get('new_obsmarkers', '0'))
665 previous = int(transaction.hookargs.get('new_obsmarkers', '0'))
666 transaction.hookargs['new_obsmarkers'] = str(previous + len(new))
666 transaction.hookargs['new_obsmarkers'] = str(previous + len(new))
667 return len(new)
667 return len(new)
668
668
669 def mergemarkers(self, transaction, data):
669 def mergemarkers(self, transaction, data):
670 """merge a binary stream of markers inside the obsstore
670 """merge a binary stream of markers inside the obsstore
671
671
672 Returns the number of new markers added."""
672 Returns the number of new markers added."""
673 version, markers = _readmarkers(data)
673 version, markers = _readmarkers(data)
674 return self.add(transaction, markers)
674 return self.add(transaction, markers)
675
675
676 @propertycache
676 @propertycache
677 def _data(self):
677 def _data(self):
678 return self.svfs.tryread('obsstore')
678 return self.svfs.tryread('obsstore')
679
679
680 @propertycache
680 @propertycache
681 def _version(self):
681 def _version(self):
682 if len(self._data) >= 1:
682 if len(self._data) >= 1:
683 return _readmarkerversion(self._data)
683 return _readmarkerversion(self._data)
684 else:
684 else:
685 return self._defaultformat
685 return self._defaultformat
686
686
687 @propertycache
687 @propertycache
688 def _all(self):
688 def _all(self):
689 data = self._data
689 data = self._data
690 if not data:
690 if not data:
691 return []
691 return []
692 self._version, markers = _readmarkers(data)
692 self._version, markers = _readmarkers(data)
693 markers = list(markers)
693 markers = list(markers)
694 _checkinvalidmarkers(markers)
694 _checkinvalidmarkers(markers)
695 return markers
695 return markers
696
696
697 @propertycache
697 @propertycache
698 def successors(self):
698 def successors(self):
699 successors = {}
699 successors = {}
700 _addsuccessors(successors, self._all)
700 _addsuccessors(successors, self._all)
701 return successors
701 return successors
702
702
703 @property
703 @property
704 def precursors(self):
704 def precursors(self):
705 msg = ("'obsstore.precursors' is deprecated, "
705 msg = ("'obsstore.precursors' is deprecated, "
706 "use 'obsstore.predecessors'")
706 "use 'obsstore.predecessors'")
707 util.nouideprecwarn(msg, '4.4')
707 util.nouideprecwarn(msg, '4.4')
708
708
709 return self.predecessors
709 return self.predecessors
710
710
711 @propertycache
711 @propertycache
712 def predecessors(self):
712 def predecessors(self):
713 predecessors = {}
713 predecessors = {}
714 _addpredecessors(predecessors, self._all)
714 _addpredecessors(predecessors, self._all)
715 return predecessors
715 return predecessors
716
716
717 @propertycache
717 @propertycache
718 def children(self):
718 def children(self):
719 children = {}
719 children = {}
720 _addchildren(children, self._all)
720 _addchildren(children, self._all)
721 return children
721 return children
722
722
723 def _cached(self, attr):
723 def _cached(self, attr):
724 return attr in self.__dict__
724 return attr in self.__dict__
725
725
726 def _addmarkers(self, markers, rawdata):
726 def _addmarkers(self, markers, rawdata):
727 markers = list(markers) # to allow repeated iteration
727 markers = list(markers) # to allow repeated iteration
728 self._data = self._data + rawdata
728 self._data = self._data + rawdata
729 self._all.extend(markers)
729 self._all.extend(markers)
730 if self._cached('successors'):
730 if self._cached('successors'):
731 _addsuccessors(self.successors, markers)
731 _addsuccessors(self.successors, markers)
732 if self._cached('predecessors'):
732 if self._cached('predecessors'):
733 _addpredecessors(self.predecessors, markers)
733 _addpredecessors(self.predecessors, markers)
734 if self._cached('children'):
734 if self._cached('children'):
735 _addchildren(self.children, markers)
735 _addchildren(self.children, markers)
736 _checkinvalidmarkers(markers)
736 _checkinvalidmarkers(markers)
737
737
738 def relevantmarkers(self, nodes):
738 def relevantmarkers(self, nodes):
739 """return a set of all obsolescence markers relevant to a set of nodes.
739 """return a set of all obsolescence markers relevant to a set of nodes.
740
740
741 "relevant" to a set of nodes mean:
741 "relevant" to a set of nodes mean:
742
742
743 - marker that use this changeset as successor
743 - marker that use this changeset as successor
744 - prune marker of direct children on this changeset
744 - prune marker of direct children on this changeset
745 - recursive application of the two rules on predecessors of these
745 - recursive application of the two rules on predecessors of these
746 markers
746 markers
747
747
748 It is a set so you cannot rely on order."""
748 It is a set so you cannot rely on order."""
749
749
750 pendingnodes = set(nodes)
750 pendingnodes = set(nodes)
751 seenmarkers = set()
751 seenmarkers = set()
752 seennodes = set(pendingnodes)
752 seennodes = set(pendingnodes)
753 precursorsmarkers = self.predecessors
753 precursorsmarkers = self.predecessors
754 succsmarkers = self.successors
754 succsmarkers = self.successors
755 children = self.children
755 children = self.children
756 while pendingnodes:
756 while pendingnodes:
757 direct = set()
757 direct = set()
758 for current in pendingnodes:
758 for current in pendingnodes:
759 direct.update(precursorsmarkers.get(current, ()))
759 direct.update(precursorsmarkers.get(current, ()))
760 pruned = [m for m in children.get(current, ()) if not m[1]]
760 pruned = [m for m in children.get(current, ()) if not m[1]]
761 direct.update(pruned)
761 direct.update(pruned)
762 pruned = [m for m in succsmarkers.get(current, ()) if not m[1]]
762 pruned = [m for m in succsmarkers.get(current, ()) if not m[1]]
763 direct.update(pruned)
763 direct.update(pruned)
764 direct -= seenmarkers
764 direct -= seenmarkers
765 pendingnodes = set([m[0] for m in direct])
765 pendingnodes = set([m[0] for m in direct])
766 seenmarkers |= direct
766 seenmarkers |= direct
767 pendingnodes -= seennodes
767 pendingnodes -= seennodes
768 seennodes |= pendingnodes
768 seennodes |= pendingnodes
769 return seenmarkers
769 return seenmarkers
770
770
771 def makestore(ui, repo):
771 def makestore(ui, repo):
772 """Create an obsstore instance from a repo."""
772 """Create an obsstore instance from a repo."""
773 # read default format for new obsstore.
773 # read default format for new obsstore.
774 # developer config: format.obsstore-version
774 # developer config: format.obsstore-version
775 defaultformat = ui.configint('format', 'obsstore-version')
775 defaultformat = ui.configint('format', 'obsstore-version')
776 # rely on obsstore class default when possible.
776 # rely on obsstore class default when possible.
777 kwargs = {}
777 kwargs = {}
778 if defaultformat is not None:
778 if defaultformat is not None:
779 kwargs['defaultformat'] = defaultformat
779 kwargs['defaultformat'] = defaultformat
780 readonly = not isenabled(repo, createmarkersopt)
780 readonly = not isenabled(repo, createmarkersopt)
781 store = obsstore(repo.svfs, readonly=readonly, **kwargs)
781 store = obsstore(repo.svfs, readonly=readonly, **kwargs)
782 if store and readonly:
782 if store and readonly:
783 ui.warn(_('obsolete feature not enabled but %i markers found!\n')
783 ui.warn(_('obsolete feature not enabled but %i markers found!\n')
784 % len(list(store)))
784 % len(list(store)))
785 return store
785 return store
786
786
787 def commonversion(versions):
787 def commonversion(versions):
788 """Return the newest version listed in both versions and our local formats.
788 """Return the newest version listed in both versions and our local formats.
789
789
790 Returns None if no common version exists.
790 Returns None if no common version exists.
791 """
791 """
792 versions.sort(reverse=True)
792 versions.sort(reverse=True)
793 # search for highest version known on both side
793 # search for highest version known on both side
794 for v in versions:
794 for v in versions:
795 if v in formats:
795 if v in formats:
796 return v
796 return v
797 return None
797 return None
798
798
799 # arbitrary picked to fit into 8K limit from HTTP server
799 # arbitrary picked to fit into 8K limit from HTTP server
800 # you have to take in account:
800 # you have to take in account:
801 # - the version header
801 # - the version header
802 # - the base85 encoding
802 # - the base85 encoding
803 _maxpayload = 5300
803 _maxpayload = 5300
804
804
805 def _pushkeyescape(markers):
805 def _pushkeyescape(markers):
806 """encode markers into a dict suitable for pushkey exchange
806 """encode markers into a dict suitable for pushkey exchange
807
807
808 - binary data is base85 encoded
808 - binary data is base85 encoded
809 - split in chunks smaller than 5300 bytes"""
809 - split in chunks smaller than 5300 bytes"""
810 keys = {}
810 keys = {}
811 parts = []
811 parts = []
812 currentlen = _maxpayload * 2 # ensure we create a new part
812 currentlen = _maxpayload * 2 # ensure we create a new part
813 for marker in markers:
813 for marker in markers:
814 nextdata = _fm0encodeonemarker(marker)
814 nextdata = _fm0encodeonemarker(marker)
815 if (len(nextdata) + currentlen > _maxpayload):
815 if (len(nextdata) + currentlen > _maxpayload):
816 currentpart = []
816 currentpart = []
817 currentlen = 0
817 currentlen = 0
818 parts.append(currentpart)
818 parts.append(currentpart)
819 currentpart.append(nextdata)
819 currentpart.append(nextdata)
820 currentlen += len(nextdata)
820 currentlen += len(nextdata)
821 for idx, part in enumerate(reversed(parts)):
821 for idx, part in enumerate(reversed(parts)):
822 data = ''.join([_pack('>B', _fm0version)] + part)
822 data = ''.join([_pack('>B', _fm0version)] + part)
823 keys['dump%i' % idx] = util.b85encode(data)
823 keys['dump%i' % idx] = util.b85encode(data)
824 return keys
824 return keys
825
825
826 def listmarkers(repo):
826 def listmarkers(repo):
827 """List markers over pushkey"""
827 """List markers over pushkey"""
828 if not repo.obsstore:
828 if not repo.obsstore:
829 return {}
829 return {}
830 return _pushkeyescape(sorted(repo.obsstore))
830 return _pushkeyescape(sorted(repo.obsstore))
831
831
832 def pushmarker(repo, key, old, new):
832 def pushmarker(repo, key, old, new):
833 """Push markers over pushkey"""
833 """Push markers over pushkey"""
834 if not key.startswith('dump'):
834 if not key.startswith('dump'):
835 repo.ui.warn(_('unknown key: %r') % key)
835 repo.ui.warn(_('unknown key: %r') % key)
836 return False
836 return False
837 if old:
837 if old:
838 repo.ui.warn(_('unexpected old value for %r') % key)
838 repo.ui.warn(_('unexpected old value for %r') % key)
839 return False
839 return False
840 data = util.b85decode(new)
840 data = util.b85decode(new)
841 lock = repo.lock()
841 lock = repo.lock()
842 try:
842 try:
843 tr = repo.transaction('pushkey: obsolete markers')
843 tr = repo.transaction('pushkey: obsolete markers')
844 try:
844 try:
845 repo.obsstore.mergemarkers(tr, data)
845 repo.obsstore.mergemarkers(tr, data)
846 repo.invalidatevolatilesets()
846 repo.invalidatevolatilesets()
847 tr.close()
847 tr.close()
848 return True
848 return True
849 finally:
849 finally:
850 tr.release()
850 tr.release()
851 finally:
851 finally:
852 lock.release()
852 lock.release()
853
853
854 # keep compatibility for the 4.3 cycle
854 # keep compatibility for the 4.3 cycle
855 def allprecursors(obsstore, nodes, ignoreflags=0):
855 def allprecursors(obsstore, nodes, ignoreflags=0):
856 movemsg = 'obsolete.allprecursors moved to obsutil.allprecursors'
856 movemsg = 'obsolete.allprecursors moved to obsutil.allprecursors'
857 util.nouideprecwarn(movemsg, '4.3')
857 util.nouideprecwarn(movemsg, '4.3')
858 return obsutil.allprecursors(obsstore, nodes, ignoreflags)
858 return obsutil.allprecursors(obsstore, nodes, ignoreflags)
859
859
860 def allsuccessors(obsstore, nodes, ignoreflags=0):
860 def allsuccessors(obsstore, nodes, ignoreflags=0):
861 movemsg = 'obsolete.allsuccessors moved to obsutil.allsuccessors'
861 movemsg = 'obsolete.allsuccessors moved to obsutil.allsuccessors'
862 util.nouideprecwarn(movemsg, '4.3')
862 util.nouideprecwarn(movemsg, '4.3')
863 return obsutil.allsuccessors(obsstore, nodes, ignoreflags)
863 return obsutil.allsuccessors(obsstore, nodes, ignoreflags)
864
864
865 def marker(repo, data):
865 def marker(repo, data):
866 movemsg = 'obsolete.marker moved to obsutil.marker'
866 movemsg = 'obsolete.marker moved to obsutil.marker'
867 repo.ui.deprecwarn(movemsg, '4.3')
867 repo.ui.deprecwarn(movemsg, '4.3')
868 return obsutil.marker(repo, data)
868 return obsutil.marker(repo, data)
869
869
870 def getmarkers(repo, nodes=None, exclusive=False):
870 def getmarkers(repo, nodes=None, exclusive=False):
871 movemsg = 'obsolete.getmarkers moved to obsutil.getmarkers'
871 movemsg = 'obsolete.getmarkers moved to obsutil.getmarkers'
872 repo.ui.deprecwarn(movemsg, '4.3')
872 repo.ui.deprecwarn(movemsg, '4.3')
873 return obsutil.getmarkers(repo, nodes=nodes, exclusive=exclusive)
873 return obsutil.getmarkers(repo, nodes=nodes, exclusive=exclusive)
874
874
875 def exclusivemarkers(repo, nodes):
875 def exclusivemarkers(repo, nodes):
876 movemsg = 'obsolete.exclusivemarkers moved to obsutil.exclusivemarkers'
876 movemsg = 'obsolete.exclusivemarkers moved to obsutil.exclusivemarkers'
877 repo.ui.deprecwarn(movemsg, '4.3')
877 repo.ui.deprecwarn(movemsg, '4.3')
878 return obsutil.exclusivemarkers(repo, nodes)
878 return obsutil.exclusivemarkers(repo, nodes)
879
879
880 def foreground(repo, nodes):
880 def foreground(repo, nodes):
881 movemsg = 'obsolete.foreground moved to obsutil.foreground'
881 movemsg = 'obsolete.foreground moved to obsutil.foreground'
882 repo.ui.deprecwarn(movemsg, '4.3')
882 repo.ui.deprecwarn(movemsg, '4.3')
883 return obsutil.foreground(repo, nodes)
883 return obsutil.foreground(repo, nodes)
884
884
885 def successorssets(repo, initialnode, cache=None):
885 def successorssets(repo, initialnode, cache=None):
886 movemsg = 'obsolete.successorssets moved to obsutil.successorssets'
886 movemsg = 'obsolete.successorssets moved to obsutil.successorssets'
887 repo.ui.deprecwarn(movemsg, '4.3')
887 repo.ui.deprecwarn(movemsg, '4.3')
888 return obsutil.successorssets(repo, initialnode, cache=cache)
888 return obsutil.successorssets(repo, initialnode, cache=cache)
889
889
890 # mapping of 'set-name' -> <function to compute this set>
890 # mapping of 'set-name' -> <function to compute this set>
891 cachefuncs = {}
891 cachefuncs = {}
892 def cachefor(name):
892 def cachefor(name):
893 """Decorator to register a function as computing the cache for a set"""
893 """Decorator to register a function as computing the cache for a set"""
894 def decorator(func):
894 def decorator(func):
895 if name in cachefuncs:
895 if name in cachefuncs:
896 msg = "duplicated registration for volatileset '%s' (existing: %r)"
896 msg = "duplicated registration for volatileset '%s' (existing: %r)"
897 raise error.ProgrammingError(msg % (name, cachefuncs[name]))
897 raise error.ProgrammingError(msg % (name, cachefuncs[name]))
898 cachefuncs[name] = func
898 cachefuncs[name] = func
899 return func
899 return func
900 return decorator
900 return decorator
901
901
902 def getrevs(repo, name):
902 def getrevs(repo, name):
903 """Return the set of revision that belong to the <name> set
903 """Return the set of revision that belong to the <name> set
904
904
905 Such access may compute the set and cache it for future use"""
905 Such access may compute the set and cache it for future use"""
906 repo = repo.unfiltered()
906 repo = repo.unfiltered()
907 if not repo.obsstore:
907 if not repo.obsstore:
908 return frozenset()
908 return frozenset()
909 if name not in repo.obsstore.caches:
909 if name not in repo.obsstore.caches:
910 repo.obsstore.caches[name] = cachefuncs[name](repo)
910 repo.obsstore.caches[name] = cachefuncs[name](repo)
911 return repo.obsstore.caches[name]
911 return repo.obsstore.caches[name]
912
912
913 # To be simple we need to invalidate obsolescence cache when:
913 # To be simple we need to invalidate obsolescence cache when:
914 #
914 #
915 # - new changeset is added:
915 # - new changeset is added:
916 # - public phase is changed
916 # - public phase is changed
917 # - obsolescence marker are added
917 # - obsolescence marker are added
918 # - strip is used a repo
918 # - strip is used a repo
919 def clearobscaches(repo):
919 def clearobscaches(repo):
920 """Remove all obsolescence related cache from a repo
920 """Remove all obsolescence related cache from a repo
921
921
922 This remove all cache in obsstore is the obsstore already exist on the
922 This remove all cache in obsstore is the obsstore already exist on the
923 repo.
923 repo.
924
924
925 (We could be smarter here given the exact event that trigger the cache
925 (We could be smarter here given the exact event that trigger the cache
926 clearing)"""
926 clearing)"""
927 # only clear cache is there is obsstore data in this repo
927 # only clear cache is there is obsstore data in this repo
928 if 'obsstore' in repo._filecache:
928 if 'obsstore' in repo._filecache:
929 repo.obsstore.caches.clear()
929 repo.obsstore.caches.clear()
930
930
931 def _mutablerevs(repo):
931 def _mutablerevs(repo):
932 """the set of mutable revision in the repository"""
932 """the set of mutable revision in the repository"""
933 return repo._phasecache.getrevset(repo, (phases.draft, phases.secret))
933 return repo._phasecache.getrevset(repo, (phases.draft, phases.secret))
934
934
935 @cachefor('obsolete')
935 @cachefor('obsolete')
936 def _computeobsoleteset(repo):
936 def _computeobsoleteset(repo):
937 """the set of obsolete revisions"""
937 """the set of obsolete revisions"""
938 getnode = repo.changelog.node
938 getnode = repo.changelog.node
939 notpublic = _mutablerevs(repo)
939 notpublic = _mutablerevs(repo)
940 isobs = repo.obsstore.successors.__contains__
940 isobs = repo.obsstore.successors.__contains__
941 obs = set(r for r in notpublic if isobs(getnode(r)))
941 obs = set(r for r in notpublic if isobs(getnode(r)))
942 return obs
942 return obs
943
943
944 @cachefor('unstable')
944 @cachefor('unstable')
945 def _computeunstableset(repo):
945 def _computeunstableset(repo):
946 msg = ("'unstable' volatile set is deprecated, "
946 msg = ("'unstable' volatile set is deprecated, "
947 "use 'orphan'")
947 "use 'orphan'")
948 repo.ui.deprecwarn(msg, '4.4')
948 repo.ui.deprecwarn(msg, '4.4')
949
949
950 return _computeorphanset(repo)
950 return _computeorphanset(repo)
951
951
952 @cachefor('orphan')
952 @cachefor('orphan')
953 def _computeorphanset(repo):
953 def _computeorphanset(repo):
954 """the set of non obsolete revisions with obsolete parents"""
954 """the set of non obsolete revisions with obsolete parents"""
955 pfunc = repo.changelog.parentrevs
955 pfunc = repo.changelog.parentrevs
956 mutable = _mutablerevs(repo)
956 mutable = _mutablerevs(repo)
957 obsolete = getrevs(repo, 'obsolete')
957 obsolete = getrevs(repo, 'obsolete')
958 others = mutable - obsolete
958 others = mutable - obsolete
959 unstable = set()
959 unstable = set()
960 for r in sorted(others):
960 for r in sorted(others):
961 # A rev is unstable if one of its parent is obsolete or unstable
961 # A rev is unstable if one of its parent is obsolete or unstable
962 # this works since we traverse following growing rev order
962 # this works since we traverse following growing rev order
963 for p in pfunc(r):
963 for p in pfunc(r):
964 if p in obsolete or p in unstable:
964 if p in obsolete or p in unstable:
965 unstable.add(r)
965 unstable.add(r)
966 break
966 break
967 return unstable
967 return unstable
968
968
969 @cachefor('suspended')
969 @cachefor('suspended')
970 def _computesuspendedset(repo):
970 def _computesuspendedset(repo):
971 """the set of obsolete parents with non obsolete descendants"""
971 """the set of obsolete parents with non obsolete descendants"""
972 suspended = repo.changelog.ancestors(getrevs(repo, 'orphan'))
972 suspended = repo.changelog.ancestors(getrevs(repo, 'orphan'))
973 return set(r for r in getrevs(repo, 'obsolete') if r in suspended)
973 return set(r for r in getrevs(repo, 'obsolete') if r in suspended)
974
974
975 @cachefor('extinct')
975 @cachefor('extinct')
976 def _computeextinctset(repo):
976 def _computeextinctset(repo):
977 """the set of obsolete parents without non obsolete descendants"""
977 """the set of obsolete parents without non obsolete descendants"""
978 return getrevs(repo, 'obsolete') - getrevs(repo, 'suspended')
978 return getrevs(repo, 'obsolete') - getrevs(repo, 'suspended')
979
979
980 @cachefor('bumped')
980 @cachefor('bumped')
981 def _computebumpedset(repo):
981 def _computebumpedset(repo):
982 msg = ("'bumped' volatile set is deprecated, "
982 msg = ("'bumped' volatile set is deprecated, "
983 "use 'phasedivergent'")
983 "use 'phasedivergent'")
984 repo.ui.deprecwarn(msg, '4.4')
984 repo.ui.deprecwarn(msg, '4.4')
985
985
986 return _computephasedivergentset(repo)
986 return _computephasedivergentset(repo)
987
987
988 @cachefor('phasedivergent')
988 @cachefor('phasedivergent')
989 def _computephasedivergentset(repo):
989 def _computephasedivergentset(repo):
990 """the set of revs trying to obsolete public revisions"""
990 """the set of revs trying to obsolete public revisions"""
991 bumped = set()
991 bumped = set()
992 # util function (avoid attribute lookup in the loop)
992 # util function (avoid attribute lookup in the loop)
993 phase = repo._phasecache.phase # would be faster to grab the full list
993 phase = repo._phasecache.phase # would be faster to grab the full list
994 public = phases.public
994 public = phases.public
995 cl = repo.changelog
995 cl = repo.changelog
996 torev = cl.nodemap.get
996 torev = cl.nodemap.get
997 for ctx in repo.set('(not public()) and (not obsolete())'):
997 for ctx in repo.set('(not public()) and (not obsolete())'):
998 rev = ctx.rev()
998 rev = ctx.rev()
999 # We only evaluate mutable, non-obsolete revision
999 # We only evaluate mutable, non-obsolete revision
1000 node = ctx.node()
1000 node = ctx.node()
1001 # (future) A cache of predecessors may worth if split is very common
1001 # (future) A cache of predecessors may worth if split is very common
1002 for pnode in obsutil.allpredecessors(repo.obsstore, [node],
1002 for pnode in obsutil.allpredecessors(repo.obsstore, [node],
1003 ignoreflags=bumpedfix):
1003 ignoreflags=bumpedfix):
1004 prev = torev(pnode) # unfiltered! but so is phasecache
1004 prev = torev(pnode) # unfiltered! but so is phasecache
1005 if (prev is not None) and (phase(repo, prev) <= public):
1005 if (prev is not None) and (phase(repo, prev) <= public):
1006 # we have a public predecessor
1006 # we have a public predecessor
1007 bumped.add(rev)
1007 bumped.add(rev)
1008 break # Next draft!
1008 break # Next draft!
1009 return bumped
1009 return bumped
1010
1010
1011 @cachefor('divergent')
1011 @cachefor('divergent')
1012 def _computedivergentset(repo):
1012 def _computedivergentset(repo):
1013 msg = ("'divergent' volatile set is deprecated, "
1013 msg = ("'divergent' volatile set is deprecated, "
1014 "use 'contentdivergent'")
1014 "use 'contentdivergent'")
1015 repo.ui.deprecwarn(msg, '4.4')
1015 repo.ui.deprecwarn(msg, '4.4')
1016
1016
1017 return _computecontentdivergentset(repo)
1017 return _computecontentdivergentset(repo)
1018
1018
1019 @cachefor('contentdivergent')
1019 @cachefor('contentdivergent')
1020 def _computecontentdivergentset(repo):
1020 def _computecontentdivergentset(repo):
1021 """the set of rev that compete to be the final successors of some revision.
1021 """the set of rev that compete to be the final successors of some revision.
1022 """
1022 """
1023 divergent = set()
1023 divergent = set()
1024 obsstore = repo.obsstore
1024 obsstore = repo.obsstore
1025 newermap = {}
1025 newermap = {}
1026 for ctx in repo.set('(not public()) - obsolete()'):
1026 for ctx in repo.set('(not public()) - obsolete()'):
1027 mark = obsstore.predecessors.get(ctx.node(), ())
1027 mark = obsstore.predecessors.get(ctx.node(), ())
1028 toprocess = set(mark)
1028 toprocess = set(mark)
1029 seen = set()
1029 seen = set()
1030 while toprocess:
1030 while toprocess:
1031 prec = toprocess.pop()[0]
1031 prec = toprocess.pop()[0]
1032 if prec in seen:
1032 if prec in seen:
1033 continue # emergency cycle hanging prevention
1033 continue # emergency cycle hanging prevention
1034 seen.add(prec)
1034 seen.add(prec)
1035 if prec not in newermap:
1035 if prec not in newermap:
1036 obsutil.successorssets(repo, prec, cache=newermap)
1036 obsutil.successorssets(repo, prec, cache=newermap)
1037 newer = [n for n in newermap[prec] if n]
1037 newer = [n for n in newermap[prec] if n]
1038 if len(newer) > 1:
1038 if len(newer) > 1:
1039 divergent.add(ctx.rev())
1039 divergent.add(ctx.rev())
1040 break
1040 break
1041 toprocess.update(obsstore.predecessors.get(prec, ()))
1041 toprocess.update(obsstore.predecessors.get(prec, ()))
1042 return divergent
1042 return divergent
1043
1043
1044
1044
1045 def createmarkers(repo, relations, flag=0, date=None, metadata=None,
1045 def createmarkers(repo, relations, flag=0, date=None, metadata=None,
1046 operation=None):
1046 operation=None):
1047 """Add obsolete markers between changesets in a repo
1047 """Add obsolete markers between changesets in a repo
1048
1048
1049 <relations> must be an iterable of (<old>, (<new>, ...)[,{metadata}])
1049 <relations> must be an iterable of (<old>, (<new>, ...)[,{metadata}])
1050 tuple. `old` and `news` are changectx. metadata is an optional dictionary
1050 tuple. `old` and `news` are changectx. metadata is an optional dictionary
1051 containing metadata for this marker only. It is merged with the global
1051 containing metadata for this marker only. It is merged with the global
1052 metadata specified through the `metadata` argument of this function,
1052 metadata specified through the `metadata` argument of this function,
1053
1053
1054 Trying to obsolete a public changeset will raise an exception.
1054 Trying to obsolete a public changeset will raise an exception.
1055
1055
1056 Current user and date are used except if specified otherwise in the
1056 Current user and date are used except if specified otherwise in the
1057 metadata attribute.
1057 metadata attribute.
1058
1058
1059 This function operates within a transaction of its own, but does
1059 This function operates within a transaction of its own, but does
1060 not take any lock on the repo.
1060 not take any lock on the repo.
1061 """
1061 """
1062 # prepare metadata
1062 # prepare metadata
1063 if metadata is None:
1063 if metadata is None:
1064 metadata = {}
1064 metadata = {}
1065 if 'user' not in metadata:
1065 if 'user' not in metadata:
1066 develuser = repo.ui.config('devel', 'user.obsmarker')
1066 develuser = repo.ui.config('devel', 'user.obsmarker')
1067 if develuser:
1067 if develuser:
1068 metadata['user'] = develuser
1068 metadata['user'] = develuser
1069 else:
1069 else:
1070 metadata['user'] = repo.ui.username()
1070 metadata['user'] = repo.ui.username()
1071
1071
1072 # Operation metadata handling
1072 # Operation metadata handling
1073 useoperation = repo.ui.configbool('experimental',
1073 useoperation = repo.ui.configbool('experimental',
1074 'evolution.track-operation')
1074 'evolution.track-operation')
1075 if useoperation and operation:
1075 if useoperation and operation:
1076 metadata['operation'] = operation
1076 metadata['operation'] = operation
1077
1077
1078 # Effect flag metadata handling
1078 # Effect flag metadata handling
1079 saveeffectflag = repo.ui.configbool('experimental',
1079 saveeffectflag = repo.ui.configbool('experimental',
1080 'effect-flags')
1080 'evolution.effect-flags')
1081
1081
1082 tr = repo.transaction('add-obsolescence-marker')
1082 tr = repo.transaction('add-obsolescence-marker')
1083 try:
1083 try:
1084 markerargs = []
1084 markerargs = []
1085 for rel in relations:
1085 for rel in relations:
1086 prec = rel[0]
1086 prec = rel[0]
1087 sucs = rel[1]
1087 sucs = rel[1]
1088 localmetadata = metadata.copy()
1088 localmetadata = metadata.copy()
1089 if 2 < len(rel):
1089 if 2 < len(rel):
1090 localmetadata.update(rel[2])
1090 localmetadata.update(rel[2])
1091
1091
1092 if not prec.mutable():
1092 if not prec.mutable():
1093 raise error.Abort(_("cannot obsolete public changeset: %s")
1093 raise error.Abort(_("cannot obsolete public changeset: %s")
1094 % prec,
1094 % prec,
1095 hint="see 'hg help phases' for details")
1095 hint="see 'hg help phases' for details")
1096 nprec = prec.node()
1096 nprec = prec.node()
1097 nsucs = tuple(s.node() for s in sucs)
1097 nsucs = tuple(s.node() for s in sucs)
1098 npare = None
1098 npare = None
1099 if not nsucs:
1099 if not nsucs:
1100 npare = tuple(p.node() for p in prec.parents())
1100 npare = tuple(p.node() for p in prec.parents())
1101 if nprec in nsucs:
1101 if nprec in nsucs:
1102 raise error.Abort(_("changeset %s cannot obsolete itself")
1102 raise error.Abort(_("changeset %s cannot obsolete itself")
1103 % prec)
1103 % prec)
1104
1104
1105 # Effect flag can be different by relation
1105 # Effect flag can be different by relation
1106 if saveeffectflag:
1106 if saveeffectflag:
1107 # The effect flag is saved in a versioned field name for future
1107 # The effect flag is saved in a versioned field name for future
1108 # evolution
1108 # evolution
1109 effectflag = obsutil.geteffectflag(rel)
1109 effectflag = obsutil.geteffectflag(rel)
1110 localmetadata[obsutil.EFFECTFLAGFIELD] = "%d" % effectflag
1110 localmetadata[obsutil.EFFECTFLAGFIELD] = "%d" % effectflag
1111
1111
1112 # Creating the marker causes the hidden cache to become invalid,
1112 # Creating the marker causes the hidden cache to become invalid,
1113 # which causes recomputation when we ask for prec.parents() above.
1113 # which causes recomputation when we ask for prec.parents() above.
1114 # Resulting in n^2 behavior. So let's prepare all of the args
1114 # Resulting in n^2 behavior. So let's prepare all of the args
1115 # first, then create the markers.
1115 # first, then create the markers.
1116 markerargs.append((nprec, nsucs, npare, localmetadata))
1116 markerargs.append((nprec, nsucs, npare, localmetadata))
1117
1117
1118 for args in markerargs:
1118 for args in markerargs:
1119 nprec, nsucs, npare, localmetadata = args
1119 nprec, nsucs, npare, localmetadata = args
1120 repo.obsstore.create(tr, nprec, nsucs, flag, parents=npare,
1120 repo.obsstore.create(tr, nprec, nsucs, flag, parents=npare,
1121 date=date, metadata=localmetadata,
1121 date=date, metadata=localmetadata,
1122 ui=repo.ui)
1122 ui=repo.ui)
1123 repo.filteredrevcache.clear()
1123 repo.filteredrevcache.clear()
1124 tr.close()
1124 tr.close()
1125 finally:
1125 finally:
1126 tr.release()
1126 tr.release()
@@ -1,167 +1,167 b''
1 Test the 'effect-flags' feature
1 Test the 'effect-flags' feature
2
2
3 Global setup
3 Global setup
4 ============
4 ============
5
5
6 $ . $TESTDIR/testlib/obsmarker-common.sh
6 $ . $TESTDIR/testlib/obsmarker-common.sh
7 $ cat >> $HGRCPATH <<EOF
7 $ cat >> $HGRCPATH <<EOF
8 > [ui]
8 > [ui]
9 > interactive = true
9 > interactive = true
10 > [phases]
10 > [phases]
11 > publish=False
11 > publish=False
12 > [extensions]
12 > [extensions]
13 > rebase =
13 > rebase =
14 > [experimental]
14 > [experimental]
15 > evolution = all
15 > evolution = all
16 > effect-flags = 1
16 > evolution.effect-flags = 1
17 > EOF
17 > EOF
18
18
19 $ hg init $TESTTMP/effect-flags
19 $ hg init $TESTTMP/effect-flags
20 $ cd $TESTTMP/effect-flags
20 $ cd $TESTTMP/effect-flags
21 $ mkcommit ROOT
21 $ mkcommit ROOT
22
22
23 amend touching the description only
23 amend touching the description only
24 -----------------------------------
24 -----------------------------------
25
25
26 $ mkcommit A0
26 $ mkcommit A0
27 $ hg commit --amend -m "A1"
27 $ hg commit --amend -m "A1"
28
28
29 check result
29 check result
30
30
31 $ hg debugobsolete --rev .
31 $ hg debugobsolete --rev .
32 471f378eab4c5e25f6c77f785b27c936efb22874 fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'test'}
32 471f378eab4c5e25f6c77f785b27c936efb22874 fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'test'}
33
33
34 amend touching the user only
34 amend touching the user only
35 ----------------------------
35 ----------------------------
36
36
37 $ mkcommit B0
37 $ mkcommit B0
38 $ hg commit --amend -u "bob <bob@bob.com>"
38 $ hg commit --amend -u "bob <bob@bob.com>"
39
39
40 check result
40 check result
41
41
42 $ hg debugobsolete --rev .
42 $ hg debugobsolete --rev .
43 ef4a313b1e0ade55718395d80e6b88c5ccd875eb 5485c92d34330dac9d7a63dc07e1e3373835b964 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '16', 'operation': 'amend', 'user': 'test'}
43 ef4a313b1e0ade55718395d80e6b88c5ccd875eb 5485c92d34330dac9d7a63dc07e1e3373835b964 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '16', 'operation': 'amend', 'user': 'test'}
44
44
45 amend touching the date only
45 amend touching the date only
46 ----------------------------
46 ----------------------------
47
47
48 $ mkcommit B1
48 $ mkcommit B1
49 $ hg commit --amend -d "42 0"
49 $ hg commit --amend -d "42 0"
50
50
51 check result
51 check result
52
52
53 $ hg debugobsolete --rev .
53 $ hg debugobsolete --rev .
54 2ef0680ff45038ac28c9f1ff3644341f54487280 4dd84345082e9e5291c2e6b3f335bbf8bf389378 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '32', 'operation': 'amend', 'user': 'test'}
54 2ef0680ff45038ac28c9f1ff3644341f54487280 4dd84345082e9e5291c2e6b3f335bbf8bf389378 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '32', 'operation': 'amend', 'user': 'test'}
55
55
56 amend touching the branch only
56 amend touching the branch only
57 ----------------------------
57 ----------------------------
58
58
59 $ mkcommit B2
59 $ mkcommit B2
60 $ hg branch my-branch
60 $ hg branch my-branch
61 marked working directory as branch my-branch
61 marked working directory as branch my-branch
62 (branches are permanent and global, did you want a bookmark?)
62 (branches are permanent and global, did you want a bookmark?)
63 $ hg commit --amend
63 $ hg commit --amend
64
64
65 check result
65 check result
66
66
67 $ hg debugobsolete --rev .
67 $ hg debugobsolete --rev .
68 bd3db8264ceebf1966319f5df3be7aac6acd1a8e 14a01456e0574f0e0a0b15b2345486a6364a8d79 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '64', 'operation': 'amend', 'user': 'test'}
68 bd3db8264ceebf1966319f5df3be7aac6acd1a8e 14a01456e0574f0e0a0b15b2345486a6364a8d79 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '64', 'operation': 'amend', 'user': 'test'}
69
69
70 $ hg up default
70 $ hg up default
71 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
71 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
72
72
73 rebase (parents change)
73 rebase (parents change)
74 -----------------------
74 -----------------------
75
75
76 $ mkcommit C0
76 $ mkcommit C0
77 $ mkcommit D0
77 $ mkcommit D0
78 $ hg rebase -r . -d 'desc(B0)'
78 $ hg rebase -r . -d 'desc(B0)'
79 rebasing 10:c85eff83a034 "D0" (tip)
79 rebasing 10:c85eff83a034 "D0" (tip)
80
80
81 check result
81 check result
82
82
83 $ hg debugobsolete --rev .
83 $ hg debugobsolete --rev .
84 c85eff83a0340efd9da52b806a94c350222f3371 da86aa2f19a30d6686b15cae15c7b6c908ec9699 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
84 c85eff83a0340efd9da52b806a94c350222f3371 da86aa2f19a30d6686b15cae15c7b6c908ec9699 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
85
85
86 amend touching the diff
86 amend touching the diff
87 -----------------------
87 -----------------------
88
88
89 $ mkcommit E0
89 $ mkcommit E0
90 $ echo 42 >> E0
90 $ echo 42 >> E0
91 $ hg commit --amend
91 $ hg commit --amend
92
92
93 check result
93 check result
94
94
95 $ hg debugobsolete --rev .
95 $ hg debugobsolete --rev .
96 ebfe0333e0d96f68a917afd97c0a0af87f1c3b5f 75781fdbdbf58a987516b00c980bccda1e9ae588 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
96 ebfe0333e0d96f68a917afd97c0a0af87f1c3b5f 75781fdbdbf58a987516b00c980bccda1e9ae588 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '8', 'operation': 'amend', 'user': 'test'}
97
97
98 amend with multiple effect (desc and meta)
98 amend with multiple effect (desc and meta)
99 -------------------------------------------
99 -------------------------------------------
100
100
101 $ mkcommit F0
101 $ mkcommit F0
102 $ hg branch my-other-branch
102 $ hg branch my-other-branch
103 marked working directory as branch my-other-branch
103 marked working directory as branch my-other-branch
104 $ hg commit --amend -m F1 -u "bob <bob@bob.com>" -d "42 0"
104 $ hg commit --amend -m F1 -u "bob <bob@bob.com>" -d "42 0"
105
105
106 check result
106 check result
107
107
108 $ hg debugobsolete --rev .
108 $ hg debugobsolete --rev .
109 fad47e5bd78e6aa4db1b5a0a1751bc12563655ff a94e0fd5f1c81d969381a76eb0d37ce499a44fae 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '113', 'operation': 'amend', 'user': 'test'}
109 fad47e5bd78e6aa4db1b5a0a1751bc12563655ff a94e0fd5f1c81d969381a76eb0d37ce499a44fae 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '113', 'operation': 'amend', 'user': 'test'}
110
110
111 rebase not touching the diff
111 rebase not touching the diff
112 ----------------------------
112 ----------------------------
113
113
114 $ cat << EOF > H0
114 $ cat << EOF > H0
115 > 0
115 > 0
116 > 1
116 > 1
117 > 2
117 > 2
118 > 3
118 > 3
119 > 4
119 > 4
120 > 5
120 > 5
121 > 6
121 > 6
122 > 7
122 > 7
123 > 8
123 > 8
124 > 9
124 > 9
125 > 10
125 > 10
126 > EOF
126 > EOF
127 $ hg add H0
127 $ hg add H0
128 $ hg commit -m 'H0'
128 $ hg commit -m 'H0'
129 $ echo "H1" >> H0
129 $ echo "H1" >> H0
130 $ hg commit -m "H1"
130 $ hg commit -m "H1"
131 $ hg up -r "desc(H0)"
131 $ hg up -r "desc(H0)"
132 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
132 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
133 $ cat << EOF > H0
133 $ cat << EOF > H0
134 > H2
134 > H2
135 > 0
135 > 0
136 > 1
136 > 1
137 > 2
137 > 2
138 > 3
138 > 3
139 > 4
139 > 4
140 > 5
140 > 5
141 > 6
141 > 6
142 > 7
142 > 7
143 > 8
143 > 8
144 > 9
144 > 9
145 > 10
145 > 10
146 > EOF
146 > EOF
147 $ hg commit -m "H2"
147 $ hg commit -m "H2"
148 created new head
148 created new head
149 $ hg rebase -s "desc(H1)" -d "desc(H2)" -t :merge3
149 $ hg rebase -s "desc(H1)" -d "desc(H2)" -t :merge3
150 rebasing 17:b57fed8d8322 "H1"
150 rebasing 17:b57fed8d8322 "H1"
151 merging H0
151 merging H0
152 $ hg debugobsolete -r tip
152 $ hg debugobsolete -r tip
153 b57fed8d83228a8ae3748d8c3760a77638dd4f8c e509e2eb3df5d131ff7c02350bf2a9edd0c09478 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
153 b57fed8d83228a8ae3748d8c3760a77638dd4f8c e509e2eb3df5d131ff7c02350bf2a9edd0c09478 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '4', 'operation': 'rebase', 'user': 'test'}
154
154
155 amend closing the branch should be detected as meta change
155 amend closing the branch should be detected as meta change
156 ----------------------------------------------------------
156 ----------------------------------------------------------
157
157
158 $ hg branch closedbranch
158 $ hg branch closedbranch
159 marked working directory as branch closedbranch
159 marked working directory as branch closedbranch
160 $ mkcommit G0
160 $ mkcommit G0
161 $ mkcommit I0
161 $ mkcommit I0
162 $ hg commit --amend --close-branch
162 $ hg commit --amend --close-branch
163
163
164 check result
164 check result
165
165
166 $ hg debugobsolete -r .
166 $ hg debugobsolete -r .
167 2f599e54c1c6974299065cdf54e1ad640bfb7b5d 12c6238b5e371eea00fd2013b12edce3f070928b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '2', 'operation': 'amend', 'user': 'test'}
167 2f599e54c1c6974299065cdf54e1ad640bfb7b5d 12c6238b5e371eea00fd2013b12edce3f070928b 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '2', 'operation': 'amend', 'user': 'test'}
General Comments 0
You need to be logged in to leave comments. Login now