##// END OF EJS Templates
side-data: drop the associated config and requirements...
marmoute -
r48003:21b3e611 default
parent child Browse files
Show More
@@ -1,2692 +1,2686 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
18
19 def loadconfigtable(ui, extname, configtable):
19 def loadconfigtable(ui, extname, configtable):
20 """update config item known to the ui with the extension ones"""
20 """update config item known to the ui with the extension ones"""
21 for section, items in sorted(configtable.items()):
21 for section, items in sorted(configtable.items()):
22 knownitems = ui._knownconfig.setdefault(section, itemregister())
22 knownitems = ui._knownconfig.setdefault(section, itemregister())
23 knownkeys = set(knownitems)
23 knownkeys = set(knownitems)
24 newkeys = set(items)
24 newkeys = set(items)
25 for key in sorted(knownkeys & newkeys):
25 for key in sorted(knownkeys & newkeys):
26 msg = b"extension '%s' overwrite config item '%s.%s'"
26 msg = b"extension '%s' overwrite config item '%s.%s'"
27 msg %= (extname, section, key)
27 msg %= (extname, section, key)
28 ui.develwarn(msg, config=b'warn-config')
28 ui.develwarn(msg, config=b'warn-config')
29
29
30 knownitems.update(items)
30 knownitems.update(items)
31
31
32
32
33 class configitem(object):
33 class configitem(object):
34 """represent a known config item
34 """represent a known config item
35
35
36 :section: the official config section where to find this item,
36 :section: the official config section where to find this item,
37 :name: the official name within the section,
37 :name: the official name within the section,
38 :default: default value for this item,
38 :default: default value for this item,
39 :alias: optional list of tuples as alternatives,
39 :alias: optional list of tuples as alternatives,
40 :generic: this is a generic definition, match name using regular expression.
40 :generic: this is a generic definition, match name using regular expression.
41 """
41 """
42
42
43 def __init__(
43 def __init__(
44 self,
44 self,
45 section,
45 section,
46 name,
46 name,
47 default=None,
47 default=None,
48 alias=(),
48 alias=(),
49 generic=False,
49 generic=False,
50 priority=0,
50 priority=0,
51 experimental=False,
51 experimental=False,
52 ):
52 ):
53 self.section = section
53 self.section = section
54 self.name = name
54 self.name = name
55 self.default = default
55 self.default = default
56 self.alias = list(alias)
56 self.alias = list(alias)
57 self.generic = generic
57 self.generic = generic
58 self.priority = priority
58 self.priority = priority
59 self.experimental = experimental
59 self.experimental = experimental
60 self._re = None
60 self._re = None
61 if generic:
61 if generic:
62 self._re = re.compile(self.name)
62 self._re = re.compile(self.name)
63
63
64
64
65 class itemregister(dict):
65 class itemregister(dict):
66 """A specialized dictionary that can handle wild-card selection"""
66 """A specialized dictionary that can handle wild-card selection"""
67
67
68 def __init__(self):
68 def __init__(self):
69 super(itemregister, self).__init__()
69 super(itemregister, self).__init__()
70 self._generics = set()
70 self._generics = set()
71
71
72 def update(self, other):
72 def update(self, other):
73 super(itemregister, self).update(other)
73 super(itemregister, self).update(other)
74 self._generics.update(other._generics)
74 self._generics.update(other._generics)
75
75
76 def __setitem__(self, key, item):
76 def __setitem__(self, key, item):
77 super(itemregister, self).__setitem__(key, item)
77 super(itemregister, self).__setitem__(key, item)
78 if item.generic:
78 if item.generic:
79 self._generics.add(item)
79 self._generics.add(item)
80
80
81 def get(self, key):
81 def get(self, key):
82 baseitem = super(itemregister, self).get(key)
82 baseitem = super(itemregister, self).get(key)
83 if baseitem is not None and not baseitem.generic:
83 if baseitem is not None and not baseitem.generic:
84 return baseitem
84 return baseitem
85
85
86 # search for a matching generic item
86 # search for a matching generic item
87 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
87 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
88 for item in generics:
88 for item in generics:
89 # we use 'match' instead of 'search' to make the matching simpler
89 # we use 'match' instead of 'search' to make the matching simpler
90 # for people unfamiliar with regular expression. Having the match
90 # for people unfamiliar with regular expression. Having the match
91 # rooted to the start of the string will produce less surprising
91 # rooted to the start of the string will produce less surprising
92 # result for user writing simple regex for sub-attribute.
92 # result for user writing simple regex for sub-attribute.
93 #
93 #
94 # For example using "color\..*" match produces an unsurprising
94 # For example using "color\..*" match produces an unsurprising
95 # result, while using search could suddenly match apparently
95 # result, while using search could suddenly match apparently
96 # unrelated configuration that happens to contains "color."
96 # unrelated configuration that happens to contains "color."
97 # anywhere. This is a tradeoff where we favor requiring ".*" on
97 # anywhere. This is a tradeoff where we favor requiring ".*" on
98 # some match to avoid the need to prefix most pattern with "^".
98 # some match to avoid the need to prefix most pattern with "^".
99 # The "^" seems more error prone.
99 # The "^" seems more error prone.
100 if item._re.match(key):
100 if item._re.match(key):
101 return item
101 return item
102
102
103 return None
103 return None
104
104
105
105
106 coreitems = {}
106 coreitems = {}
107
107
108
108
109 def _register(configtable, *args, **kwargs):
109 def _register(configtable, *args, **kwargs):
110 item = configitem(*args, **kwargs)
110 item = configitem(*args, **kwargs)
111 section = configtable.setdefault(item.section, itemregister())
111 section = configtable.setdefault(item.section, itemregister())
112 if item.name in section:
112 if item.name in section:
113 msg = b"duplicated config item registration for '%s.%s'"
113 msg = b"duplicated config item registration for '%s.%s'"
114 raise error.ProgrammingError(msg % (item.section, item.name))
114 raise error.ProgrammingError(msg % (item.section, item.name))
115 section[item.name] = item
115 section[item.name] = item
116
116
117
117
118 # special value for case where the default is derived from other values
118 # special value for case where the default is derived from other values
119 dynamicdefault = object()
119 dynamicdefault = object()
120
120
121 # Registering actual config items
121 # Registering actual config items
122
122
123
123
124 def getitemregister(configtable):
124 def getitemregister(configtable):
125 f = functools.partial(_register, configtable)
125 f = functools.partial(_register, configtable)
126 # export pseudo enum as configitem.*
126 # export pseudo enum as configitem.*
127 f.dynamicdefault = dynamicdefault
127 f.dynamicdefault = dynamicdefault
128 return f
128 return f
129
129
130
130
131 coreconfigitem = getitemregister(coreitems)
131 coreconfigitem = getitemregister(coreitems)
132
132
133
133
134 def _registerdiffopts(section, configprefix=b''):
134 def _registerdiffopts(section, configprefix=b''):
135 coreconfigitem(
135 coreconfigitem(
136 section,
136 section,
137 configprefix + b'nodates',
137 configprefix + b'nodates',
138 default=False,
138 default=False,
139 )
139 )
140 coreconfigitem(
140 coreconfigitem(
141 section,
141 section,
142 configprefix + b'showfunc',
142 configprefix + b'showfunc',
143 default=False,
143 default=False,
144 )
144 )
145 coreconfigitem(
145 coreconfigitem(
146 section,
146 section,
147 configprefix + b'unified',
147 configprefix + b'unified',
148 default=None,
148 default=None,
149 )
149 )
150 coreconfigitem(
150 coreconfigitem(
151 section,
151 section,
152 configprefix + b'git',
152 configprefix + b'git',
153 default=False,
153 default=False,
154 )
154 )
155 coreconfigitem(
155 coreconfigitem(
156 section,
156 section,
157 configprefix + b'ignorews',
157 configprefix + b'ignorews',
158 default=False,
158 default=False,
159 )
159 )
160 coreconfigitem(
160 coreconfigitem(
161 section,
161 section,
162 configprefix + b'ignorewsamount',
162 configprefix + b'ignorewsamount',
163 default=False,
163 default=False,
164 )
164 )
165 coreconfigitem(
165 coreconfigitem(
166 section,
166 section,
167 configprefix + b'ignoreblanklines',
167 configprefix + b'ignoreblanklines',
168 default=False,
168 default=False,
169 )
169 )
170 coreconfigitem(
170 coreconfigitem(
171 section,
171 section,
172 configprefix + b'ignorewseol',
172 configprefix + b'ignorewseol',
173 default=False,
173 default=False,
174 )
174 )
175 coreconfigitem(
175 coreconfigitem(
176 section,
176 section,
177 configprefix + b'nobinary',
177 configprefix + b'nobinary',
178 default=False,
178 default=False,
179 )
179 )
180 coreconfigitem(
180 coreconfigitem(
181 section,
181 section,
182 configprefix + b'noprefix',
182 configprefix + b'noprefix',
183 default=False,
183 default=False,
184 )
184 )
185 coreconfigitem(
185 coreconfigitem(
186 section,
186 section,
187 configprefix + b'word-diff',
187 configprefix + b'word-diff',
188 default=False,
188 default=False,
189 )
189 )
190
190
191
191
192 coreconfigitem(
192 coreconfigitem(
193 b'alias',
193 b'alias',
194 b'.*',
194 b'.*',
195 default=dynamicdefault,
195 default=dynamicdefault,
196 generic=True,
196 generic=True,
197 )
197 )
198 coreconfigitem(
198 coreconfigitem(
199 b'auth',
199 b'auth',
200 b'cookiefile',
200 b'cookiefile',
201 default=None,
201 default=None,
202 )
202 )
203 _registerdiffopts(section=b'annotate')
203 _registerdiffopts(section=b'annotate')
204 # bookmarks.pushing: internal hack for discovery
204 # bookmarks.pushing: internal hack for discovery
205 coreconfigitem(
205 coreconfigitem(
206 b'bookmarks',
206 b'bookmarks',
207 b'pushing',
207 b'pushing',
208 default=list,
208 default=list,
209 )
209 )
210 # bundle.mainreporoot: internal hack for bundlerepo
210 # bundle.mainreporoot: internal hack for bundlerepo
211 coreconfigitem(
211 coreconfigitem(
212 b'bundle',
212 b'bundle',
213 b'mainreporoot',
213 b'mainreporoot',
214 default=b'',
214 default=b'',
215 )
215 )
216 coreconfigitem(
216 coreconfigitem(
217 b'censor',
217 b'censor',
218 b'policy',
218 b'policy',
219 default=b'abort',
219 default=b'abort',
220 experimental=True,
220 experimental=True,
221 )
221 )
222 coreconfigitem(
222 coreconfigitem(
223 b'chgserver',
223 b'chgserver',
224 b'idletimeout',
224 b'idletimeout',
225 default=3600,
225 default=3600,
226 )
226 )
227 coreconfigitem(
227 coreconfigitem(
228 b'chgserver',
228 b'chgserver',
229 b'skiphash',
229 b'skiphash',
230 default=False,
230 default=False,
231 )
231 )
232 coreconfigitem(
232 coreconfigitem(
233 b'cmdserver',
233 b'cmdserver',
234 b'log',
234 b'log',
235 default=None,
235 default=None,
236 )
236 )
237 coreconfigitem(
237 coreconfigitem(
238 b'cmdserver',
238 b'cmdserver',
239 b'max-log-files',
239 b'max-log-files',
240 default=7,
240 default=7,
241 )
241 )
242 coreconfigitem(
242 coreconfigitem(
243 b'cmdserver',
243 b'cmdserver',
244 b'max-log-size',
244 b'max-log-size',
245 default=b'1 MB',
245 default=b'1 MB',
246 )
246 )
247 coreconfigitem(
247 coreconfigitem(
248 b'cmdserver',
248 b'cmdserver',
249 b'max-repo-cache',
249 b'max-repo-cache',
250 default=0,
250 default=0,
251 experimental=True,
251 experimental=True,
252 )
252 )
253 coreconfigitem(
253 coreconfigitem(
254 b'cmdserver',
254 b'cmdserver',
255 b'message-encodings',
255 b'message-encodings',
256 default=list,
256 default=list,
257 )
257 )
258 coreconfigitem(
258 coreconfigitem(
259 b'cmdserver',
259 b'cmdserver',
260 b'track-log',
260 b'track-log',
261 default=lambda: [b'chgserver', b'cmdserver', b'repocache'],
261 default=lambda: [b'chgserver', b'cmdserver', b'repocache'],
262 )
262 )
263 coreconfigitem(
263 coreconfigitem(
264 b'cmdserver',
264 b'cmdserver',
265 b'shutdown-on-interrupt',
265 b'shutdown-on-interrupt',
266 default=True,
266 default=True,
267 )
267 )
268 coreconfigitem(
268 coreconfigitem(
269 b'color',
269 b'color',
270 b'.*',
270 b'.*',
271 default=None,
271 default=None,
272 generic=True,
272 generic=True,
273 )
273 )
274 coreconfigitem(
274 coreconfigitem(
275 b'color',
275 b'color',
276 b'mode',
276 b'mode',
277 default=b'auto',
277 default=b'auto',
278 )
278 )
279 coreconfigitem(
279 coreconfigitem(
280 b'color',
280 b'color',
281 b'pagermode',
281 b'pagermode',
282 default=dynamicdefault,
282 default=dynamicdefault,
283 )
283 )
284 coreconfigitem(
284 coreconfigitem(
285 b'command-templates',
285 b'command-templates',
286 b'graphnode',
286 b'graphnode',
287 default=None,
287 default=None,
288 alias=[(b'ui', b'graphnodetemplate')],
288 alias=[(b'ui', b'graphnodetemplate')],
289 )
289 )
290 coreconfigitem(
290 coreconfigitem(
291 b'command-templates',
291 b'command-templates',
292 b'log',
292 b'log',
293 default=None,
293 default=None,
294 alias=[(b'ui', b'logtemplate')],
294 alias=[(b'ui', b'logtemplate')],
295 )
295 )
296 coreconfigitem(
296 coreconfigitem(
297 b'command-templates',
297 b'command-templates',
298 b'mergemarker',
298 b'mergemarker',
299 default=(
299 default=(
300 b'{node|short} '
300 b'{node|short} '
301 b'{ifeq(tags, "tip", "", '
301 b'{ifeq(tags, "tip", "", '
302 b'ifeq(tags, "", "", "{tags} "))}'
302 b'ifeq(tags, "", "", "{tags} "))}'
303 b'{if(bookmarks, "{bookmarks} ")}'
303 b'{if(bookmarks, "{bookmarks} ")}'
304 b'{ifeq(branch, "default", "", "{branch} ")}'
304 b'{ifeq(branch, "default", "", "{branch} ")}'
305 b'- {author|user}: {desc|firstline}'
305 b'- {author|user}: {desc|firstline}'
306 ),
306 ),
307 alias=[(b'ui', b'mergemarkertemplate')],
307 alias=[(b'ui', b'mergemarkertemplate')],
308 )
308 )
309 coreconfigitem(
309 coreconfigitem(
310 b'command-templates',
310 b'command-templates',
311 b'pre-merge-tool-output',
311 b'pre-merge-tool-output',
312 default=None,
312 default=None,
313 alias=[(b'ui', b'pre-merge-tool-output-template')],
313 alias=[(b'ui', b'pre-merge-tool-output-template')],
314 )
314 )
315 coreconfigitem(
315 coreconfigitem(
316 b'command-templates',
316 b'command-templates',
317 b'oneline-summary',
317 b'oneline-summary',
318 default=None,
318 default=None,
319 )
319 )
320 coreconfigitem(
320 coreconfigitem(
321 b'command-templates',
321 b'command-templates',
322 b'oneline-summary.*',
322 b'oneline-summary.*',
323 default=dynamicdefault,
323 default=dynamicdefault,
324 generic=True,
324 generic=True,
325 )
325 )
326 _registerdiffopts(section=b'commands', configprefix=b'commit.interactive.')
326 _registerdiffopts(section=b'commands', configprefix=b'commit.interactive.')
327 coreconfigitem(
327 coreconfigitem(
328 b'commands',
328 b'commands',
329 b'commit.post-status',
329 b'commit.post-status',
330 default=False,
330 default=False,
331 )
331 )
332 coreconfigitem(
332 coreconfigitem(
333 b'commands',
333 b'commands',
334 b'grep.all-files',
334 b'grep.all-files',
335 default=False,
335 default=False,
336 experimental=True,
336 experimental=True,
337 )
337 )
338 coreconfigitem(
338 coreconfigitem(
339 b'commands',
339 b'commands',
340 b'merge.require-rev',
340 b'merge.require-rev',
341 default=False,
341 default=False,
342 )
342 )
343 coreconfigitem(
343 coreconfigitem(
344 b'commands',
344 b'commands',
345 b'push.require-revs',
345 b'push.require-revs',
346 default=False,
346 default=False,
347 )
347 )
348 coreconfigitem(
348 coreconfigitem(
349 b'commands',
349 b'commands',
350 b'resolve.confirm',
350 b'resolve.confirm',
351 default=False,
351 default=False,
352 )
352 )
353 coreconfigitem(
353 coreconfigitem(
354 b'commands',
354 b'commands',
355 b'resolve.explicit-re-merge',
355 b'resolve.explicit-re-merge',
356 default=False,
356 default=False,
357 )
357 )
358 coreconfigitem(
358 coreconfigitem(
359 b'commands',
359 b'commands',
360 b'resolve.mark-check',
360 b'resolve.mark-check',
361 default=b'none',
361 default=b'none',
362 )
362 )
363 _registerdiffopts(section=b'commands', configprefix=b'revert.interactive.')
363 _registerdiffopts(section=b'commands', configprefix=b'revert.interactive.')
364 coreconfigitem(
364 coreconfigitem(
365 b'commands',
365 b'commands',
366 b'show.aliasprefix',
366 b'show.aliasprefix',
367 default=list,
367 default=list,
368 )
368 )
369 coreconfigitem(
369 coreconfigitem(
370 b'commands',
370 b'commands',
371 b'status.relative',
371 b'status.relative',
372 default=False,
372 default=False,
373 )
373 )
374 coreconfigitem(
374 coreconfigitem(
375 b'commands',
375 b'commands',
376 b'status.skipstates',
376 b'status.skipstates',
377 default=[],
377 default=[],
378 experimental=True,
378 experimental=True,
379 )
379 )
380 coreconfigitem(
380 coreconfigitem(
381 b'commands',
381 b'commands',
382 b'status.terse',
382 b'status.terse',
383 default=b'',
383 default=b'',
384 )
384 )
385 coreconfigitem(
385 coreconfigitem(
386 b'commands',
386 b'commands',
387 b'status.verbose',
387 b'status.verbose',
388 default=False,
388 default=False,
389 )
389 )
390 coreconfigitem(
390 coreconfigitem(
391 b'commands',
391 b'commands',
392 b'update.check',
392 b'update.check',
393 default=None,
393 default=None,
394 )
394 )
395 coreconfigitem(
395 coreconfigitem(
396 b'commands',
396 b'commands',
397 b'update.requiredest',
397 b'update.requiredest',
398 default=False,
398 default=False,
399 )
399 )
400 coreconfigitem(
400 coreconfigitem(
401 b'committemplate',
401 b'committemplate',
402 b'.*',
402 b'.*',
403 default=None,
403 default=None,
404 generic=True,
404 generic=True,
405 )
405 )
406 coreconfigitem(
406 coreconfigitem(
407 b'convert',
407 b'convert',
408 b'bzr.saverev',
408 b'bzr.saverev',
409 default=True,
409 default=True,
410 )
410 )
411 coreconfigitem(
411 coreconfigitem(
412 b'convert',
412 b'convert',
413 b'cvsps.cache',
413 b'cvsps.cache',
414 default=True,
414 default=True,
415 )
415 )
416 coreconfigitem(
416 coreconfigitem(
417 b'convert',
417 b'convert',
418 b'cvsps.fuzz',
418 b'cvsps.fuzz',
419 default=60,
419 default=60,
420 )
420 )
421 coreconfigitem(
421 coreconfigitem(
422 b'convert',
422 b'convert',
423 b'cvsps.logencoding',
423 b'cvsps.logencoding',
424 default=None,
424 default=None,
425 )
425 )
426 coreconfigitem(
426 coreconfigitem(
427 b'convert',
427 b'convert',
428 b'cvsps.mergefrom',
428 b'cvsps.mergefrom',
429 default=None,
429 default=None,
430 )
430 )
431 coreconfigitem(
431 coreconfigitem(
432 b'convert',
432 b'convert',
433 b'cvsps.mergeto',
433 b'cvsps.mergeto',
434 default=None,
434 default=None,
435 )
435 )
436 coreconfigitem(
436 coreconfigitem(
437 b'convert',
437 b'convert',
438 b'git.committeractions',
438 b'git.committeractions',
439 default=lambda: [b'messagedifferent'],
439 default=lambda: [b'messagedifferent'],
440 )
440 )
441 coreconfigitem(
441 coreconfigitem(
442 b'convert',
442 b'convert',
443 b'git.extrakeys',
443 b'git.extrakeys',
444 default=list,
444 default=list,
445 )
445 )
446 coreconfigitem(
446 coreconfigitem(
447 b'convert',
447 b'convert',
448 b'git.findcopiesharder',
448 b'git.findcopiesharder',
449 default=False,
449 default=False,
450 )
450 )
451 coreconfigitem(
451 coreconfigitem(
452 b'convert',
452 b'convert',
453 b'git.remoteprefix',
453 b'git.remoteprefix',
454 default=b'remote',
454 default=b'remote',
455 )
455 )
456 coreconfigitem(
456 coreconfigitem(
457 b'convert',
457 b'convert',
458 b'git.renamelimit',
458 b'git.renamelimit',
459 default=400,
459 default=400,
460 )
460 )
461 coreconfigitem(
461 coreconfigitem(
462 b'convert',
462 b'convert',
463 b'git.saverev',
463 b'git.saverev',
464 default=True,
464 default=True,
465 )
465 )
466 coreconfigitem(
466 coreconfigitem(
467 b'convert',
467 b'convert',
468 b'git.similarity',
468 b'git.similarity',
469 default=50,
469 default=50,
470 )
470 )
471 coreconfigitem(
471 coreconfigitem(
472 b'convert',
472 b'convert',
473 b'git.skipsubmodules',
473 b'git.skipsubmodules',
474 default=False,
474 default=False,
475 )
475 )
476 coreconfigitem(
476 coreconfigitem(
477 b'convert',
477 b'convert',
478 b'hg.clonebranches',
478 b'hg.clonebranches',
479 default=False,
479 default=False,
480 )
480 )
481 coreconfigitem(
481 coreconfigitem(
482 b'convert',
482 b'convert',
483 b'hg.ignoreerrors',
483 b'hg.ignoreerrors',
484 default=False,
484 default=False,
485 )
485 )
486 coreconfigitem(
486 coreconfigitem(
487 b'convert',
487 b'convert',
488 b'hg.preserve-hash',
488 b'hg.preserve-hash',
489 default=False,
489 default=False,
490 )
490 )
491 coreconfigitem(
491 coreconfigitem(
492 b'convert',
492 b'convert',
493 b'hg.revs',
493 b'hg.revs',
494 default=None,
494 default=None,
495 )
495 )
496 coreconfigitem(
496 coreconfigitem(
497 b'convert',
497 b'convert',
498 b'hg.saverev',
498 b'hg.saverev',
499 default=False,
499 default=False,
500 )
500 )
501 coreconfigitem(
501 coreconfigitem(
502 b'convert',
502 b'convert',
503 b'hg.sourcename',
503 b'hg.sourcename',
504 default=None,
504 default=None,
505 )
505 )
506 coreconfigitem(
506 coreconfigitem(
507 b'convert',
507 b'convert',
508 b'hg.startrev',
508 b'hg.startrev',
509 default=None,
509 default=None,
510 )
510 )
511 coreconfigitem(
511 coreconfigitem(
512 b'convert',
512 b'convert',
513 b'hg.tagsbranch',
513 b'hg.tagsbranch',
514 default=b'default',
514 default=b'default',
515 )
515 )
516 coreconfigitem(
516 coreconfigitem(
517 b'convert',
517 b'convert',
518 b'hg.usebranchnames',
518 b'hg.usebranchnames',
519 default=True,
519 default=True,
520 )
520 )
521 coreconfigitem(
521 coreconfigitem(
522 b'convert',
522 b'convert',
523 b'ignoreancestorcheck',
523 b'ignoreancestorcheck',
524 default=False,
524 default=False,
525 experimental=True,
525 experimental=True,
526 )
526 )
527 coreconfigitem(
527 coreconfigitem(
528 b'convert',
528 b'convert',
529 b'localtimezone',
529 b'localtimezone',
530 default=False,
530 default=False,
531 )
531 )
532 coreconfigitem(
532 coreconfigitem(
533 b'convert',
533 b'convert',
534 b'p4.encoding',
534 b'p4.encoding',
535 default=dynamicdefault,
535 default=dynamicdefault,
536 )
536 )
537 coreconfigitem(
537 coreconfigitem(
538 b'convert',
538 b'convert',
539 b'p4.startrev',
539 b'p4.startrev',
540 default=0,
540 default=0,
541 )
541 )
542 coreconfigitem(
542 coreconfigitem(
543 b'convert',
543 b'convert',
544 b'skiptags',
544 b'skiptags',
545 default=False,
545 default=False,
546 )
546 )
547 coreconfigitem(
547 coreconfigitem(
548 b'convert',
548 b'convert',
549 b'svn.debugsvnlog',
549 b'svn.debugsvnlog',
550 default=True,
550 default=True,
551 )
551 )
552 coreconfigitem(
552 coreconfigitem(
553 b'convert',
553 b'convert',
554 b'svn.trunk',
554 b'svn.trunk',
555 default=None,
555 default=None,
556 )
556 )
557 coreconfigitem(
557 coreconfigitem(
558 b'convert',
558 b'convert',
559 b'svn.tags',
559 b'svn.tags',
560 default=None,
560 default=None,
561 )
561 )
562 coreconfigitem(
562 coreconfigitem(
563 b'convert',
563 b'convert',
564 b'svn.branches',
564 b'svn.branches',
565 default=None,
565 default=None,
566 )
566 )
567 coreconfigitem(
567 coreconfigitem(
568 b'convert',
568 b'convert',
569 b'svn.startrev',
569 b'svn.startrev',
570 default=0,
570 default=0,
571 )
571 )
572 coreconfigitem(
572 coreconfigitem(
573 b'convert',
573 b'convert',
574 b'svn.dangerous-set-commit-dates',
574 b'svn.dangerous-set-commit-dates',
575 default=False,
575 default=False,
576 )
576 )
577 coreconfigitem(
577 coreconfigitem(
578 b'debug',
578 b'debug',
579 b'dirstate.delaywrite',
579 b'dirstate.delaywrite',
580 default=0,
580 default=0,
581 )
581 )
582 coreconfigitem(
582 coreconfigitem(
583 b'debug',
583 b'debug',
584 b'revlog.verifyposition.changelog',
584 b'revlog.verifyposition.changelog',
585 default=b'',
585 default=b'',
586 )
586 )
587 coreconfigitem(
587 coreconfigitem(
588 b'defaults',
588 b'defaults',
589 b'.*',
589 b'.*',
590 default=None,
590 default=None,
591 generic=True,
591 generic=True,
592 )
592 )
593 coreconfigitem(
593 coreconfigitem(
594 b'devel',
594 b'devel',
595 b'all-warnings',
595 b'all-warnings',
596 default=False,
596 default=False,
597 )
597 )
598 coreconfigitem(
598 coreconfigitem(
599 b'devel',
599 b'devel',
600 b'bundle2.debug',
600 b'bundle2.debug',
601 default=False,
601 default=False,
602 )
602 )
603 coreconfigitem(
603 coreconfigitem(
604 b'devel',
604 b'devel',
605 b'bundle.delta',
605 b'bundle.delta',
606 default=b'',
606 default=b'',
607 )
607 )
608 coreconfigitem(
608 coreconfigitem(
609 b'devel',
609 b'devel',
610 b'cache-vfs',
610 b'cache-vfs',
611 default=None,
611 default=None,
612 )
612 )
613 coreconfigitem(
613 coreconfigitem(
614 b'devel',
614 b'devel',
615 b'check-locks',
615 b'check-locks',
616 default=False,
616 default=False,
617 )
617 )
618 coreconfigitem(
618 coreconfigitem(
619 b'devel',
619 b'devel',
620 b'check-relroot',
620 b'check-relroot',
621 default=False,
621 default=False,
622 )
622 )
623 # Track copy information for all file, not just "added" one (very slow)
623 # Track copy information for all file, not just "added" one (very slow)
624 coreconfigitem(
624 coreconfigitem(
625 b'devel',
625 b'devel',
626 b'copy-tracing.trace-all-files',
626 b'copy-tracing.trace-all-files',
627 default=False,
627 default=False,
628 )
628 )
629 coreconfigitem(
629 coreconfigitem(
630 b'devel',
630 b'devel',
631 b'default-date',
631 b'default-date',
632 default=None,
632 default=None,
633 )
633 )
634 coreconfigitem(
634 coreconfigitem(
635 b'devel',
635 b'devel',
636 b'deprec-warn',
636 b'deprec-warn',
637 default=False,
637 default=False,
638 )
638 )
639 coreconfigitem(
639 coreconfigitem(
640 b'devel',
640 b'devel',
641 b'disableloaddefaultcerts',
641 b'disableloaddefaultcerts',
642 default=False,
642 default=False,
643 )
643 )
644 coreconfigitem(
644 coreconfigitem(
645 b'devel',
645 b'devel',
646 b'warn-empty-changegroup',
646 b'warn-empty-changegroup',
647 default=False,
647 default=False,
648 )
648 )
649 coreconfigitem(
649 coreconfigitem(
650 b'devel',
650 b'devel',
651 b'legacy.exchange',
651 b'legacy.exchange',
652 default=list,
652 default=list,
653 )
653 )
654 # When True, revlogs use a special reference version of the nodemap, that is not
654 # When True, revlogs use a special reference version of the nodemap, that is not
655 # performant but is "known" to behave properly.
655 # performant but is "known" to behave properly.
656 coreconfigitem(
656 coreconfigitem(
657 b'devel',
657 b'devel',
658 b'persistent-nodemap',
658 b'persistent-nodemap',
659 default=False,
659 default=False,
660 )
660 )
661 coreconfigitem(
661 coreconfigitem(
662 b'devel',
662 b'devel',
663 b'servercafile',
663 b'servercafile',
664 default=b'',
664 default=b'',
665 )
665 )
666 coreconfigitem(
666 coreconfigitem(
667 b'devel',
667 b'devel',
668 b'serverexactprotocol',
668 b'serverexactprotocol',
669 default=b'',
669 default=b'',
670 )
670 )
671 coreconfigitem(
671 coreconfigitem(
672 b'devel',
672 b'devel',
673 b'serverrequirecert',
673 b'serverrequirecert',
674 default=False,
674 default=False,
675 )
675 )
676 coreconfigitem(
676 coreconfigitem(
677 b'devel',
677 b'devel',
678 b'strip-obsmarkers',
678 b'strip-obsmarkers',
679 default=True,
679 default=True,
680 )
680 )
681 coreconfigitem(
681 coreconfigitem(
682 b'devel',
682 b'devel',
683 b'warn-config',
683 b'warn-config',
684 default=None,
684 default=None,
685 )
685 )
686 coreconfigitem(
686 coreconfigitem(
687 b'devel',
687 b'devel',
688 b'warn-config-default',
688 b'warn-config-default',
689 default=None,
689 default=None,
690 )
690 )
691 coreconfigitem(
691 coreconfigitem(
692 b'devel',
692 b'devel',
693 b'user.obsmarker',
693 b'user.obsmarker',
694 default=None,
694 default=None,
695 )
695 )
696 coreconfigitem(
696 coreconfigitem(
697 b'devel',
697 b'devel',
698 b'warn-config-unknown',
698 b'warn-config-unknown',
699 default=None,
699 default=None,
700 )
700 )
701 coreconfigitem(
701 coreconfigitem(
702 b'devel',
702 b'devel',
703 b'debug.copies',
703 b'debug.copies',
704 default=False,
704 default=False,
705 )
705 )
706 coreconfigitem(
706 coreconfigitem(
707 b'devel',
707 b'devel',
708 b'copy-tracing.multi-thread',
708 b'copy-tracing.multi-thread',
709 default=True,
709 default=True,
710 )
710 )
711 coreconfigitem(
711 coreconfigitem(
712 b'devel',
712 b'devel',
713 b'debug.extensions',
713 b'debug.extensions',
714 default=False,
714 default=False,
715 )
715 )
716 coreconfigitem(
716 coreconfigitem(
717 b'devel',
717 b'devel',
718 b'debug.repo-filters',
718 b'debug.repo-filters',
719 default=False,
719 default=False,
720 )
720 )
721 coreconfigitem(
721 coreconfigitem(
722 b'devel',
722 b'devel',
723 b'debug.peer-request',
723 b'debug.peer-request',
724 default=False,
724 default=False,
725 )
725 )
726 # If discovery.exchange-heads is False, the discovery will not start with
726 # If discovery.exchange-heads is False, the discovery will not start with
727 # remote head fetching and local head querying.
727 # remote head fetching and local head querying.
728 coreconfigitem(
728 coreconfigitem(
729 b'devel',
729 b'devel',
730 b'discovery.exchange-heads',
730 b'discovery.exchange-heads',
731 default=True,
731 default=True,
732 )
732 )
733 # If discovery.grow-sample is False, the sample size used in set discovery will
733 # If discovery.grow-sample is False, the sample size used in set discovery will
734 # not be increased through the process
734 # not be increased through the process
735 coreconfigitem(
735 coreconfigitem(
736 b'devel',
736 b'devel',
737 b'discovery.grow-sample',
737 b'discovery.grow-sample',
738 default=True,
738 default=True,
739 )
739 )
740 # When discovery.grow-sample.dynamic is True, the default, the sample size is
740 # When discovery.grow-sample.dynamic is True, the default, the sample size is
741 # adapted to the shape of the undecided set (it is set to the max of:
741 # adapted to the shape of the undecided set (it is set to the max of:
742 # <target-size>, len(roots(undecided)), len(heads(undecided)
742 # <target-size>, len(roots(undecided)), len(heads(undecided)
743 coreconfigitem(
743 coreconfigitem(
744 b'devel',
744 b'devel',
745 b'discovery.grow-sample.dynamic',
745 b'discovery.grow-sample.dynamic',
746 default=True,
746 default=True,
747 )
747 )
748 # discovery.grow-sample.rate control the rate at which the sample grow
748 # discovery.grow-sample.rate control the rate at which the sample grow
749 coreconfigitem(
749 coreconfigitem(
750 b'devel',
750 b'devel',
751 b'discovery.grow-sample.rate',
751 b'discovery.grow-sample.rate',
752 default=1.05,
752 default=1.05,
753 )
753 )
754 # If discovery.randomize is False, random sampling during discovery are
754 # If discovery.randomize is False, random sampling during discovery are
755 # deterministic. It is meant for integration tests.
755 # deterministic. It is meant for integration tests.
756 coreconfigitem(
756 coreconfigitem(
757 b'devel',
757 b'devel',
758 b'discovery.randomize',
758 b'discovery.randomize',
759 default=True,
759 default=True,
760 )
760 )
761 # Control the initial size of the discovery sample
761 # Control the initial size of the discovery sample
762 coreconfigitem(
762 coreconfigitem(
763 b'devel',
763 b'devel',
764 b'discovery.sample-size',
764 b'discovery.sample-size',
765 default=200,
765 default=200,
766 )
766 )
767 # Control the initial size of the discovery for initial change
767 # Control the initial size of the discovery for initial change
768 coreconfigitem(
768 coreconfigitem(
769 b'devel',
769 b'devel',
770 b'discovery.sample-size.initial',
770 b'discovery.sample-size.initial',
771 default=100,
771 default=100,
772 )
772 )
773 _registerdiffopts(section=b'diff')
773 _registerdiffopts(section=b'diff')
774 coreconfigitem(
774 coreconfigitem(
775 b'diff',
775 b'diff',
776 b'merge',
776 b'merge',
777 default=False,
777 default=False,
778 experimental=True,
778 experimental=True,
779 )
779 )
780 coreconfigitem(
780 coreconfigitem(
781 b'email',
781 b'email',
782 b'bcc',
782 b'bcc',
783 default=None,
783 default=None,
784 )
784 )
785 coreconfigitem(
785 coreconfigitem(
786 b'email',
786 b'email',
787 b'cc',
787 b'cc',
788 default=None,
788 default=None,
789 )
789 )
790 coreconfigitem(
790 coreconfigitem(
791 b'email',
791 b'email',
792 b'charsets',
792 b'charsets',
793 default=list,
793 default=list,
794 )
794 )
795 coreconfigitem(
795 coreconfigitem(
796 b'email',
796 b'email',
797 b'from',
797 b'from',
798 default=None,
798 default=None,
799 )
799 )
800 coreconfigitem(
800 coreconfigitem(
801 b'email',
801 b'email',
802 b'method',
802 b'method',
803 default=b'smtp',
803 default=b'smtp',
804 )
804 )
805 coreconfigitem(
805 coreconfigitem(
806 b'email',
806 b'email',
807 b'reply-to',
807 b'reply-to',
808 default=None,
808 default=None,
809 )
809 )
810 coreconfigitem(
810 coreconfigitem(
811 b'email',
811 b'email',
812 b'to',
812 b'to',
813 default=None,
813 default=None,
814 )
814 )
815 coreconfigitem(
815 coreconfigitem(
816 b'experimental',
816 b'experimental',
817 b'archivemetatemplate',
817 b'archivemetatemplate',
818 default=dynamicdefault,
818 default=dynamicdefault,
819 )
819 )
820 coreconfigitem(
820 coreconfigitem(
821 b'experimental',
821 b'experimental',
822 b'auto-publish',
822 b'auto-publish',
823 default=b'publish',
823 default=b'publish',
824 )
824 )
825 coreconfigitem(
825 coreconfigitem(
826 b'experimental',
826 b'experimental',
827 b'bundle-phases',
827 b'bundle-phases',
828 default=False,
828 default=False,
829 )
829 )
830 coreconfigitem(
830 coreconfigitem(
831 b'experimental',
831 b'experimental',
832 b'bundle2-advertise',
832 b'bundle2-advertise',
833 default=True,
833 default=True,
834 )
834 )
835 coreconfigitem(
835 coreconfigitem(
836 b'experimental',
836 b'experimental',
837 b'bundle2-output-capture',
837 b'bundle2-output-capture',
838 default=False,
838 default=False,
839 )
839 )
840 coreconfigitem(
840 coreconfigitem(
841 b'experimental',
841 b'experimental',
842 b'bundle2.pushback',
842 b'bundle2.pushback',
843 default=False,
843 default=False,
844 )
844 )
845 coreconfigitem(
845 coreconfigitem(
846 b'experimental',
846 b'experimental',
847 b'bundle2lazylocking',
847 b'bundle2lazylocking',
848 default=False,
848 default=False,
849 )
849 )
850 coreconfigitem(
850 coreconfigitem(
851 b'experimental',
851 b'experimental',
852 b'bundlecomplevel',
852 b'bundlecomplevel',
853 default=None,
853 default=None,
854 )
854 )
855 coreconfigitem(
855 coreconfigitem(
856 b'experimental',
856 b'experimental',
857 b'bundlecomplevel.bzip2',
857 b'bundlecomplevel.bzip2',
858 default=None,
858 default=None,
859 )
859 )
860 coreconfigitem(
860 coreconfigitem(
861 b'experimental',
861 b'experimental',
862 b'bundlecomplevel.gzip',
862 b'bundlecomplevel.gzip',
863 default=None,
863 default=None,
864 )
864 )
865 coreconfigitem(
865 coreconfigitem(
866 b'experimental',
866 b'experimental',
867 b'bundlecomplevel.none',
867 b'bundlecomplevel.none',
868 default=None,
868 default=None,
869 )
869 )
870 coreconfigitem(
870 coreconfigitem(
871 b'experimental',
871 b'experimental',
872 b'bundlecomplevel.zstd',
872 b'bundlecomplevel.zstd',
873 default=None,
873 default=None,
874 )
874 )
875 coreconfigitem(
875 coreconfigitem(
876 b'experimental',
876 b'experimental',
877 b'bundlecompthreads',
877 b'bundlecompthreads',
878 default=None,
878 default=None,
879 )
879 )
880 coreconfigitem(
880 coreconfigitem(
881 b'experimental',
881 b'experimental',
882 b'bundlecompthreads.bzip2',
882 b'bundlecompthreads.bzip2',
883 default=None,
883 default=None,
884 )
884 )
885 coreconfigitem(
885 coreconfigitem(
886 b'experimental',
886 b'experimental',
887 b'bundlecompthreads.gzip',
887 b'bundlecompthreads.gzip',
888 default=None,
888 default=None,
889 )
889 )
890 coreconfigitem(
890 coreconfigitem(
891 b'experimental',
891 b'experimental',
892 b'bundlecompthreads.none',
892 b'bundlecompthreads.none',
893 default=None,
893 default=None,
894 )
894 )
895 coreconfigitem(
895 coreconfigitem(
896 b'experimental',
896 b'experimental',
897 b'bundlecompthreads.zstd',
897 b'bundlecompthreads.zstd',
898 default=None,
898 default=None,
899 )
899 )
900 coreconfigitem(
900 coreconfigitem(
901 b'experimental',
901 b'experimental',
902 b'changegroup3',
902 b'changegroup3',
903 default=False,
903 default=False,
904 )
904 )
905 coreconfigitem(
905 coreconfigitem(
906 b'experimental',
906 b'experimental',
907 b'changegroup4',
907 b'changegroup4',
908 default=False,
908 default=False,
909 )
909 )
910 coreconfigitem(
910 coreconfigitem(
911 b'experimental',
911 b'experimental',
912 b'cleanup-as-archived',
912 b'cleanup-as-archived',
913 default=False,
913 default=False,
914 )
914 )
915 coreconfigitem(
915 coreconfigitem(
916 b'experimental',
916 b'experimental',
917 b'clientcompressionengines',
917 b'clientcompressionengines',
918 default=list,
918 default=list,
919 )
919 )
920 coreconfigitem(
920 coreconfigitem(
921 b'experimental',
921 b'experimental',
922 b'copytrace',
922 b'copytrace',
923 default=b'on',
923 default=b'on',
924 )
924 )
925 coreconfigitem(
925 coreconfigitem(
926 b'experimental',
926 b'experimental',
927 b'copytrace.movecandidateslimit',
927 b'copytrace.movecandidateslimit',
928 default=100,
928 default=100,
929 )
929 )
930 coreconfigitem(
930 coreconfigitem(
931 b'experimental',
931 b'experimental',
932 b'copytrace.sourcecommitlimit',
932 b'copytrace.sourcecommitlimit',
933 default=100,
933 default=100,
934 )
934 )
935 coreconfigitem(
935 coreconfigitem(
936 b'experimental',
936 b'experimental',
937 b'copies.read-from',
937 b'copies.read-from',
938 default=b"filelog-only",
938 default=b"filelog-only",
939 )
939 )
940 coreconfigitem(
940 coreconfigitem(
941 b'experimental',
941 b'experimental',
942 b'copies.write-to',
942 b'copies.write-to',
943 default=b'filelog-only',
943 default=b'filelog-only',
944 )
944 )
945 coreconfigitem(
945 coreconfigitem(
946 b'experimental',
946 b'experimental',
947 b'crecordtest',
947 b'crecordtest',
948 default=None,
948 default=None,
949 )
949 )
950 coreconfigitem(
950 coreconfigitem(
951 b'experimental',
951 b'experimental',
952 b'directaccess',
952 b'directaccess',
953 default=False,
953 default=False,
954 )
954 )
955 coreconfigitem(
955 coreconfigitem(
956 b'experimental',
956 b'experimental',
957 b'directaccess.revnums',
957 b'directaccess.revnums',
958 default=False,
958 default=False,
959 )
959 )
960 coreconfigitem(
960 coreconfigitem(
961 b'experimental',
961 b'experimental',
962 b'dirstate-tree.in-memory',
962 b'dirstate-tree.in-memory',
963 default=False,
963 default=False,
964 )
964 )
965 coreconfigitem(
965 coreconfigitem(
966 b'experimental',
966 b'experimental',
967 b'editortmpinhg',
967 b'editortmpinhg',
968 default=False,
968 default=False,
969 )
969 )
970 coreconfigitem(
970 coreconfigitem(
971 b'experimental',
971 b'experimental',
972 b'evolution',
972 b'evolution',
973 default=list,
973 default=list,
974 )
974 )
975 coreconfigitem(
975 coreconfigitem(
976 b'experimental',
976 b'experimental',
977 b'evolution.allowdivergence',
977 b'evolution.allowdivergence',
978 default=False,
978 default=False,
979 alias=[(b'experimental', b'allowdivergence')],
979 alias=[(b'experimental', b'allowdivergence')],
980 )
980 )
981 coreconfigitem(
981 coreconfigitem(
982 b'experimental',
982 b'experimental',
983 b'evolution.allowunstable',
983 b'evolution.allowunstable',
984 default=None,
984 default=None,
985 )
985 )
986 coreconfigitem(
986 coreconfigitem(
987 b'experimental',
987 b'experimental',
988 b'evolution.createmarkers',
988 b'evolution.createmarkers',
989 default=None,
989 default=None,
990 )
990 )
991 coreconfigitem(
991 coreconfigitem(
992 b'experimental',
992 b'experimental',
993 b'evolution.effect-flags',
993 b'evolution.effect-flags',
994 default=True,
994 default=True,
995 alias=[(b'experimental', b'effect-flags')],
995 alias=[(b'experimental', b'effect-flags')],
996 )
996 )
997 coreconfigitem(
997 coreconfigitem(
998 b'experimental',
998 b'experimental',
999 b'evolution.exchange',
999 b'evolution.exchange',
1000 default=None,
1000 default=None,
1001 )
1001 )
1002 coreconfigitem(
1002 coreconfigitem(
1003 b'experimental',
1003 b'experimental',
1004 b'evolution.bundle-obsmarker',
1004 b'evolution.bundle-obsmarker',
1005 default=False,
1005 default=False,
1006 )
1006 )
1007 coreconfigitem(
1007 coreconfigitem(
1008 b'experimental',
1008 b'experimental',
1009 b'evolution.bundle-obsmarker:mandatory',
1009 b'evolution.bundle-obsmarker:mandatory',
1010 default=True,
1010 default=True,
1011 )
1011 )
1012 coreconfigitem(
1012 coreconfigitem(
1013 b'experimental',
1013 b'experimental',
1014 b'log.topo',
1014 b'log.topo',
1015 default=False,
1015 default=False,
1016 )
1016 )
1017 coreconfigitem(
1017 coreconfigitem(
1018 b'experimental',
1018 b'experimental',
1019 b'evolution.report-instabilities',
1019 b'evolution.report-instabilities',
1020 default=True,
1020 default=True,
1021 )
1021 )
1022 coreconfigitem(
1022 coreconfigitem(
1023 b'experimental',
1023 b'experimental',
1024 b'evolution.track-operation',
1024 b'evolution.track-operation',
1025 default=True,
1025 default=True,
1026 )
1026 )
1027 # repo-level config to exclude a revset visibility
1027 # repo-level config to exclude a revset visibility
1028 #
1028 #
1029 # The target use case is to use `share` to expose different subset of the same
1029 # The target use case is to use `share` to expose different subset of the same
1030 # repository, especially server side. See also `server.view`.
1030 # repository, especially server side. See also `server.view`.
1031 coreconfigitem(
1031 coreconfigitem(
1032 b'experimental',
1032 b'experimental',
1033 b'extra-filter-revs',
1033 b'extra-filter-revs',
1034 default=None,
1034 default=None,
1035 )
1035 )
1036 coreconfigitem(
1036 coreconfigitem(
1037 b'experimental',
1037 b'experimental',
1038 b'maxdeltachainspan',
1038 b'maxdeltachainspan',
1039 default=-1,
1039 default=-1,
1040 )
1040 )
1041 # tracks files which were undeleted (merge might delete them but we explicitly
1041 # tracks files which were undeleted (merge might delete them but we explicitly
1042 # kept/undeleted them) and creates new filenodes for them
1042 # kept/undeleted them) and creates new filenodes for them
1043 coreconfigitem(
1043 coreconfigitem(
1044 b'experimental',
1044 b'experimental',
1045 b'merge-track-salvaged',
1045 b'merge-track-salvaged',
1046 default=False,
1046 default=False,
1047 )
1047 )
1048 coreconfigitem(
1048 coreconfigitem(
1049 b'experimental',
1049 b'experimental',
1050 b'mergetempdirprefix',
1050 b'mergetempdirprefix',
1051 default=None,
1051 default=None,
1052 )
1052 )
1053 coreconfigitem(
1053 coreconfigitem(
1054 b'experimental',
1054 b'experimental',
1055 b'mmapindexthreshold',
1055 b'mmapindexthreshold',
1056 default=None,
1056 default=None,
1057 )
1057 )
1058 coreconfigitem(
1058 coreconfigitem(
1059 b'experimental',
1059 b'experimental',
1060 b'narrow',
1060 b'narrow',
1061 default=False,
1061 default=False,
1062 )
1062 )
1063 coreconfigitem(
1063 coreconfigitem(
1064 b'experimental',
1064 b'experimental',
1065 b'nonnormalparanoidcheck',
1065 b'nonnormalparanoidcheck',
1066 default=False,
1066 default=False,
1067 )
1067 )
1068 coreconfigitem(
1068 coreconfigitem(
1069 b'experimental',
1069 b'experimental',
1070 b'exportableenviron',
1070 b'exportableenviron',
1071 default=list,
1071 default=list,
1072 )
1072 )
1073 coreconfigitem(
1073 coreconfigitem(
1074 b'experimental',
1074 b'experimental',
1075 b'extendedheader.index',
1075 b'extendedheader.index',
1076 default=None,
1076 default=None,
1077 )
1077 )
1078 coreconfigitem(
1078 coreconfigitem(
1079 b'experimental',
1079 b'experimental',
1080 b'extendedheader.similarity',
1080 b'extendedheader.similarity',
1081 default=False,
1081 default=False,
1082 )
1082 )
1083 coreconfigitem(
1083 coreconfigitem(
1084 b'experimental',
1084 b'experimental',
1085 b'graphshorten',
1085 b'graphshorten',
1086 default=False,
1086 default=False,
1087 )
1087 )
1088 coreconfigitem(
1088 coreconfigitem(
1089 b'experimental',
1089 b'experimental',
1090 b'graphstyle.parent',
1090 b'graphstyle.parent',
1091 default=dynamicdefault,
1091 default=dynamicdefault,
1092 )
1092 )
1093 coreconfigitem(
1093 coreconfigitem(
1094 b'experimental',
1094 b'experimental',
1095 b'graphstyle.missing',
1095 b'graphstyle.missing',
1096 default=dynamicdefault,
1096 default=dynamicdefault,
1097 )
1097 )
1098 coreconfigitem(
1098 coreconfigitem(
1099 b'experimental',
1099 b'experimental',
1100 b'graphstyle.grandparent',
1100 b'graphstyle.grandparent',
1101 default=dynamicdefault,
1101 default=dynamicdefault,
1102 )
1102 )
1103 coreconfigitem(
1103 coreconfigitem(
1104 b'experimental',
1104 b'experimental',
1105 b'hook-track-tags',
1105 b'hook-track-tags',
1106 default=False,
1106 default=False,
1107 )
1107 )
1108 coreconfigitem(
1108 coreconfigitem(
1109 b'experimental',
1109 b'experimental',
1110 b'httppeer.advertise-v2',
1110 b'httppeer.advertise-v2',
1111 default=False,
1111 default=False,
1112 )
1112 )
1113 coreconfigitem(
1113 coreconfigitem(
1114 b'experimental',
1114 b'experimental',
1115 b'httppeer.v2-encoder-order',
1115 b'httppeer.v2-encoder-order',
1116 default=None,
1116 default=None,
1117 )
1117 )
1118 coreconfigitem(
1118 coreconfigitem(
1119 b'experimental',
1119 b'experimental',
1120 b'httppostargs',
1120 b'httppostargs',
1121 default=False,
1121 default=False,
1122 )
1122 )
1123 coreconfigitem(b'experimental', b'nointerrupt', default=False)
1123 coreconfigitem(b'experimental', b'nointerrupt', default=False)
1124 coreconfigitem(b'experimental', b'nointerrupt-interactiveonly', default=True)
1124 coreconfigitem(b'experimental', b'nointerrupt-interactiveonly', default=True)
1125
1125
1126 coreconfigitem(
1126 coreconfigitem(
1127 b'experimental',
1127 b'experimental',
1128 b'obsmarkers-exchange-debug',
1128 b'obsmarkers-exchange-debug',
1129 default=False,
1129 default=False,
1130 )
1130 )
1131 coreconfigitem(
1131 coreconfigitem(
1132 b'experimental',
1132 b'experimental',
1133 b'remotenames',
1133 b'remotenames',
1134 default=False,
1134 default=False,
1135 )
1135 )
1136 coreconfigitem(
1136 coreconfigitem(
1137 b'experimental',
1137 b'experimental',
1138 b'removeemptydirs',
1138 b'removeemptydirs',
1139 default=True,
1139 default=True,
1140 )
1140 )
1141 coreconfigitem(
1141 coreconfigitem(
1142 b'experimental',
1142 b'experimental',
1143 b'revert.interactive.select-to-keep',
1143 b'revert.interactive.select-to-keep',
1144 default=False,
1144 default=False,
1145 )
1145 )
1146 coreconfigitem(
1146 coreconfigitem(
1147 b'experimental',
1147 b'experimental',
1148 b'revisions.prefixhexnode',
1148 b'revisions.prefixhexnode',
1149 default=False,
1149 default=False,
1150 )
1150 )
1151 # "out of experimental" todo list.
1151 # "out of experimental" todo list.
1152 #
1152 #
1153 # * to grow a docket file to at least store the last offset of the data
1153 # * to grow a docket file to at least store the last offset of the data
1154 # file when rewriting sidedata.
1154 # file when rewriting sidedata.
1155 # * need a way of dealing with garbage data if we allow rewriting
1155 # * need a way of dealing with garbage data if we allow rewriting
1156 # *existing* sidedata.
1156 # *existing* sidedata.
1157 # * Exchange-wise, we will also need to do something more efficient than
1157 # * Exchange-wise, we will also need to do something more efficient than
1158 # keeping references to the affected revlogs, especially memory-wise when
1158 # keeping references to the affected revlogs, especially memory-wise when
1159 # rewriting sidedata.
1159 # rewriting sidedata.
1160 # * Also... compress the sidedata? (this should be coming very soon)
1160 # * Also... compress the sidedata? (this should be coming very soon)
1161 coreconfigitem(
1161 coreconfigitem(
1162 b'experimental',
1162 b'experimental',
1163 b'revlogv2',
1163 b'revlogv2',
1164 default=None,
1164 default=None,
1165 )
1165 )
1166 coreconfigitem(
1166 coreconfigitem(
1167 b'experimental',
1167 b'experimental',
1168 b'revisions.disambiguatewithin',
1168 b'revisions.disambiguatewithin',
1169 default=None,
1169 default=None,
1170 )
1170 )
1171 coreconfigitem(
1171 coreconfigitem(
1172 b'experimental',
1172 b'experimental',
1173 b'rust.index',
1173 b'rust.index',
1174 default=False,
1174 default=False,
1175 )
1175 )
1176 coreconfigitem(
1176 coreconfigitem(
1177 b'experimental',
1177 b'experimental',
1178 b'server.filesdata.recommended-batch-size',
1178 b'server.filesdata.recommended-batch-size',
1179 default=50000,
1179 default=50000,
1180 )
1180 )
1181 coreconfigitem(
1181 coreconfigitem(
1182 b'experimental',
1182 b'experimental',
1183 b'server.manifestdata.recommended-batch-size',
1183 b'server.manifestdata.recommended-batch-size',
1184 default=100000,
1184 default=100000,
1185 )
1185 )
1186 coreconfigitem(
1186 coreconfigitem(
1187 b'experimental',
1187 b'experimental',
1188 b'server.stream-narrow-clones',
1188 b'server.stream-narrow-clones',
1189 default=False,
1189 default=False,
1190 )
1190 )
1191 coreconfigitem(
1191 coreconfigitem(
1192 b'experimental',
1192 b'experimental',
1193 b'single-head-per-branch',
1193 b'single-head-per-branch',
1194 default=False,
1194 default=False,
1195 )
1195 )
1196 coreconfigitem(
1196 coreconfigitem(
1197 b'experimental',
1197 b'experimental',
1198 b'single-head-per-branch:account-closed-heads',
1198 b'single-head-per-branch:account-closed-heads',
1199 default=False,
1199 default=False,
1200 )
1200 )
1201 coreconfigitem(
1201 coreconfigitem(
1202 b'experimental',
1202 b'experimental',
1203 b'single-head-per-branch:public-changes-only',
1203 b'single-head-per-branch:public-changes-only',
1204 default=False,
1204 default=False,
1205 )
1205 )
1206 coreconfigitem(
1206 coreconfigitem(
1207 b'experimental',
1207 b'experimental',
1208 b'sshserver.support-v2',
1208 b'sshserver.support-v2',
1209 default=False,
1209 default=False,
1210 )
1210 )
1211 coreconfigitem(
1211 coreconfigitem(
1212 b'experimental',
1212 b'experimental',
1213 b'sparse-read',
1213 b'sparse-read',
1214 default=False,
1214 default=False,
1215 )
1215 )
1216 coreconfigitem(
1216 coreconfigitem(
1217 b'experimental',
1217 b'experimental',
1218 b'sparse-read.density-threshold',
1218 b'sparse-read.density-threshold',
1219 default=0.50,
1219 default=0.50,
1220 )
1220 )
1221 coreconfigitem(
1221 coreconfigitem(
1222 b'experimental',
1222 b'experimental',
1223 b'sparse-read.min-gap-size',
1223 b'sparse-read.min-gap-size',
1224 default=b'65K',
1224 default=b'65K',
1225 )
1225 )
1226 coreconfigitem(
1226 coreconfigitem(
1227 b'experimental',
1227 b'experimental',
1228 b'treemanifest',
1228 b'treemanifest',
1229 default=False,
1229 default=False,
1230 )
1230 )
1231 coreconfigitem(
1231 coreconfigitem(
1232 b'experimental',
1232 b'experimental',
1233 b'update.atomic-file',
1233 b'update.atomic-file',
1234 default=False,
1234 default=False,
1235 )
1235 )
1236 coreconfigitem(
1236 coreconfigitem(
1237 b'experimental',
1237 b'experimental',
1238 b'sshpeer.advertise-v2',
1238 b'sshpeer.advertise-v2',
1239 default=False,
1239 default=False,
1240 )
1240 )
1241 coreconfigitem(
1241 coreconfigitem(
1242 b'experimental',
1242 b'experimental',
1243 b'web.apiserver',
1243 b'web.apiserver',
1244 default=False,
1244 default=False,
1245 )
1245 )
1246 coreconfigitem(
1246 coreconfigitem(
1247 b'experimental',
1247 b'experimental',
1248 b'web.api.http-v2',
1248 b'web.api.http-v2',
1249 default=False,
1249 default=False,
1250 )
1250 )
1251 coreconfigitem(
1251 coreconfigitem(
1252 b'experimental',
1252 b'experimental',
1253 b'web.api.debugreflect',
1253 b'web.api.debugreflect',
1254 default=False,
1254 default=False,
1255 )
1255 )
1256 coreconfigitem(
1256 coreconfigitem(
1257 b'experimental',
1257 b'experimental',
1258 b'worker.wdir-get-thread-safe',
1258 b'worker.wdir-get-thread-safe',
1259 default=False,
1259 default=False,
1260 )
1260 )
1261 coreconfigitem(
1261 coreconfigitem(
1262 b'experimental',
1262 b'experimental',
1263 b'worker.repository-upgrade',
1263 b'worker.repository-upgrade',
1264 default=False,
1264 default=False,
1265 )
1265 )
1266 coreconfigitem(
1266 coreconfigitem(
1267 b'experimental',
1267 b'experimental',
1268 b'xdiff',
1268 b'xdiff',
1269 default=False,
1269 default=False,
1270 )
1270 )
1271 coreconfigitem(
1271 coreconfigitem(
1272 b'extensions',
1272 b'extensions',
1273 b'.*',
1273 b'.*',
1274 default=None,
1274 default=None,
1275 generic=True,
1275 generic=True,
1276 )
1276 )
1277 coreconfigitem(
1277 coreconfigitem(
1278 b'extdata',
1278 b'extdata',
1279 b'.*',
1279 b'.*',
1280 default=None,
1280 default=None,
1281 generic=True,
1281 generic=True,
1282 )
1282 )
1283 coreconfigitem(
1283 coreconfigitem(
1284 b'format',
1284 b'format',
1285 b'bookmarks-in-store',
1285 b'bookmarks-in-store',
1286 default=False,
1286 default=False,
1287 )
1287 )
1288 coreconfigitem(
1288 coreconfigitem(
1289 b'format',
1289 b'format',
1290 b'chunkcachesize',
1290 b'chunkcachesize',
1291 default=None,
1291 default=None,
1292 experimental=True,
1292 experimental=True,
1293 )
1293 )
1294 coreconfigitem(
1294 coreconfigitem(
1295 b'format',
1295 b'format',
1296 b'dotencode',
1296 b'dotencode',
1297 default=True,
1297 default=True,
1298 )
1298 )
1299 coreconfigitem(
1299 coreconfigitem(
1300 b'format',
1300 b'format',
1301 b'generaldelta',
1301 b'generaldelta',
1302 default=False,
1302 default=False,
1303 experimental=True,
1303 experimental=True,
1304 )
1304 )
1305 coreconfigitem(
1305 coreconfigitem(
1306 b'format',
1306 b'format',
1307 b'manifestcachesize',
1307 b'manifestcachesize',
1308 default=None,
1308 default=None,
1309 experimental=True,
1309 experimental=True,
1310 )
1310 )
1311 coreconfigitem(
1311 coreconfigitem(
1312 b'format',
1312 b'format',
1313 b'maxchainlen',
1313 b'maxchainlen',
1314 default=dynamicdefault,
1314 default=dynamicdefault,
1315 experimental=True,
1315 experimental=True,
1316 )
1316 )
1317 coreconfigitem(
1317 coreconfigitem(
1318 b'format',
1318 b'format',
1319 b'obsstore-version',
1319 b'obsstore-version',
1320 default=None,
1320 default=None,
1321 )
1321 )
1322 coreconfigitem(
1322 coreconfigitem(
1323 b'format',
1323 b'format',
1324 b'sparse-revlog',
1324 b'sparse-revlog',
1325 default=True,
1325 default=True,
1326 )
1326 )
1327 coreconfigitem(
1327 coreconfigitem(
1328 b'format',
1328 b'format',
1329 b'revlog-compression',
1329 b'revlog-compression',
1330 default=lambda: [b'zstd', b'zlib'],
1330 default=lambda: [b'zstd', b'zlib'],
1331 alias=[(b'experimental', b'format.compression')],
1331 alias=[(b'experimental', b'format.compression')],
1332 )
1332 )
1333 coreconfigitem(
1333 coreconfigitem(
1334 b'format',
1334 b'format',
1335 b'usefncache',
1335 b'usefncache',
1336 default=True,
1336 default=True,
1337 )
1337 )
1338 coreconfigitem(
1338 coreconfigitem(
1339 b'format',
1339 b'format',
1340 b'usegeneraldelta',
1340 b'usegeneraldelta',
1341 default=True,
1341 default=True,
1342 )
1342 )
1343 coreconfigitem(
1343 coreconfigitem(
1344 b'format',
1344 b'format',
1345 b'usestore',
1345 b'usestore',
1346 default=True,
1346 default=True,
1347 )
1347 )
1348
1348
1349
1349
1350 def _persistent_nodemap_default():
1350 def _persistent_nodemap_default():
1351 """compute `use-persistent-nodemap` default value
1351 """compute `use-persistent-nodemap` default value
1352
1352
1353 The feature is disabled unless a fast implementation is available.
1353 The feature is disabled unless a fast implementation is available.
1354 """
1354 """
1355 from . import policy
1355 from . import policy
1356
1356
1357 return policy.importrust('revlog') is not None
1357 return policy.importrust('revlog') is not None
1358
1358
1359
1359
1360 coreconfigitem(
1360 coreconfigitem(
1361 b'format',
1361 b'format',
1362 b'use-persistent-nodemap',
1362 b'use-persistent-nodemap',
1363 default=_persistent_nodemap_default,
1363 default=_persistent_nodemap_default,
1364 )
1364 )
1365 coreconfigitem(
1365 coreconfigitem(
1366 b'format',
1366 b'format',
1367 b'exp-use-copies-side-data-changeset',
1367 b'exp-use-copies-side-data-changeset',
1368 default=False,
1368 default=False,
1369 experimental=True,
1369 experimental=True,
1370 )
1370 )
1371 coreconfigitem(
1371 coreconfigitem(
1372 b'format',
1372 b'format',
1373 b'exp-use-side-data',
1374 default=False,
1375 experimental=True,
1376 )
1377 coreconfigitem(
1378 b'format',
1379 b'use-share-safe',
1373 b'use-share-safe',
1380 default=False,
1374 default=False,
1381 )
1375 )
1382 coreconfigitem(
1376 coreconfigitem(
1383 b'format',
1377 b'format',
1384 b'internal-phase',
1378 b'internal-phase',
1385 default=False,
1379 default=False,
1386 experimental=True,
1380 experimental=True,
1387 )
1381 )
1388 coreconfigitem(
1382 coreconfigitem(
1389 b'fsmonitor',
1383 b'fsmonitor',
1390 b'warn_when_unused',
1384 b'warn_when_unused',
1391 default=True,
1385 default=True,
1392 )
1386 )
1393 coreconfigitem(
1387 coreconfigitem(
1394 b'fsmonitor',
1388 b'fsmonitor',
1395 b'warn_update_file_count',
1389 b'warn_update_file_count',
1396 default=50000,
1390 default=50000,
1397 )
1391 )
1398 coreconfigitem(
1392 coreconfigitem(
1399 b'fsmonitor',
1393 b'fsmonitor',
1400 b'warn_update_file_count_rust',
1394 b'warn_update_file_count_rust',
1401 default=400000,
1395 default=400000,
1402 )
1396 )
1403 coreconfigitem(
1397 coreconfigitem(
1404 b'help',
1398 b'help',
1405 br'hidden-command\..*',
1399 br'hidden-command\..*',
1406 default=False,
1400 default=False,
1407 generic=True,
1401 generic=True,
1408 )
1402 )
1409 coreconfigitem(
1403 coreconfigitem(
1410 b'help',
1404 b'help',
1411 br'hidden-topic\..*',
1405 br'hidden-topic\..*',
1412 default=False,
1406 default=False,
1413 generic=True,
1407 generic=True,
1414 )
1408 )
1415 coreconfigitem(
1409 coreconfigitem(
1416 b'hooks',
1410 b'hooks',
1417 b'[^:]*',
1411 b'[^:]*',
1418 default=dynamicdefault,
1412 default=dynamicdefault,
1419 generic=True,
1413 generic=True,
1420 )
1414 )
1421 coreconfigitem(
1415 coreconfigitem(
1422 b'hooks',
1416 b'hooks',
1423 b'.*:run-with-plain',
1417 b'.*:run-with-plain',
1424 default=True,
1418 default=True,
1425 generic=True,
1419 generic=True,
1426 )
1420 )
1427 coreconfigitem(
1421 coreconfigitem(
1428 b'hgweb-paths',
1422 b'hgweb-paths',
1429 b'.*',
1423 b'.*',
1430 default=list,
1424 default=list,
1431 generic=True,
1425 generic=True,
1432 )
1426 )
1433 coreconfigitem(
1427 coreconfigitem(
1434 b'hostfingerprints',
1428 b'hostfingerprints',
1435 b'.*',
1429 b'.*',
1436 default=list,
1430 default=list,
1437 generic=True,
1431 generic=True,
1438 )
1432 )
1439 coreconfigitem(
1433 coreconfigitem(
1440 b'hostsecurity',
1434 b'hostsecurity',
1441 b'ciphers',
1435 b'ciphers',
1442 default=None,
1436 default=None,
1443 )
1437 )
1444 coreconfigitem(
1438 coreconfigitem(
1445 b'hostsecurity',
1439 b'hostsecurity',
1446 b'minimumprotocol',
1440 b'minimumprotocol',
1447 default=dynamicdefault,
1441 default=dynamicdefault,
1448 )
1442 )
1449 coreconfigitem(
1443 coreconfigitem(
1450 b'hostsecurity',
1444 b'hostsecurity',
1451 b'.*:minimumprotocol$',
1445 b'.*:minimumprotocol$',
1452 default=dynamicdefault,
1446 default=dynamicdefault,
1453 generic=True,
1447 generic=True,
1454 )
1448 )
1455 coreconfigitem(
1449 coreconfigitem(
1456 b'hostsecurity',
1450 b'hostsecurity',
1457 b'.*:ciphers$',
1451 b'.*:ciphers$',
1458 default=dynamicdefault,
1452 default=dynamicdefault,
1459 generic=True,
1453 generic=True,
1460 )
1454 )
1461 coreconfigitem(
1455 coreconfigitem(
1462 b'hostsecurity',
1456 b'hostsecurity',
1463 b'.*:fingerprints$',
1457 b'.*:fingerprints$',
1464 default=list,
1458 default=list,
1465 generic=True,
1459 generic=True,
1466 )
1460 )
1467 coreconfigitem(
1461 coreconfigitem(
1468 b'hostsecurity',
1462 b'hostsecurity',
1469 b'.*:verifycertsfile$',
1463 b'.*:verifycertsfile$',
1470 default=None,
1464 default=None,
1471 generic=True,
1465 generic=True,
1472 )
1466 )
1473
1467
1474 coreconfigitem(
1468 coreconfigitem(
1475 b'http_proxy',
1469 b'http_proxy',
1476 b'always',
1470 b'always',
1477 default=False,
1471 default=False,
1478 )
1472 )
1479 coreconfigitem(
1473 coreconfigitem(
1480 b'http_proxy',
1474 b'http_proxy',
1481 b'host',
1475 b'host',
1482 default=None,
1476 default=None,
1483 )
1477 )
1484 coreconfigitem(
1478 coreconfigitem(
1485 b'http_proxy',
1479 b'http_proxy',
1486 b'no',
1480 b'no',
1487 default=list,
1481 default=list,
1488 )
1482 )
1489 coreconfigitem(
1483 coreconfigitem(
1490 b'http_proxy',
1484 b'http_proxy',
1491 b'passwd',
1485 b'passwd',
1492 default=None,
1486 default=None,
1493 )
1487 )
1494 coreconfigitem(
1488 coreconfigitem(
1495 b'http_proxy',
1489 b'http_proxy',
1496 b'user',
1490 b'user',
1497 default=None,
1491 default=None,
1498 )
1492 )
1499
1493
1500 coreconfigitem(
1494 coreconfigitem(
1501 b'http',
1495 b'http',
1502 b'timeout',
1496 b'timeout',
1503 default=None,
1497 default=None,
1504 )
1498 )
1505
1499
1506 coreconfigitem(
1500 coreconfigitem(
1507 b'logtoprocess',
1501 b'logtoprocess',
1508 b'commandexception',
1502 b'commandexception',
1509 default=None,
1503 default=None,
1510 )
1504 )
1511 coreconfigitem(
1505 coreconfigitem(
1512 b'logtoprocess',
1506 b'logtoprocess',
1513 b'commandfinish',
1507 b'commandfinish',
1514 default=None,
1508 default=None,
1515 )
1509 )
1516 coreconfigitem(
1510 coreconfigitem(
1517 b'logtoprocess',
1511 b'logtoprocess',
1518 b'command',
1512 b'command',
1519 default=None,
1513 default=None,
1520 )
1514 )
1521 coreconfigitem(
1515 coreconfigitem(
1522 b'logtoprocess',
1516 b'logtoprocess',
1523 b'develwarn',
1517 b'develwarn',
1524 default=None,
1518 default=None,
1525 )
1519 )
1526 coreconfigitem(
1520 coreconfigitem(
1527 b'logtoprocess',
1521 b'logtoprocess',
1528 b'uiblocked',
1522 b'uiblocked',
1529 default=None,
1523 default=None,
1530 )
1524 )
1531 coreconfigitem(
1525 coreconfigitem(
1532 b'merge',
1526 b'merge',
1533 b'checkunknown',
1527 b'checkunknown',
1534 default=b'abort',
1528 default=b'abort',
1535 )
1529 )
1536 coreconfigitem(
1530 coreconfigitem(
1537 b'merge',
1531 b'merge',
1538 b'checkignored',
1532 b'checkignored',
1539 default=b'abort',
1533 default=b'abort',
1540 )
1534 )
1541 coreconfigitem(
1535 coreconfigitem(
1542 b'experimental',
1536 b'experimental',
1543 b'merge.checkpathconflicts',
1537 b'merge.checkpathconflicts',
1544 default=False,
1538 default=False,
1545 )
1539 )
1546 coreconfigitem(
1540 coreconfigitem(
1547 b'merge',
1541 b'merge',
1548 b'followcopies',
1542 b'followcopies',
1549 default=True,
1543 default=True,
1550 )
1544 )
1551 coreconfigitem(
1545 coreconfigitem(
1552 b'merge',
1546 b'merge',
1553 b'on-failure',
1547 b'on-failure',
1554 default=b'continue',
1548 default=b'continue',
1555 )
1549 )
1556 coreconfigitem(
1550 coreconfigitem(
1557 b'merge',
1551 b'merge',
1558 b'preferancestor',
1552 b'preferancestor',
1559 default=lambda: [b'*'],
1553 default=lambda: [b'*'],
1560 experimental=True,
1554 experimental=True,
1561 )
1555 )
1562 coreconfigitem(
1556 coreconfigitem(
1563 b'merge',
1557 b'merge',
1564 b'strict-capability-check',
1558 b'strict-capability-check',
1565 default=False,
1559 default=False,
1566 )
1560 )
1567 coreconfigitem(
1561 coreconfigitem(
1568 b'merge-tools',
1562 b'merge-tools',
1569 b'.*',
1563 b'.*',
1570 default=None,
1564 default=None,
1571 generic=True,
1565 generic=True,
1572 )
1566 )
1573 coreconfigitem(
1567 coreconfigitem(
1574 b'merge-tools',
1568 b'merge-tools',
1575 br'.*\.args$',
1569 br'.*\.args$',
1576 default=b"$local $base $other",
1570 default=b"$local $base $other",
1577 generic=True,
1571 generic=True,
1578 priority=-1,
1572 priority=-1,
1579 )
1573 )
1580 coreconfigitem(
1574 coreconfigitem(
1581 b'merge-tools',
1575 b'merge-tools',
1582 br'.*\.binary$',
1576 br'.*\.binary$',
1583 default=False,
1577 default=False,
1584 generic=True,
1578 generic=True,
1585 priority=-1,
1579 priority=-1,
1586 )
1580 )
1587 coreconfigitem(
1581 coreconfigitem(
1588 b'merge-tools',
1582 b'merge-tools',
1589 br'.*\.check$',
1583 br'.*\.check$',
1590 default=list,
1584 default=list,
1591 generic=True,
1585 generic=True,
1592 priority=-1,
1586 priority=-1,
1593 )
1587 )
1594 coreconfigitem(
1588 coreconfigitem(
1595 b'merge-tools',
1589 b'merge-tools',
1596 br'.*\.checkchanged$',
1590 br'.*\.checkchanged$',
1597 default=False,
1591 default=False,
1598 generic=True,
1592 generic=True,
1599 priority=-1,
1593 priority=-1,
1600 )
1594 )
1601 coreconfigitem(
1595 coreconfigitem(
1602 b'merge-tools',
1596 b'merge-tools',
1603 br'.*\.executable$',
1597 br'.*\.executable$',
1604 default=dynamicdefault,
1598 default=dynamicdefault,
1605 generic=True,
1599 generic=True,
1606 priority=-1,
1600 priority=-1,
1607 )
1601 )
1608 coreconfigitem(
1602 coreconfigitem(
1609 b'merge-tools',
1603 b'merge-tools',
1610 br'.*\.fixeol$',
1604 br'.*\.fixeol$',
1611 default=False,
1605 default=False,
1612 generic=True,
1606 generic=True,
1613 priority=-1,
1607 priority=-1,
1614 )
1608 )
1615 coreconfigitem(
1609 coreconfigitem(
1616 b'merge-tools',
1610 b'merge-tools',
1617 br'.*\.gui$',
1611 br'.*\.gui$',
1618 default=False,
1612 default=False,
1619 generic=True,
1613 generic=True,
1620 priority=-1,
1614 priority=-1,
1621 )
1615 )
1622 coreconfigitem(
1616 coreconfigitem(
1623 b'merge-tools',
1617 b'merge-tools',
1624 br'.*\.mergemarkers$',
1618 br'.*\.mergemarkers$',
1625 default=b'basic',
1619 default=b'basic',
1626 generic=True,
1620 generic=True,
1627 priority=-1,
1621 priority=-1,
1628 )
1622 )
1629 coreconfigitem(
1623 coreconfigitem(
1630 b'merge-tools',
1624 b'merge-tools',
1631 br'.*\.mergemarkertemplate$',
1625 br'.*\.mergemarkertemplate$',
1632 default=dynamicdefault, # take from command-templates.mergemarker
1626 default=dynamicdefault, # take from command-templates.mergemarker
1633 generic=True,
1627 generic=True,
1634 priority=-1,
1628 priority=-1,
1635 )
1629 )
1636 coreconfigitem(
1630 coreconfigitem(
1637 b'merge-tools',
1631 b'merge-tools',
1638 br'.*\.priority$',
1632 br'.*\.priority$',
1639 default=0,
1633 default=0,
1640 generic=True,
1634 generic=True,
1641 priority=-1,
1635 priority=-1,
1642 )
1636 )
1643 coreconfigitem(
1637 coreconfigitem(
1644 b'merge-tools',
1638 b'merge-tools',
1645 br'.*\.premerge$',
1639 br'.*\.premerge$',
1646 default=dynamicdefault,
1640 default=dynamicdefault,
1647 generic=True,
1641 generic=True,
1648 priority=-1,
1642 priority=-1,
1649 )
1643 )
1650 coreconfigitem(
1644 coreconfigitem(
1651 b'merge-tools',
1645 b'merge-tools',
1652 br'.*\.symlink$',
1646 br'.*\.symlink$',
1653 default=False,
1647 default=False,
1654 generic=True,
1648 generic=True,
1655 priority=-1,
1649 priority=-1,
1656 )
1650 )
1657 coreconfigitem(
1651 coreconfigitem(
1658 b'pager',
1652 b'pager',
1659 b'attend-.*',
1653 b'attend-.*',
1660 default=dynamicdefault,
1654 default=dynamicdefault,
1661 generic=True,
1655 generic=True,
1662 )
1656 )
1663 coreconfigitem(
1657 coreconfigitem(
1664 b'pager',
1658 b'pager',
1665 b'ignore',
1659 b'ignore',
1666 default=list,
1660 default=list,
1667 )
1661 )
1668 coreconfigitem(
1662 coreconfigitem(
1669 b'pager',
1663 b'pager',
1670 b'pager',
1664 b'pager',
1671 default=dynamicdefault,
1665 default=dynamicdefault,
1672 )
1666 )
1673 coreconfigitem(
1667 coreconfigitem(
1674 b'patch',
1668 b'patch',
1675 b'eol',
1669 b'eol',
1676 default=b'strict',
1670 default=b'strict',
1677 )
1671 )
1678 coreconfigitem(
1672 coreconfigitem(
1679 b'patch',
1673 b'patch',
1680 b'fuzz',
1674 b'fuzz',
1681 default=2,
1675 default=2,
1682 )
1676 )
1683 coreconfigitem(
1677 coreconfigitem(
1684 b'paths',
1678 b'paths',
1685 b'default',
1679 b'default',
1686 default=None,
1680 default=None,
1687 )
1681 )
1688 coreconfigitem(
1682 coreconfigitem(
1689 b'paths',
1683 b'paths',
1690 b'default-push',
1684 b'default-push',
1691 default=None,
1685 default=None,
1692 )
1686 )
1693 coreconfigitem(
1687 coreconfigitem(
1694 b'paths',
1688 b'paths',
1695 b'.*',
1689 b'.*',
1696 default=None,
1690 default=None,
1697 generic=True,
1691 generic=True,
1698 )
1692 )
1699 coreconfigitem(
1693 coreconfigitem(
1700 b'phases',
1694 b'phases',
1701 b'checksubrepos',
1695 b'checksubrepos',
1702 default=b'follow',
1696 default=b'follow',
1703 )
1697 )
1704 coreconfigitem(
1698 coreconfigitem(
1705 b'phases',
1699 b'phases',
1706 b'new-commit',
1700 b'new-commit',
1707 default=b'draft',
1701 default=b'draft',
1708 )
1702 )
1709 coreconfigitem(
1703 coreconfigitem(
1710 b'phases',
1704 b'phases',
1711 b'publish',
1705 b'publish',
1712 default=True,
1706 default=True,
1713 )
1707 )
1714 coreconfigitem(
1708 coreconfigitem(
1715 b'profiling',
1709 b'profiling',
1716 b'enabled',
1710 b'enabled',
1717 default=False,
1711 default=False,
1718 )
1712 )
1719 coreconfigitem(
1713 coreconfigitem(
1720 b'profiling',
1714 b'profiling',
1721 b'format',
1715 b'format',
1722 default=b'text',
1716 default=b'text',
1723 )
1717 )
1724 coreconfigitem(
1718 coreconfigitem(
1725 b'profiling',
1719 b'profiling',
1726 b'freq',
1720 b'freq',
1727 default=1000,
1721 default=1000,
1728 )
1722 )
1729 coreconfigitem(
1723 coreconfigitem(
1730 b'profiling',
1724 b'profiling',
1731 b'limit',
1725 b'limit',
1732 default=30,
1726 default=30,
1733 )
1727 )
1734 coreconfigitem(
1728 coreconfigitem(
1735 b'profiling',
1729 b'profiling',
1736 b'nested',
1730 b'nested',
1737 default=0,
1731 default=0,
1738 )
1732 )
1739 coreconfigitem(
1733 coreconfigitem(
1740 b'profiling',
1734 b'profiling',
1741 b'output',
1735 b'output',
1742 default=None,
1736 default=None,
1743 )
1737 )
1744 coreconfigitem(
1738 coreconfigitem(
1745 b'profiling',
1739 b'profiling',
1746 b'showmax',
1740 b'showmax',
1747 default=0.999,
1741 default=0.999,
1748 )
1742 )
1749 coreconfigitem(
1743 coreconfigitem(
1750 b'profiling',
1744 b'profiling',
1751 b'showmin',
1745 b'showmin',
1752 default=dynamicdefault,
1746 default=dynamicdefault,
1753 )
1747 )
1754 coreconfigitem(
1748 coreconfigitem(
1755 b'profiling',
1749 b'profiling',
1756 b'showtime',
1750 b'showtime',
1757 default=True,
1751 default=True,
1758 )
1752 )
1759 coreconfigitem(
1753 coreconfigitem(
1760 b'profiling',
1754 b'profiling',
1761 b'sort',
1755 b'sort',
1762 default=b'inlinetime',
1756 default=b'inlinetime',
1763 )
1757 )
1764 coreconfigitem(
1758 coreconfigitem(
1765 b'profiling',
1759 b'profiling',
1766 b'statformat',
1760 b'statformat',
1767 default=b'hotpath',
1761 default=b'hotpath',
1768 )
1762 )
1769 coreconfigitem(
1763 coreconfigitem(
1770 b'profiling',
1764 b'profiling',
1771 b'time-track',
1765 b'time-track',
1772 default=dynamicdefault,
1766 default=dynamicdefault,
1773 )
1767 )
1774 coreconfigitem(
1768 coreconfigitem(
1775 b'profiling',
1769 b'profiling',
1776 b'type',
1770 b'type',
1777 default=b'stat',
1771 default=b'stat',
1778 )
1772 )
1779 coreconfigitem(
1773 coreconfigitem(
1780 b'progress',
1774 b'progress',
1781 b'assume-tty',
1775 b'assume-tty',
1782 default=False,
1776 default=False,
1783 )
1777 )
1784 coreconfigitem(
1778 coreconfigitem(
1785 b'progress',
1779 b'progress',
1786 b'changedelay',
1780 b'changedelay',
1787 default=1,
1781 default=1,
1788 )
1782 )
1789 coreconfigitem(
1783 coreconfigitem(
1790 b'progress',
1784 b'progress',
1791 b'clear-complete',
1785 b'clear-complete',
1792 default=True,
1786 default=True,
1793 )
1787 )
1794 coreconfigitem(
1788 coreconfigitem(
1795 b'progress',
1789 b'progress',
1796 b'debug',
1790 b'debug',
1797 default=False,
1791 default=False,
1798 )
1792 )
1799 coreconfigitem(
1793 coreconfigitem(
1800 b'progress',
1794 b'progress',
1801 b'delay',
1795 b'delay',
1802 default=3,
1796 default=3,
1803 )
1797 )
1804 coreconfigitem(
1798 coreconfigitem(
1805 b'progress',
1799 b'progress',
1806 b'disable',
1800 b'disable',
1807 default=False,
1801 default=False,
1808 )
1802 )
1809 coreconfigitem(
1803 coreconfigitem(
1810 b'progress',
1804 b'progress',
1811 b'estimateinterval',
1805 b'estimateinterval',
1812 default=60.0,
1806 default=60.0,
1813 )
1807 )
1814 coreconfigitem(
1808 coreconfigitem(
1815 b'progress',
1809 b'progress',
1816 b'format',
1810 b'format',
1817 default=lambda: [b'topic', b'bar', b'number', b'estimate'],
1811 default=lambda: [b'topic', b'bar', b'number', b'estimate'],
1818 )
1812 )
1819 coreconfigitem(
1813 coreconfigitem(
1820 b'progress',
1814 b'progress',
1821 b'refresh',
1815 b'refresh',
1822 default=0.1,
1816 default=0.1,
1823 )
1817 )
1824 coreconfigitem(
1818 coreconfigitem(
1825 b'progress',
1819 b'progress',
1826 b'width',
1820 b'width',
1827 default=dynamicdefault,
1821 default=dynamicdefault,
1828 )
1822 )
1829 coreconfigitem(
1823 coreconfigitem(
1830 b'pull',
1824 b'pull',
1831 b'confirm',
1825 b'confirm',
1832 default=False,
1826 default=False,
1833 )
1827 )
1834 coreconfigitem(
1828 coreconfigitem(
1835 b'push',
1829 b'push',
1836 b'pushvars.server',
1830 b'pushvars.server',
1837 default=False,
1831 default=False,
1838 )
1832 )
1839 coreconfigitem(
1833 coreconfigitem(
1840 b'rewrite',
1834 b'rewrite',
1841 b'backup-bundle',
1835 b'backup-bundle',
1842 default=True,
1836 default=True,
1843 alias=[(b'ui', b'history-editing-backup')],
1837 alias=[(b'ui', b'history-editing-backup')],
1844 )
1838 )
1845 coreconfigitem(
1839 coreconfigitem(
1846 b'rewrite',
1840 b'rewrite',
1847 b'update-timestamp',
1841 b'update-timestamp',
1848 default=False,
1842 default=False,
1849 )
1843 )
1850 coreconfigitem(
1844 coreconfigitem(
1851 b'rewrite',
1845 b'rewrite',
1852 b'empty-successor',
1846 b'empty-successor',
1853 default=b'skip',
1847 default=b'skip',
1854 experimental=True,
1848 experimental=True,
1855 )
1849 )
1856 coreconfigitem(
1850 coreconfigitem(
1857 b'storage',
1851 b'storage',
1858 b'new-repo-backend',
1852 b'new-repo-backend',
1859 default=b'revlogv1',
1853 default=b'revlogv1',
1860 experimental=True,
1854 experimental=True,
1861 )
1855 )
1862 coreconfigitem(
1856 coreconfigitem(
1863 b'storage',
1857 b'storage',
1864 b'revlog.optimize-delta-parent-choice',
1858 b'revlog.optimize-delta-parent-choice',
1865 default=True,
1859 default=True,
1866 alias=[(b'format', b'aggressivemergedeltas')],
1860 alias=[(b'format', b'aggressivemergedeltas')],
1867 )
1861 )
1868 # experimental as long as rust is experimental (or a C version is implemented)
1862 # experimental as long as rust is experimental (or a C version is implemented)
1869 coreconfigitem(
1863 coreconfigitem(
1870 b'storage',
1864 b'storage',
1871 b'revlog.persistent-nodemap.mmap',
1865 b'revlog.persistent-nodemap.mmap',
1872 default=True,
1866 default=True,
1873 )
1867 )
1874 # experimental as long as format.use-persistent-nodemap is.
1868 # experimental as long as format.use-persistent-nodemap is.
1875 coreconfigitem(
1869 coreconfigitem(
1876 b'storage',
1870 b'storage',
1877 b'revlog.persistent-nodemap.slow-path',
1871 b'revlog.persistent-nodemap.slow-path',
1878 default=b"abort",
1872 default=b"abort",
1879 )
1873 )
1880
1874
1881 coreconfigitem(
1875 coreconfigitem(
1882 b'storage',
1876 b'storage',
1883 b'revlog.reuse-external-delta',
1877 b'revlog.reuse-external-delta',
1884 default=True,
1878 default=True,
1885 )
1879 )
1886 coreconfigitem(
1880 coreconfigitem(
1887 b'storage',
1881 b'storage',
1888 b'revlog.reuse-external-delta-parent',
1882 b'revlog.reuse-external-delta-parent',
1889 default=None,
1883 default=None,
1890 )
1884 )
1891 coreconfigitem(
1885 coreconfigitem(
1892 b'storage',
1886 b'storage',
1893 b'revlog.zlib.level',
1887 b'revlog.zlib.level',
1894 default=None,
1888 default=None,
1895 )
1889 )
1896 coreconfigitem(
1890 coreconfigitem(
1897 b'storage',
1891 b'storage',
1898 b'revlog.zstd.level',
1892 b'revlog.zstd.level',
1899 default=None,
1893 default=None,
1900 )
1894 )
1901 coreconfigitem(
1895 coreconfigitem(
1902 b'server',
1896 b'server',
1903 b'bookmarks-pushkey-compat',
1897 b'bookmarks-pushkey-compat',
1904 default=True,
1898 default=True,
1905 )
1899 )
1906 coreconfigitem(
1900 coreconfigitem(
1907 b'server',
1901 b'server',
1908 b'bundle1',
1902 b'bundle1',
1909 default=True,
1903 default=True,
1910 )
1904 )
1911 coreconfigitem(
1905 coreconfigitem(
1912 b'server',
1906 b'server',
1913 b'bundle1gd',
1907 b'bundle1gd',
1914 default=None,
1908 default=None,
1915 )
1909 )
1916 coreconfigitem(
1910 coreconfigitem(
1917 b'server',
1911 b'server',
1918 b'bundle1.pull',
1912 b'bundle1.pull',
1919 default=None,
1913 default=None,
1920 )
1914 )
1921 coreconfigitem(
1915 coreconfigitem(
1922 b'server',
1916 b'server',
1923 b'bundle1gd.pull',
1917 b'bundle1gd.pull',
1924 default=None,
1918 default=None,
1925 )
1919 )
1926 coreconfigitem(
1920 coreconfigitem(
1927 b'server',
1921 b'server',
1928 b'bundle1.push',
1922 b'bundle1.push',
1929 default=None,
1923 default=None,
1930 )
1924 )
1931 coreconfigitem(
1925 coreconfigitem(
1932 b'server',
1926 b'server',
1933 b'bundle1gd.push',
1927 b'bundle1gd.push',
1934 default=None,
1928 default=None,
1935 )
1929 )
1936 coreconfigitem(
1930 coreconfigitem(
1937 b'server',
1931 b'server',
1938 b'bundle2.stream',
1932 b'bundle2.stream',
1939 default=True,
1933 default=True,
1940 alias=[(b'experimental', b'bundle2.stream')],
1934 alias=[(b'experimental', b'bundle2.stream')],
1941 )
1935 )
1942 coreconfigitem(
1936 coreconfigitem(
1943 b'server',
1937 b'server',
1944 b'compressionengines',
1938 b'compressionengines',
1945 default=list,
1939 default=list,
1946 )
1940 )
1947 coreconfigitem(
1941 coreconfigitem(
1948 b'server',
1942 b'server',
1949 b'concurrent-push-mode',
1943 b'concurrent-push-mode',
1950 default=b'check-related',
1944 default=b'check-related',
1951 )
1945 )
1952 coreconfigitem(
1946 coreconfigitem(
1953 b'server',
1947 b'server',
1954 b'disablefullbundle',
1948 b'disablefullbundle',
1955 default=False,
1949 default=False,
1956 )
1950 )
1957 coreconfigitem(
1951 coreconfigitem(
1958 b'server',
1952 b'server',
1959 b'maxhttpheaderlen',
1953 b'maxhttpheaderlen',
1960 default=1024,
1954 default=1024,
1961 )
1955 )
1962 coreconfigitem(
1956 coreconfigitem(
1963 b'server',
1957 b'server',
1964 b'pullbundle',
1958 b'pullbundle',
1965 default=False,
1959 default=False,
1966 )
1960 )
1967 coreconfigitem(
1961 coreconfigitem(
1968 b'server',
1962 b'server',
1969 b'preferuncompressed',
1963 b'preferuncompressed',
1970 default=False,
1964 default=False,
1971 )
1965 )
1972 coreconfigitem(
1966 coreconfigitem(
1973 b'server',
1967 b'server',
1974 b'streamunbundle',
1968 b'streamunbundle',
1975 default=False,
1969 default=False,
1976 )
1970 )
1977 coreconfigitem(
1971 coreconfigitem(
1978 b'server',
1972 b'server',
1979 b'uncompressed',
1973 b'uncompressed',
1980 default=True,
1974 default=True,
1981 )
1975 )
1982 coreconfigitem(
1976 coreconfigitem(
1983 b'server',
1977 b'server',
1984 b'uncompressedallowsecret',
1978 b'uncompressedallowsecret',
1985 default=False,
1979 default=False,
1986 )
1980 )
1987 coreconfigitem(
1981 coreconfigitem(
1988 b'server',
1982 b'server',
1989 b'view',
1983 b'view',
1990 default=b'served',
1984 default=b'served',
1991 )
1985 )
1992 coreconfigitem(
1986 coreconfigitem(
1993 b'server',
1987 b'server',
1994 b'validate',
1988 b'validate',
1995 default=False,
1989 default=False,
1996 )
1990 )
1997 coreconfigitem(
1991 coreconfigitem(
1998 b'server',
1992 b'server',
1999 b'zliblevel',
1993 b'zliblevel',
2000 default=-1,
1994 default=-1,
2001 )
1995 )
2002 coreconfigitem(
1996 coreconfigitem(
2003 b'server',
1997 b'server',
2004 b'zstdlevel',
1998 b'zstdlevel',
2005 default=3,
1999 default=3,
2006 )
2000 )
2007 coreconfigitem(
2001 coreconfigitem(
2008 b'share',
2002 b'share',
2009 b'pool',
2003 b'pool',
2010 default=None,
2004 default=None,
2011 )
2005 )
2012 coreconfigitem(
2006 coreconfigitem(
2013 b'share',
2007 b'share',
2014 b'poolnaming',
2008 b'poolnaming',
2015 default=b'identity',
2009 default=b'identity',
2016 )
2010 )
2017 coreconfigitem(
2011 coreconfigitem(
2018 b'share',
2012 b'share',
2019 b'safe-mismatch.source-not-safe',
2013 b'safe-mismatch.source-not-safe',
2020 default=b'abort',
2014 default=b'abort',
2021 )
2015 )
2022 coreconfigitem(
2016 coreconfigitem(
2023 b'share',
2017 b'share',
2024 b'safe-mismatch.source-safe',
2018 b'safe-mismatch.source-safe',
2025 default=b'abort',
2019 default=b'abort',
2026 )
2020 )
2027 coreconfigitem(
2021 coreconfigitem(
2028 b'share',
2022 b'share',
2029 b'safe-mismatch.source-not-safe.warn',
2023 b'safe-mismatch.source-not-safe.warn',
2030 default=True,
2024 default=True,
2031 )
2025 )
2032 coreconfigitem(
2026 coreconfigitem(
2033 b'share',
2027 b'share',
2034 b'safe-mismatch.source-safe.warn',
2028 b'safe-mismatch.source-safe.warn',
2035 default=True,
2029 default=True,
2036 )
2030 )
2037 coreconfigitem(
2031 coreconfigitem(
2038 b'shelve',
2032 b'shelve',
2039 b'maxbackups',
2033 b'maxbackups',
2040 default=10,
2034 default=10,
2041 )
2035 )
2042 coreconfigitem(
2036 coreconfigitem(
2043 b'smtp',
2037 b'smtp',
2044 b'host',
2038 b'host',
2045 default=None,
2039 default=None,
2046 )
2040 )
2047 coreconfigitem(
2041 coreconfigitem(
2048 b'smtp',
2042 b'smtp',
2049 b'local_hostname',
2043 b'local_hostname',
2050 default=None,
2044 default=None,
2051 )
2045 )
2052 coreconfigitem(
2046 coreconfigitem(
2053 b'smtp',
2047 b'smtp',
2054 b'password',
2048 b'password',
2055 default=None,
2049 default=None,
2056 )
2050 )
2057 coreconfigitem(
2051 coreconfigitem(
2058 b'smtp',
2052 b'smtp',
2059 b'port',
2053 b'port',
2060 default=dynamicdefault,
2054 default=dynamicdefault,
2061 )
2055 )
2062 coreconfigitem(
2056 coreconfigitem(
2063 b'smtp',
2057 b'smtp',
2064 b'tls',
2058 b'tls',
2065 default=b'none',
2059 default=b'none',
2066 )
2060 )
2067 coreconfigitem(
2061 coreconfigitem(
2068 b'smtp',
2062 b'smtp',
2069 b'username',
2063 b'username',
2070 default=None,
2064 default=None,
2071 )
2065 )
2072 coreconfigitem(
2066 coreconfigitem(
2073 b'sparse',
2067 b'sparse',
2074 b'missingwarning',
2068 b'missingwarning',
2075 default=True,
2069 default=True,
2076 experimental=True,
2070 experimental=True,
2077 )
2071 )
2078 coreconfigitem(
2072 coreconfigitem(
2079 b'subrepos',
2073 b'subrepos',
2080 b'allowed',
2074 b'allowed',
2081 default=dynamicdefault, # to make backporting simpler
2075 default=dynamicdefault, # to make backporting simpler
2082 )
2076 )
2083 coreconfigitem(
2077 coreconfigitem(
2084 b'subrepos',
2078 b'subrepos',
2085 b'hg:allowed',
2079 b'hg:allowed',
2086 default=dynamicdefault,
2080 default=dynamicdefault,
2087 )
2081 )
2088 coreconfigitem(
2082 coreconfigitem(
2089 b'subrepos',
2083 b'subrepos',
2090 b'git:allowed',
2084 b'git:allowed',
2091 default=dynamicdefault,
2085 default=dynamicdefault,
2092 )
2086 )
2093 coreconfigitem(
2087 coreconfigitem(
2094 b'subrepos',
2088 b'subrepos',
2095 b'svn:allowed',
2089 b'svn:allowed',
2096 default=dynamicdefault,
2090 default=dynamicdefault,
2097 )
2091 )
2098 coreconfigitem(
2092 coreconfigitem(
2099 b'templates',
2093 b'templates',
2100 b'.*',
2094 b'.*',
2101 default=None,
2095 default=None,
2102 generic=True,
2096 generic=True,
2103 )
2097 )
2104 coreconfigitem(
2098 coreconfigitem(
2105 b'templateconfig',
2099 b'templateconfig',
2106 b'.*',
2100 b'.*',
2107 default=dynamicdefault,
2101 default=dynamicdefault,
2108 generic=True,
2102 generic=True,
2109 )
2103 )
2110 coreconfigitem(
2104 coreconfigitem(
2111 b'trusted',
2105 b'trusted',
2112 b'groups',
2106 b'groups',
2113 default=list,
2107 default=list,
2114 )
2108 )
2115 coreconfigitem(
2109 coreconfigitem(
2116 b'trusted',
2110 b'trusted',
2117 b'users',
2111 b'users',
2118 default=list,
2112 default=list,
2119 )
2113 )
2120 coreconfigitem(
2114 coreconfigitem(
2121 b'ui',
2115 b'ui',
2122 b'_usedassubrepo',
2116 b'_usedassubrepo',
2123 default=False,
2117 default=False,
2124 )
2118 )
2125 coreconfigitem(
2119 coreconfigitem(
2126 b'ui',
2120 b'ui',
2127 b'allowemptycommit',
2121 b'allowemptycommit',
2128 default=False,
2122 default=False,
2129 )
2123 )
2130 coreconfigitem(
2124 coreconfigitem(
2131 b'ui',
2125 b'ui',
2132 b'archivemeta',
2126 b'archivemeta',
2133 default=True,
2127 default=True,
2134 )
2128 )
2135 coreconfigitem(
2129 coreconfigitem(
2136 b'ui',
2130 b'ui',
2137 b'askusername',
2131 b'askusername',
2138 default=False,
2132 default=False,
2139 )
2133 )
2140 coreconfigitem(
2134 coreconfigitem(
2141 b'ui',
2135 b'ui',
2142 b'available-memory',
2136 b'available-memory',
2143 default=None,
2137 default=None,
2144 )
2138 )
2145
2139
2146 coreconfigitem(
2140 coreconfigitem(
2147 b'ui',
2141 b'ui',
2148 b'clonebundlefallback',
2142 b'clonebundlefallback',
2149 default=False,
2143 default=False,
2150 )
2144 )
2151 coreconfigitem(
2145 coreconfigitem(
2152 b'ui',
2146 b'ui',
2153 b'clonebundleprefers',
2147 b'clonebundleprefers',
2154 default=list,
2148 default=list,
2155 )
2149 )
2156 coreconfigitem(
2150 coreconfigitem(
2157 b'ui',
2151 b'ui',
2158 b'clonebundles',
2152 b'clonebundles',
2159 default=True,
2153 default=True,
2160 )
2154 )
2161 coreconfigitem(
2155 coreconfigitem(
2162 b'ui',
2156 b'ui',
2163 b'color',
2157 b'color',
2164 default=b'auto',
2158 default=b'auto',
2165 )
2159 )
2166 coreconfigitem(
2160 coreconfigitem(
2167 b'ui',
2161 b'ui',
2168 b'commitsubrepos',
2162 b'commitsubrepos',
2169 default=False,
2163 default=False,
2170 )
2164 )
2171 coreconfigitem(
2165 coreconfigitem(
2172 b'ui',
2166 b'ui',
2173 b'debug',
2167 b'debug',
2174 default=False,
2168 default=False,
2175 )
2169 )
2176 coreconfigitem(
2170 coreconfigitem(
2177 b'ui',
2171 b'ui',
2178 b'debugger',
2172 b'debugger',
2179 default=None,
2173 default=None,
2180 )
2174 )
2181 coreconfigitem(
2175 coreconfigitem(
2182 b'ui',
2176 b'ui',
2183 b'editor',
2177 b'editor',
2184 default=dynamicdefault,
2178 default=dynamicdefault,
2185 )
2179 )
2186 coreconfigitem(
2180 coreconfigitem(
2187 b'ui',
2181 b'ui',
2188 b'detailed-exit-code',
2182 b'detailed-exit-code',
2189 default=False,
2183 default=False,
2190 experimental=True,
2184 experimental=True,
2191 )
2185 )
2192 coreconfigitem(
2186 coreconfigitem(
2193 b'ui',
2187 b'ui',
2194 b'fallbackencoding',
2188 b'fallbackencoding',
2195 default=None,
2189 default=None,
2196 )
2190 )
2197 coreconfigitem(
2191 coreconfigitem(
2198 b'ui',
2192 b'ui',
2199 b'forcecwd',
2193 b'forcecwd',
2200 default=None,
2194 default=None,
2201 )
2195 )
2202 coreconfigitem(
2196 coreconfigitem(
2203 b'ui',
2197 b'ui',
2204 b'forcemerge',
2198 b'forcemerge',
2205 default=None,
2199 default=None,
2206 )
2200 )
2207 coreconfigitem(
2201 coreconfigitem(
2208 b'ui',
2202 b'ui',
2209 b'formatdebug',
2203 b'formatdebug',
2210 default=False,
2204 default=False,
2211 )
2205 )
2212 coreconfigitem(
2206 coreconfigitem(
2213 b'ui',
2207 b'ui',
2214 b'formatjson',
2208 b'formatjson',
2215 default=False,
2209 default=False,
2216 )
2210 )
2217 coreconfigitem(
2211 coreconfigitem(
2218 b'ui',
2212 b'ui',
2219 b'formatted',
2213 b'formatted',
2220 default=None,
2214 default=None,
2221 )
2215 )
2222 coreconfigitem(
2216 coreconfigitem(
2223 b'ui',
2217 b'ui',
2224 b'interactive',
2218 b'interactive',
2225 default=None,
2219 default=None,
2226 )
2220 )
2227 coreconfigitem(
2221 coreconfigitem(
2228 b'ui',
2222 b'ui',
2229 b'interface',
2223 b'interface',
2230 default=None,
2224 default=None,
2231 )
2225 )
2232 coreconfigitem(
2226 coreconfigitem(
2233 b'ui',
2227 b'ui',
2234 b'interface.chunkselector',
2228 b'interface.chunkselector',
2235 default=None,
2229 default=None,
2236 )
2230 )
2237 coreconfigitem(
2231 coreconfigitem(
2238 b'ui',
2232 b'ui',
2239 b'large-file-limit',
2233 b'large-file-limit',
2240 default=10000000,
2234 default=10000000,
2241 )
2235 )
2242 coreconfigitem(
2236 coreconfigitem(
2243 b'ui',
2237 b'ui',
2244 b'logblockedtimes',
2238 b'logblockedtimes',
2245 default=False,
2239 default=False,
2246 )
2240 )
2247 coreconfigitem(
2241 coreconfigitem(
2248 b'ui',
2242 b'ui',
2249 b'merge',
2243 b'merge',
2250 default=None,
2244 default=None,
2251 )
2245 )
2252 coreconfigitem(
2246 coreconfigitem(
2253 b'ui',
2247 b'ui',
2254 b'mergemarkers',
2248 b'mergemarkers',
2255 default=b'basic',
2249 default=b'basic',
2256 )
2250 )
2257 coreconfigitem(
2251 coreconfigitem(
2258 b'ui',
2252 b'ui',
2259 b'message-output',
2253 b'message-output',
2260 default=b'stdio',
2254 default=b'stdio',
2261 )
2255 )
2262 coreconfigitem(
2256 coreconfigitem(
2263 b'ui',
2257 b'ui',
2264 b'nontty',
2258 b'nontty',
2265 default=False,
2259 default=False,
2266 )
2260 )
2267 coreconfigitem(
2261 coreconfigitem(
2268 b'ui',
2262 b'ui',
2269 b'origbackuppath',
2263 b'origbackuppath',
2270 default=None,
2264 default=None,
2271 )
2265 )
2272 coreconfigitem(
2266 coreconfigitem(
2273 b'ui',
2267 b'ui',
2274 b'paginate',
2268 b'paginate',
2275 default=True,
2269 default=True,
2276 )
2270 )
2277 coreconfigitem(
2271 coreconfigitem(
2278 b'ui',
2272 b'ui',
2279 b'patch',
2273 b'patch',
2280 default=None,
2274 default=None,
2281 )
2275 )
2282 coreconfigitem(
2276 coreconfigitem(
2283 b'ui',
2277 b'ui',
2284 b'portablefilenames',
2278 b'portablefilenames',
2285 default=b'warn',
2279 default=b'warn',
2286 )
2280 )
2287 coreconfigitem(
2281 coreconfigitem(
2288 b'ui',
2282 b'ui',
2289 b'promptecho',
2283 b'promptecho',
2290 default=False,
2284 default=False,
2291 )
2285 )
2292 coreconfigitem(
2286 coreconfigitem(
2293 b'ui',
2287 b'ui',
2294 b'quiet',
2288 b'quiet',
2295 default=False,
2289 default=False,
2296 )
2290 )
2297 coreconfigitem(
2291 coreconfigitem(
2298 b'ui',
2292 b'ui',
2299 b'quietbookmarkmove',
2293 b'quietbookmarkmove',
2300 default=False,
2294 default=False,
2301 )
2295 )
2302 coreconfigitem(
2296 coreconfigitem(
2303 b'ui',
2297 b'ui',
2304 b'relative-paths',
2298 b'relative-paths',
2305 default=b'legacy',
2299 default=b'legacy',
2306 )
2300 )
2307 coreconfigitem(
2301 coreconfigitem(
2308 b'ui',
2302 b'ui',
2309 b'remotecmd',
2303 b'remotecmd',
2310 default=b'hg',
2304 default=b'hg',
2311 )
2305 )
2312 coreconfigitem(
2306 coreconfigitem(
2313 b'ui',
2307 b'ui',
2314 b'report_untrusted',
2308 b'report_untrusted',
2315 default=True,
2309 default=True,
2316 )
2310 )
2317 coreconfigitem(
2311 coreconfigitem(
2318 b'ui',
2312 b'ui',
2319 b'rollback',
2313 b'rollback',
2320 default=True,
2314 default=True,
2321 )
2315 )
2322 coreconfigitem(
2316 coreconfigitem(
2323 b'ui',
2317 b'ui',
2324 b'signal-safe-lock',
2318 b'signal-safe-lock',
2325 default=True,
2319 default=True,
2326 )
2320 )
2327 coreconfigitem(
2321 coreconfigitem(
2328 b'ui',
2322 b'ui',
2329 b'slash',
2323 b'slash',
2330 default=False,
2324 default=False,
2331 )
2325 )
2332 coreconfigitem(
2326 coreconfigitem(
2333 b'ui',
2327 b'ui',
2334 b'ssh',
2328 b'ssh',
2335 default=b'ssh',
2329 default=b'ssh',
2336 )
2330 )
2337 coreconfigitem(
2331 coreconfigitem(
2338 b'ui',
2332 b'ui',
2339 b'ssherrorhint',
2333 b'ssherrorhint',
2340 default=None,
2334 default=None,
2341 )
2335 )
2342 coreconfigitem(
2336 coreconfigitem(
2343 b'ui',
2337 b'ui',
2344 b'statuscopies',
2338 b'statuscopies',
2345 default=False,
2339 default=False,
2346 )
2340 )
2347 coreconfigitem(
2341 coreconfigitem(
2348 b'ui',
2342 b'ui',
2349 b'strict',
2343 b'strict',
2350 default=False,
2344 default=False,
2351 )
2345 )
2352 coreconfigitem(
2346 coreconfigitem(
2353 b'ui',
2347 b'ui',
2354 b'style',
2348 b'style',
2355 default=b'',
2349 default=b'',
2356 )
2350 )
2357 coreconfigitem(
2351 coreconfigitem(
2358 b'ui',
2352 b'ui',
2359 b'supportcontact',
2353 b'supportcontact',
2360 default=None,
2354 default=None,
2361 )
2355 )
2362 coreconfigitem(
2356 coreconfigitem(
2363 b'ui',
2357 b'ui',
2364 b'textwidth',
2358 b'textwidth',
2365 default=78,
2359 default=78,
2366 )
2360 )
2367 coreconfigitem(
2361 coreconfigitem(
2368 b'ui',
2362 b'ui',
2369 b'timeout',
2363 b'timeout',
2370 default=b'600',
2364 default=b'600',
2371 )
2365 )
2372 coreconfigitem(
2366 coreconfigitem(
2373 b'ui',
2367 b'ui',
2374 b'timeout.warn',
2368 b'timeout.warn',
2375 default=0,
2369 default=0,
2376 )
2370 )
2377 coreconfigitem(
2371 coreconfigitem(
2378 b'ui',
2372 b'ui',
2379 b'timestamp-output',
2373 b'timestamp-output',
2380 default=False,
2374 default=False,
2381 )
2375 )
2382 coreconfigitem(
2376 coreconfigitem(
2383 b'ui',
2377 b'ui',
2384 b'traceback',
2378 b'traceback',
2385 default=False,
2379 default=False,
2386 )
2380 )
2387 coreconfigitem(
2381 coreconfigitem(
2388 b'ui',
2382 b'ui',
2389 b'tweakdefaults',
2383 b'tweakdefaults',
2390 default=False,
2384 default=False,
2391 )
2385 )
2392 coreconfigitem(b'ui', b'username', alias=[(b'ui', b'user')])
2386 coreconfigitem(b'ui', b'username', alias=[(b'ui', b'user')])
2393 coreconfigitem(
2387 coreconfigitem(
2394 b'ui',
2388 b'ui',
2395 b'verbose',
2389 b'verbose',
2396 default=False,
2390 default=False,
2397 )
2391 )
2398 coreconfigitem(
2392 coreconfigitem(
2399 b'verify',
2393 b'verify',
2400 b'skipflags',
2394 b'skipflags',
2401 default=None,
2395 default=None,
2402 )
2396 )
2403 coreconfigitem(
2397 coreconfigitem(
2404 b'web',
2398 b'web',
2405 b'allowbz2',
2399 b'allowbz2',
2406 default=False,
2400 default=False,
2407 )
2401 )
2408 coreconfigitem(
2402 coreconfigitem(
2409 b'web',
2403 b'web',
2410 b'allowgz',
2404 b'allowgz',
2411 default=False,
2405 default=False,
2412 )
2406 )
2413 coreconfigitem(
2407 coreconfigitem(
2414 b'web',
2408 b'web',
2415 b'allow-pull',
2409 b'allow-pull',
2416 alias=[(b'web', b'allowpull')],
2410 alias=[(b'web', b'allowpull')],
2417 default=True,
2411 default=True,
2418 )
2412 )
2419 coreconfigitem(
2413 coreconfigitem(
2420 b'web',
2414 b'web',
2421 b'allow-push',
2415 b'allow-push',
2422 alias=[(b'web', b'allow_push')],
2416 alias=[(b'web', b'allow_push')],
2423 default=list,
2417 default=list,
2424 )
2418 )
2425 coreconfigitem(
2419 coreconfigitem(
2426 b'web',
2420 b'web',
2427 b'allowzip',
2421 b'allowzip',
2428 default=False,
2422 default=False,
2429 )
2423 )
2430 coreconfigitem(
2424 coreconfigitem(
2431 b'web',
2425 b'web',
2432 b'archivesubrepos',
2426 b'archivesubrepos',
2433 default=False,
2427 default=False,
2434 )
2428 )
2435 coreconfigitem(
2429 coreconfigitem(
2436 b'web',
2430 b'web',
2437 b'cache',
2431 b'cache',
2438 default=True,
2432 default=True,
2439 )
2433 )
2440 coreconfigitem(
2434 coreconfigitem(
2441 b'web',
2435 b'web',
2442 b'comparisoncontext',
2436 b'comparisoncontext',
2443 default=5,
2437 default=5,
2444 )
2438 )
2445 coreconfigitem(
2439 coreconfigitem(
2446 b'web',
2440 b'web',
2447 b'contact',
2441 b'contact',
2448 default=None,
2442 default=None,
2449 )
2443 )
2450 coreconfigitem(
2444 coreconfigitem(
2451 b'web',
2445 b'web',
2452 b'deny_push',
2446 b'deny_push',
2453 default=list,
2447 default=list,
2454 )
2448 )
2455 coreconfigitem(
2449 coreconfigitem(
2456 b'web',
2450 b'web',
2457 b'guessmime',
2451 b'guessmime',
2458 default=False,
2452 default=False,
2459 )
2453 )
2460 coreconfigitem(
2454 coreconfigitem(
2461 b'web',
2455 b'web',
2462 b'hidden',
2456 b'hidden',
2463 default=False,
2457 default=False,
2464 )
2458 )
2465 coreconfigitem(
2459 coreconfigitem(
2466 b'web',
2460 b'web',
2467 b'labels',
2461 b'labels',
2468 default=list,
2462 default=list,
2469 )
2463 )
2470 coreconfigitem(
2464 coreconfigitem(
2471 b'web',
2465 b'web',
2472 b'logoimg',
2466 b'logoimg',
2473 default=b'hglogo.png',
2467 default=b'hglogo.png',
2474 )
2468 )
2475 coreconfigitem(
2469 coreconfigitem(
2476 b'web',
2470 b'web',
2477 b'logourl',
2471 b'logourl',
2478 default=b'https://mercurial-scm.org/',
2472 default=b'https://mercurial-scm.org/',
2479 )
2473 )
2480 coreconfigitem(
2474 coreconfigitem(
2481 b'web',
2475 b'web',
2482 b'accesslog',
2476 b'accesslog',
2483 default=b'-',
2477 default=b'-',
2484 )
2478 )
2485 coreconfigitem(
2479 coreconfigitem(
2486 b'web',
2480 b'web',
2487 b'address',
2481 b'address',
2488 default=b'',
2482 default=b'',
2489 )
2483 )
2490 coreconfigitem(
2484 coreconfigitem(
2491 b'web',
2485 b'web',
2492 b'allow-archive',
2486 b'allow-archive',
2493 alias=[(b'web', b'allow_archive')],
2487 alias=[(b'web', b'allow_archive')],
2494 default=list,
2488 default=list,
2495 )
2489 )
2496 coreconfigitem(
2490 coreconfigitem(
2497 b'web',
2491 b'web',
2498 b'allow_read',
2492 b'allow_read',
2499 default=list,
2493 default=list,
2500 )
2494 )
2501 coreconfigitem(
2495 coreconfigitem(
2502 b'web',
2496 b'web',
2503 b'baseurl',
2497 b'baseurl',
2504 default=None,
2498 default=None,
2505 )
2499 )
2506 coreconfigitem(
2500 coreconfigitem(
2507 b'web',
2501 b'web',
2508 b'cacerts',
2502 b'cacerts',
2509 default=None,
2503 default=None,
2510 )
2504 )
2511 coreconfigitem(
2505 coreconfigitem(
2512 b'web',
2506 b'web',
2513 b'certificate',
2507 b'certificate',
2514 default=None,
2508 default=None,
2515 )
2509 )
2516 coreconfigitem(
2510 coreconfigitem(
2517 b'web',
2511 b'web',
2518 b'collapse',
2512 b'collapse',
2519 default=False,
2513 default=False,
2520 )
2514 )
2521 coreconfigitem(
2515 coreconfigitem(
2522 b'web',
2516 b'web',
2523 b'csp',
2517 b'csp',
2524 default=None,
2518 default=None,
2525 )
2519 )
2526 coreconfigitem(
2520 coreconfigitem(
2527 b'web',
2521 b'web',
2528 b'deny_read',
2522 b'deny_read',
2529 default=list,
2523 default=list,
2530 )
2524 )
2531 coreconfigitem(
2525 coreconfigitem(
2532 b'web',
2526 b'web',
2533 b'descend',
2527 b'descend',
2534 default=True,
2528 default=True,
2535 )
2529 )
2536 coreconfigitem(
2530 coreconfigitem(
2537 b'web',
2531 b'web',
2538 b'description',
2532 b'description',
2539 default=b"",
2533 default=b"",
2540 )
2534 )
2541 coreconfigitem(
2535 coreconfigitem(
2542 b'web',
2536 b'web',
2543 b'encoding',
2537 b'encoding',
2544 default=lambda: encoding.encoding,
2538 default=lambda: encoding.encoding,
2545 )
2539 )
2546 coreconfigitem(
2540 coreconfigitem(
2547 b'web',
2541 b'web',
2548 b'errorlog',
2542 b'errorlog',
2549 default=b'-',
2543 default=b'-',
2550 )
2544 )
2551 coreconfigitem(
2545 coreconfigitem(
2552 b'web',
2546 b'web',
2553 b'ipv6',
2547 b'ipv6',
2554 default=False,
2548 default=False,
2555 )
2549 )
2556 coreconfigitem(
2550 coreconfigitem(
2557 b'web',
2551 b'web',
2558 b'maxchanges',
2552 b'maxchanges',
2559 default=10,
2553 default=10,
2560 )
2554 )
2561 coreconfigitem(
2555 coreconfigitem(
2562 b'web',
2556 b'web',
2563 b'maxfiles',
2557 b'maxfiles',
2564 default=10,
2558 default=10,
2565 )
2559 )
2566 coreconfigitem(
2560 coreconfigitem(
2567 b'web',
2561 b'web',
2568 b'maxshortchanges',
2562 b'maxshortchanges',
2569 default=60,
2563 default=60,
2570 )
2564 )
2571 coreconfigitem(
2565 coreconfigitem(
2572 b'web',
2566 b'web',
2573 b'motd',
2567 b'motd',
2574 default=b'',
2568 default=b'',
2575 )
2569 )
2576 coreconfigitem(
2570 coreconfigitem(
2577 b'web',
2571 b'web',
2578 b'name',
2572 b'name',
2579 default=dynamicdefault,
2573 default=dynamicdefault,
2580 )
2574 )
2581 coreconfigitem(
2575 coreconfigitem(
2582 b'web',
2576 b'web',
2583 b'port',
2577 b'port',
2584 default=8000,
2578 default=8000,
2585 )
2579 )
2586 coreconfigitem(
2580 coreconfigitem(
2587 b'web',
2581 b'web',
2588 b'prefix',
2582 b'prefix',
2589 default=b'',
2583 default=b'',
2590 )
2584 )
2591 coreconfigitem(
2585 coreconfigitem(
2592 b'web',
2586 b'web',
2593 b'push_ssl',
2587 b'push_ssl',
2594 default=True,
2588 default=True,
2595 )
2589 )
2596 coreconfigitem(
2590 coreconfigitem(
2597 b'web',
2591 b'web',
2598 b'refreshinterval',
2592 b'refreshinterval',
2599 default=20,
2593 default=20,
2600 )
2594 )
2601 coreconfigitem(
2595 coreconfigitem(
2602 b'web',
2596 b'web',
2603 b'server-header',
2597 b'server-header',
2604 default=None,
2598 default=None,
2605 )
2599 )
2606 coreconfigitem(
2600 coreconfigitem(
2607 b'web',
2601 b'web',
2608 b'static',
2602 b'static',
2609 default=None,
2603 default=None,
2610 )
2604 )
2611 coreconfigitem(
2605 coreconfigitem(
2612 b'web',
2606 b'web',
2613 b'staticurl',
2607 b'staticurl',
2614 default=None,
2608 default=None,
2615 )
2609 )
2616 coreconfigitem(
2610 coreconfigitem(
2617 b'web',
2611 b'web',
2618 b'stripes',
2612 b'stripes',
2619 default=1,
2613 default=1,
2620 )
2614 )
2621 coreconfigitem(
2615 coreconfigitem(
2622 b'web',
2616 b'web',
2623 b'style',
2617 b'style',
2624 default=b'paper',
2618 default=b'paper',
2625 )
2619 )
2626 coreconfigitem(
2620 coreconfigitem(
2627 b'web',
2621 b'web',
2628 b'templates',
2622 b'templates',
2629 default=None,
2623 default=None,
2630 )
2624 )
2631 coreconfigitem(
2625 coreconfigitem(
2632 b'web',
2626 b'web',
2633 b'view',
2627 b'view',
2634 default=b'served',
2628 default=b'served',
2635 experimental=True,
2629 experimental=True,
2636 )
2630 )
2637 coreconfigitem(
2631 coreconfigitem(
2638 b'worker',
2632 b'worker',
2639 b'backgroundclose',
2633 b'backgroundclose',
2640 default=dynamicdefault,
2634 default=dynamicdefault,
2641 )
2635 )
2642 # Windows defaults to a limit of 512 open files. A buffer of 128
2636 # Windows defaults to a limit of 512 open files. A buffer of 128
2643 # should give us enough headway.
2637 # should give us enough headway.
2644 coreconfigitem(
2638 coreconfigitem(
2645 b'worker',
2639 b'worker',
2646 b'backgroundclosemaxqueue',
2640 b'backgroundclosemaxqueue',
2647 default=384,
2641 default=384,
2648 )
2642 )
2649 coreconfigitem(
2643 coreconfigitem(
2650 b'worker',
2644 b'worker',
2651 b'backgroundcloseminfilecount',
2645 b'backgroundcloseminfilecount',
2652 default=2048,
2646 default=2048,
2653 )
2647 )
2654 coreconfigitem(
2648 coreconfigitem(
2655 b'worker',
2649 b'worker',
2656 b'backgroundclosethreadcount',
2650 b'backgroundclosethreadcount',
2657 default=4,
2651 default=4,
2658 )
2652 )
2659 coreconfigitem(
2653 coreconfigitem(
2660 b'worker',
2654 b'worker',
2661 b'enabled',
2655 b'enabled',
2662 default=True,
2656 default=True,
2663 )
2657 )
2664 coreconfigitem(
2658 coreconfigitem(
2665 b'worker',
2659 b'worker',
2666 b'numcpus',
2660 b'numcpus',
2667 default=None,
2661 default=None,
2668 )
2662 )
2669
2663
2670 # Rebase related configuration moved to core because other extension are doing
2664 # Rebase related configuration moved to core because other extension are doing
2671 # strange things. For example, shelve import the extensions to reuse some bit
2665 # strange things. For example, shelve import the extensions to reuse some bit
2672 # without formally loading it.
2666 # without formally loading it.
2673 coreconfigitem(
2667 coreconfigitem(
2674 b'commands',
2668 b'commands',
2675 b'rebase.requiredest',
2669 b'rebase.requiredest',
2676 default=False,
2670 default=False,
2677 )
2671 )
2678 coreconfigitem(
2672 coreconfigitem(
2679 b'experimental',
2673 b'experimental',
2680 b'rebaseskipobsolete',
2674 b'rebaseskipobsolete',
2681 default=True,
2675 default=True,
2682 )
2676 )
2683 coreconfigitem(
2677 coreconfigitem(
2684 b'rebase',
2678 b'rebase',
2685 b'singletransaction',
2679 b'singletransaction',
2686 default=False,
2680 default=False,
2687 )
2681 )
2688 coreconfigitem(
2682 coreconfigitem(
2689 b'rebase',
2683 b'rebase',
2690 b'experimental.inmemory',
2684 b'experimental.inmemory',
2691 default=False,
2685 default=False,
2692 )
2686 )
@@ -1,3773 +1,3763 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import functools
11 import functools
12 import os
12 import os
13 import random
13 import random
14 import sys
14 import sys
15 import time
15 import time
16 import weakref
16 import weakref
17
17
18 from .i18n import _
18 from .i18n import _
19 from .node import (
19 from .node import (
20 bin,
20 bin,
21 hex,
21 hex,
22 nullrev,
22 nullrev,
23 sha1nodeconstants,
23 sha1nodeconstants,
24 short,
24 short,
25 )
25 )
26 from .pycompat import (
26 from .pycompat import (
27 delattr,
27 delattr,
28 getattr,
28 getattr,
29 )
29 )
30 from . import (
30 from . import (
31 bookmarks,
31 bookmarks,
32 branchmap,
32 branchmap,
33 bundle2,
33 bundle2,
34 bundlecaches,
34 bundlecaches,
35 changegroup,
35 changegroup,
36 color,
36 color,
37 commit,
37 commit,
38 context,
38 context,
39 dirstate,
39 dirstate,
40 dirstateguard,
40 dirstateguard,
41 discovery,
41 discovery,
42 encoding,
42 encoding,
43 error,
43 error,
44 exchange,
44 exchange,
45 extensions,
45 extensions,
46 filelog,
46 filelog,
47 hook,
47 hook,
48 lock as lockmod,
48 lock as lockmod,
49 match as matchmod,
49 match as matchmod,
50 mergestate as mergestatemod,
50 mergestate as mergestatemod,
51 mergeutil,
51 mergeutil,
52 namespaces,
52 namespaces,
53 narrowspec,
53 narrowspec,
54 obsolete,
54 obsolete,
55 pathutil,
55 pathutil,
56 phases,
56 phases,
57 pushkey,
57 pushkey,
58 pycompat,
58 pycompat,
59 rcutil,
59 rcutil,
60 repoview,
60 repoview,
61 requirements as requirementsmod,
61 requirements as requirementsmod,
62 revlog,
62 revlog,
63 revset,
63 revset,
64 revsetlang,
64 revsetlang,
65 scmutil,
65 scmutil,
66 sparse,
66 sparse,
67 store as storemod,
67 store as storemod,
68 subrepoutil,
68 subrepoutil,
69 tags as tagsmod,
69 tags as tagsmod,
70 transaction,
70 transaction,
71 txnutil,
71 txnutil,
72 util,
72 util,
73 vfs as vfsmod,
73 vfs as vfsmod,
74 wireprototypes,
74 wireprototypes,
75 )
75 )
76
76
77 from .interfaces import (
77 from .interfaces import (
78 repository,
78 repository,
79 util as interfaceutil,
79 util as interfaceutil,
80 )
80 )
81
81
82 from .utils import (
82 from .utils import (
83 hashutil,
83 hashutil,
84 procutil,
84 procutil,
85 stringutil,
85 stringutil,
86 urlutil,
86 urlutil,
87 )
87 )
88
88
89 from .revlogutils import (
89 from .revlogutils import (
90 concurrency_checker as revlogchecker,
90 concurrency_checker as revlogchecker,
91 constants as revlogconst,
91 constants as revlogconst,
92 sidedata as sidedatamod,
92 sidedata as sidedatamod,
93 )
93 )
94
94
95 release = lockmod.release
95 release = lockmod.release
96 urlerr = util.urlerr
96 urlerr = util.urlerr
97 urlreq = util.urlreq
97 urlreq = util.urlreq
98
98
99 # set of (path, vfs-location) tuples. vfs-location is:
99 # set of (path, vfs-location) tuples. vfs-location is:
100 # - 'plain for vfs relative paths
100 # - 'plain for vfs relative paths
101 # - '' for svfs relative paths
101 # - '' for svfs relative paths
102 _cachedfiles = set()
102 _cachedfiles = set()
103
103
104
104
105 class _basefilecache(scmutil.filecache):
105 class _basefilecache(scmutil.filecache):
106 """All filecache usage on repo are done for logic that should be unfiltered"""
106 """All filecache usage on repo are done for logic that should be unfiltered"""
107
107
108 def __get__(self, repo, type=None):
108 def __get__(self, repo, type=None):
109 if repo is None:
109 if repo is None:
110 return self
110 return self
111 # proxy to unfiltered __dict__ since filtered repo has no entry
111 # proxy to unfiltered __dict__ since filtered repo has no entry
112 unfi = repo.unfiltered()
112 unfi = repo.unfiltered()
113 try:
113 try:
114 return unfi.__dict__[self.sname]
114 return unfi.__dict__[self.sname]
115 except KeyError:
115 except KeyError:
116 pass
116 pass
117 return super(_basefilecache, self).__get__(unfi, type)
117 return super(_basefilecache, self).__get__(unfi, type)
118
118
119 def set(self, repo, value):
119 def set(self, repo, value):
120 return super(_basefilecache, self).set(repo.unfiltered(), value)
120 return super(_basefilecache, self).set(repo.unfiltered(), value)
121
121
122
122
123 class repofilecache(_basefilecache):
123 class repofilecache(_basefilecache):
124 """filecache for files in .hg but outside of .hg/store"""
124 """filecache for files in .hg but outside of .hg/store"""
125
125
126 def __init__(self, *paths):
126 def __init__(self, *paths):
127 super(repofilecache, self).__init__(*paths)
127 super(repofilecache, self).__init__(*paths)
128 for path in paths:
128 for path in paths:
129 _cachedfiles.add((path, b'plain'))
129 _cachedfiles.add((path, b'plain'))
130
130
131 def join(self, obj, fname):
131 def join(self, obj, fname):
132 return obj.vfs.join(fname)
132 return obj.vfs.join(fname)
133
133
134
134
135 class storecache(_basefilecache):
135 class storecache(_basefilecache):
136 """filecache for files in the store"""
136 """filecache for files in the store"""
137
137
138 def __init__(self, *paths):
138 def __init__(self, *paths):
139 super(storecache, self).__init__(*paths)
139 super(storecache, self).__init__(*paths)
140 for path in paths:
140 for path in paths:
141 _cachedfiles.add((path, b''))
141 _cachedfiles.add((path, b''))
142
142
143 def join(self, obj, fname):
143 def join(self, obj, fname):
144 return obj.sjoin(fname)
144 return obj.sjoin(fname)
145
145
146
146
147 class mixedrepostorecache(_basefilecache):
147 class mixedrepostorecache(_basefilecache):
148 """filecache for a mix files in .hg/store and outside"""
148 """filecache for a mix files in .hg/store and outside"""
149
149
150 def __init__(self, *pathsandlocations):
150 def __init__(self, *pathsandlocations):
151 # scmutil.filecache only uses the path for passing back into our
151 # scmutil.filecache only uses the path for passing back into our
152 # join(), so we can safely pass a list of paths and locations
152 # join(), so we can safely pass a list of paths and locations
153 super(mixedrepostorecache, self).__init__(*pathsandlocations)
153 super(mixedrepostorecache, self).__init__(*pathsandlocations)
154 _cachedfiles.update(pathsandlocations)
154 _cachedfiles.update(pathsandlocations)
155
155
156 def join(self, obj, fnameandlocation):
156 def join(self, obj, fnameandlocation):
157 fname, location = fnameandlocation
157 fname, location = fnameandlocation
158 if location == b'plain':
158 if location == b'plain':
159 return obj.vfs.join(fname)
159 return obj.vfs.join(fname)
160 else:
160 else:
161 if location != b'':
161 if location != b'':
162 raise error.ProgrammingError(
162 raise error.ProgrammingError(
163 b'unexpected location: %s' % location
163 b'unexpected location: %s' % location
164 )
164 )
165 return obj.sjoin(fname)
165 return obj.sjoin(fname)
166
166
167
167
168 def isfilecached(repo, name):
168 def isfilecached(repo, name):
169 """check if a repo has already cached "name" filecache-ed property
169 """check if a repo has already cached "name" filecache-ed property
170
170
171 This returns (cachedobj-or-None, iscached) tuple.
171 This returns (cachedobj-or-None, iscached) tuple.
172 """
172 """
173 cacheentry = repo.unfiltered()._filecache.get(name, None)
173 cacheentry = repo.unfiltered()._filecache.get(name, None)
174 if not cacheentry:
174 if not cacheentry:
175 return None, False
175 return None, False
176 return cacheentry.obj, True
176 return cacheentry.obj, True
177
177
178
178
179 class unfilteredpropertycache(util.propertycache):
179 class unfilteredpropertycache(util.propertycache):
180 """propertycache that apply to unfiltered repo only"""
180 """propertycache that apply to unfiltered repo only"""
181
181
182 def __get__(self, repo, type=None):
182 def __get__(self, repo, type=None):
183 unfi = repo.unfiltered()
183 unfi = repo.unfiltered()
184 if unfi is repo:
184 if unfi is repo:
185 return super(unfilteredpropertycache, self).__get__(unfi)
185 return super(unfilteredpropertycache, self).__get__(unfi)
186 return getattr(unfi, self.name)
186 return getattr(unfi, self.name)
187
187
188
188
189 class filteredpropertycache(util.propertycache):
189 class filteredpropertycache(util.propertycache):
190 """propertycache that must take filtering in account"""
190 """propertycache that must take filtering in account"""
191
191
192 def cachevalue(self, obj, value):
192 def cachevalue(self, obj, value):
193 object.__setattr__(obj, self.name, value)
193 object.__setattr__(obj, self.name, value)
194
194
195
195
196 def hasunfilteredcache(repo, name):
196 def hasunfilteredcache(repo, name):
197 """check if a repo has an unfilteredpropertycache value for <name>"""
197 """check if a repo has an unfilteredpropertycache value for <name>"""
198 return name in vars(repo.unfiltered())
198 return name in vars(repo.unfiltered())
199
199
200
200
201 def unfilteredmethod(orig):
201 def unfilteredmethod(orig):
202 """decorate method that always need to be run on unfiltered version"""
202 """decorate method that always need to be run on unfiltered version"""
203
203
204 @functools.wraps(orig)
204 @functools.wraps(orig)
205 def wrapper(repo, *args, **kwargs):
205 def wrapper(repo, *args, **kwargs):
206 return orig(repo.unfiltered(), *args, **kwargs)
206 return orig(repo.unfiltered(), *args, **kwargs)
207
207
208 return wrapper
208 return wrapper
209
209
210
210
211 moderncaps = {
211 moderncaps = {
212 b'lookup',
212 b'lookup',
213 b'branchmap',
213 b'branchmap',
214 b'pushkey',
214 b'pushkey',
215 b'known',
215 b'known',
216 b'getbundle',
216 b'getbundle',
217 b'unbundle',
217 b'unbundle',
218 }
218 }
219 legacycaps = moderncaps.union({b'changegroupsubset'})
219 legacycaps = moderncaps.union({b'changegroupsubset'})
220
220
221
221
222 @interfaceutil.implementer(repository.ipeercommandexecutor)
222 @interfaceutil.implementer(repository.ipeercommandexecutor)
223 class localcommandexecutor(object):
223 class localcommandexecutor(object):
224 def __init__(self, peer):
224 def __init__(self, peer):
225 self._peer = peer
225 self._peer = peer
226 self._sent = False
226 self._sent = False
227 self._closed = False
227 self._closed = False
228
228
229 def __enter__(self):
229 def __enter__(self):
230 return self
230 return self
231
231
232 def __exit__(self, exctype, excvalue, exctb):
232 def __exit__(self, exctype, excvalue, exctb):
233 self.close()
233 self.close()
234
234
235 def callcommand(self, command, args):
235 def callcommand(self, command, args):
236 if self._sent:
236 if self._sent:
237 raise error.ProgrammingError(
237 raise error.ProgrammingError(
238 b'callcommand() cannot be used after sendcommands()'
238 b'callcommand() cannot be used after sendcommands()'
239 )
239 )
240
240
241 if self._closed:
241 if self._closed:
242 raise error.ProgrammingError(
242 raise error.ProgrammingError(
243 b'callcommand() cannot be used after close()'
243 b'callcommand() cannot be used after close()'
244 )
244 )
245
245
246 # We don't need to support anything fancy. Just call the named
246 # We don't need to support anything fancy. Just call the named
247 # method on the peer and return a resolved future.
247 # method on the peer and return a resolved future.
248 fn = getattr(self._peer, pycompat.sysstr(command))
248 fn = getattr(self._peer, pycompat.sysstr(command))
249
249
250 f = pycompat.futures.Future()
250 f = pycompat.futures.Future()
251
251
252 try:
252 try:
253 result = fn(**pycompat.strkwargs(args))
253 result = fn(**pycompat.strkwargs(args))
254 except Exception:
254 except Exception:
255 pycompat.future_set_exception_info(f, sys.exc_info()[1:])
255 pycompat.future_set_exception_info(f, sys.exc_info()[1:])
256 else:
256 else:
257 f.set_result(result)
257 f.set_result(result)
258
258
259 return f
259 return f
260
260
261 def sendcommands(self):
261 def sendcommands(self):
262 self._sent = True
262 self._sent = True
263
263
264 def close(self):
264 def close(self):
265 self._closed = True
265 self._closed = True
266
266
267
267
268 @interfaceutil.implementer(repository.ipeercommands)
268 @interfaceutil.implementer(repository.ipeercommands)
269 class localpeer(repository.peer):
269 class localpeer(repository.peer):
270 '''peer for a local repo; reflects only the most recent API'''
270 '''peer for a local repo; reflects only the most recent API'''
271
271
272 def __init__(self, repo, caps=None):
272 def __init__(self, repo, caps=None):
273 super(localpeer, self).__init__()
273 super(localpeer, self).__init__()
274
274
275 if caps is None:
275 if caps is None:
276 caps = moderncaps.copy()
276 caps = moderncaps.copy()
277 self._repo = repo.filtered(b'served')
277 self._repo = repo.filtered(b'served')
278 self.ui = repo.ui
278 self.ui = repo.ui
279
279
280 if repo._wanted_sidedata:
280 if repo._wanted_sidedata:
281 formatted = bundle2.format_remote_wanted_sidedata(repo)
281 formatted = bundle2.format_remote_wanted_sidedata(repo)
282 caps.add(b'exp-wanted-sidedata=' + formatted)
282 caps.add(b'exp-wanted-sidedata=' + formatted)
283
283
284 self._caps = repo._restrictcapabilities(caps)
284 self._caps = repo._restrictcapabilities(caps)
285
285
286 # Begin of _basepeer interface.
286 # Begin of _basepeer interface.
287
287
288 def url(self):
288 def url(self):
289 return self._repo.url()
289 return self._repo.url()
290
290
291 def local(self):
291 def local(self):
292 return self._repo
292 return self._repo
293
293
294 def peer(self):
294 def peer(self):
295 return self
295 return self
296
296
297 def canpush(self):
297 def canpush(self):
298 return True
298 return True
299
299
300 def close(self):
300 def close(self):
301 self._repo.close()
301 self._repo.close()
302
302
303 # End of _basepeer interface.
303 # End of _basepeer interface.
304
304
305 # Begin of _basewirecommands interface.
305 # Begin of _basewirecommands interface.
306
306
307 def branchmap(self):
307 def branchmap(self):
308 return self._repo.branchmap()
308 return self._repo.branchmap()
309
309
310 def capabilities(self):
310 def capabilities(self):
311 return self._caps
311 return self._caps
312
312
313 def clonebundles(self):
313 def clonebundles(self):
314 return self._repo.tryread(bundlecaches.CB_MANIFEST_FILE)
314 return self._repo.tryread(bundlecaches.CB_MANIFEST_FILE)
315
315
316 def debugwireargs(self, one, two, three=None, four=None, five=None):
316 def debugwireargs(self, one, two, three=None, four=None, five=None):
317 """Used to test argument passing over the wire"""
317 """Used to test argument passing over the wire"""
318 return b"%s %s %s %s %s" % (
318 return b"%s %s %s %s %s" % (
319 one,
319 one,
320 two,
320 two,
321 pycompat.bytestr(three),
321 pycompat.bytestr(three),
322 pycompat.bytestr(four),
322 pycompat.bytestr(four),
323 pycompat.bytestr(five),
323 pycompat.bytestr(five),
324 )
324 )
325
325
326 def getbundle(
326 def getbundle(
327 self,
327 self,
328 source,
328 source,
329 heads=None,
329 heads=None,
330 common=None,
330 common=None,
331 bundlecaps=None,
331 bundlecaps=None,
332 remote_sidedata=None,
332 remote_sidedata=None,
333 **kwargs
333 **kwargs
334 ):
334 ):
335 chunks = exchange.getbundlechunks(
335 chunks = exchange.getbundlechunks(
336 self._repo,
336 self._repo,
337 source,
337 source,
338 heads=heads,
338 heads=heads,
339 common=common,
339 common=common,
340 bundlecaps=bundlecaps,
340 bundlecaps=bundlecaps,
341 remote_sidedata=remote_sidedata,
341 remote_sidedata=remote_sidedata,
342 **kwargs
342 **kwargs
343 )[1]
343 )[1]
344 cb = util.chunkbuffer(chunks)
344 cb = util.chunkbuffer(chunks)
345
345
346 if exchange.bundle2requested(bundlecaps):
346 if exchange.bundle2requested(bundlecaps):
347 # When requesting a bundle2, getbundle returns a stream to make the
347 # When requesting a bundle2, getbundle returns a stream to make the
348 # wire level function happier. We need to build a proper object
348 # wire level function happier. We need to build a proper object
349 # from it in local peer.
349 # from it in local peer.
350 return bundle2.getunbundler(self.ui, cb)
350 return bundle2.getunbundler(self.ui, cb)
351 else:
351 else:
352 return changegroup.getunbundler(b'01', cb, None)
352 return changegroup.getunbundler(b'01', cb, None)
353
353
354 def heads(self):
354 def heads(self):
355 return self._repo.heads()
355 return self._repo.heads()
356
356
357 def known(self, nodes):
357 def known(self, nodes):
358 return self._repo.known(nodes)
358 return self._repo.known(nodes)
359
359
360 def listkeys(self, namespace):
360 def listkeys(self, namespace):
361 return self._repo.listkeys(namespace)
361 return self._repo.listkeys(namespace)
362
362
363 def lookup(self, key):
363 def lookup(self, key):
364 return self._repo.lookup(key)
364 return self._repo.lookup(key)
365
365
366 def pushkey(self, namespace, key, old, new):
366 def pushkey(self, namespace, key, old, new):
367 return self._repo.pushkey(namespace, key, old, new)
367 return self._repo.pushkey(namespace, key, old, new)
368
368
369 def stream_out(self):
369 def stream_out(self):
370 raise error.Abort(_(b'cannot perform stream clone against local peer'))
370 raise error.Abort(_(b'cannot perform stream clone against local peer'))
371
371
372 def unbundle(self, bundle, heads, url):
372 def unbundle(self, bundle, heads, url):
373 """apply a bundle on a repo
373 """apply a bundle on a repo
374
374
375 This function handles the repo locking itself."""
375 This function handles the repo locking itself."""
376 try:
376 try:
377 try:
377 try:
378 bundle = exchange.readbundle(self.ui, bundle, None)
378 bundle = exchange.readbundle(self.ui, bundle, None)
379 ret = exchange.unbundle(self._repo, bundle, heads, b'push', url)
379 ret = exchange.unbundle(self._repo, bundle, heads, b'push', url)
380 if util.safehasattr(ret, b'getchunks'):
380 if util.safehasattr(ret, b'getchunks'):
381 # This is a bundle20 object, turn it into an unbundler.
381 # This is a bundle20 object, turn it into an unbundler.
382 # This little dance should be dropped eventually when the
382 # This little dance should be dropped eventually when the
383 # API is finally improved.
383 # API is finally improved.
384 stream = util.chunkbuffer(ret.getchunks())
384 stream = util.chunkbuffer(ret.getchunks())
385 ret = bundle2.getunbundler(self.ui, stream)
385 ret = bundle2.getunbundler(self.ui, stream)
386 return ret
386 return ret
387 except Exception as exc:
387 except Exception as exc:
388 # If the exception contains output salvaged from a bundle2
388 # If the exception contains output salvaged from a bundle2
389 # reply, we need to make sure it is printed before continuing
389 # reply, we need to make sure it is printed before continuing
390 # to fail. So we build a bundle2 with such output and consume
390 # to fail. So we build a bundle2 with such output and consume
391 # it directly.
391 # it directly.
392 #
392 #
393 # This is not very elegant but allows a "simple" solution for
393 # This is not very elegant but allows a "simple" solution for
394 # issue4594
394 # issue4594
395 output = getattr(exc, '_bundle2salvagedoutput', ())
395 output = getattr(exc, '_bundle2salvagedoutput', ())
396 if output:
396 if output:
397 bundler = bundle2.bundle20(self._repo.ui)
397 bundler = bundle2.bundle20(self._repo.ui)
398 for out in output:
398 for out in output:
399 bundler.addpart(out)
399 bundler.addpart(out)
400 stream = util.chunkbuffer(bundler.getchunks())
400 stream = util.chunkbuffer(bundler.getchunks())
401 b = bundle2.getunbundler(self.ui, stream)
401 b = bundle2.getunbundler(self.ui, stream)
402 bundle2.processbundle(self._repo, b)
402 bundle2.processbundle(self._repo, b)
403 raise
403 raise
404 except error.PushRaced as exc:
404 except error.PushRaced as exc:
405 raise error.ResponseError(
405 raise error.ResponseError(
406 _(b'push failed:'), stringutil.forcebytestr(exc)
406 _(b'push failed:'), stringutil.forcebytestr(exc)
407 )
407 )
408
408
409 # End of _basewirecommands interface.
409 # End of _basewirecommands interface.
410
410
411 # Begin of peer interface.
411 # Begin of peer interface.
412
412
413 def commandexecutor(self):
413 def commandexecutor(self):
414 return localcommandexecutor(self)
414 return localcommandexecutor(self)
415
415
416 # End of peer interface.
416 # End of peer interface.
417
417
418
418
419 @interfaceutil.implementer(repository.ipeerlegacycommands)
419 @interfaceutil.implementer(repository.ipeerlegacycommands)
420 class locallegacypeer(localpeer):
420 class locallegacypeer(localpeer):
421 """peer extension which implements legacy methods too; used for tests with
421 """peer extension which implements legacy methods too; used for tests with
422 restricted capabilities"""
422 restricted capabilities"""
423
423
424 def __init__(self, repo):
424 def __init__(self, repo):
425 super(locallegacypeer, self).__init__(repo, caps=legacycaps)
425 super(locallegacypeer, self).__init__(repo, caps=legacycaps)
426
426
427 # Begin of baselegacywirecommands interface.
427 # Begin of baselegacywirecommands interface.
428
428
429 def between(self, pairs):
429 def between(self, pairs):
430 return self._repo.between(pairs)
430 return self._repo.between(pairs)
431
431
432 def branches(self, nodes):
432 def branches(self, nodes):
433 return self._repo.branches(nodes)
433 return self._repo.branches(nodes)
434
434
435 def changegroup(self, nodes, source):
435 def changegroup(self, nodes, source):
436 outgoing = discovery.outgoing(
436 outgoing = discovery.outgoing(
437 self._repo, missingroots=nodes, ancestorsof=self._repo.heads()
437 self._repo, missingroots=nodes, ancestorsof=self._repo.heads()
438 )
438 )
439 return changegroup.makechangegroup(self._repo, outgoing, b'01', source)
439 return changegroup.makechangegroup(self._repo, outgoing, b'01', source)
440
440
441 def changegroupsubset(self, bases, heads, source):
441 def changegroupsubset(self, bases, heads, source):
442 outgoing = discovery.outgoing(
442 outgoing = discovery.outgoing(
443 self._repo, missingroots=bases, ancestorsof=heads
443 self._repo, missingroots=bases, ancestorsof=heads
444 )
444 )
445 return changegroup.makechangegroup(self._repo, outgoing, b'01', source)
445 return changegroup.makechangegroup(self._repo, outgoing, b'01', source)
446
446
447 # End of baselegacywirecommands interface.
447 # End of baselegacywirecommands interface.
448
448
449
449
450 # Functions receiving (ui, features) that extensions can register to impact
450 # Functions receiving (ui, features) that extensions can register to impact
451 # the ability to load repositories with custom requirements. Only
451 # the ability to load repositories with custom requirements. Only
452 # functions defined in loaded extensions are called.
452 # functions defined in loaded extensions are called.
453 #
453 #
454 # The function receives a set of requirement strings that the repository
454 # The function receives a set of requirement strings that the repository
455 # is capable of opening. Functions will typically add elements to the
455 # is capable of opening. Functions will typically add elements to the
456 # set to reflect that the extension knows how to handle that requirements.
456 # set to reflect that the extension knows how to handle that requirements.
457 featuresetupfuncs = set()
457 featuresetupfuncs = set()
458
458
459
459
460 def _getsharedvfs(hgvfs, requirements):
460 def _getsharedvfs(hgvfs, requirements):
461 """returns the vfs object pointing to root of shared source
461 """returns the vfs object pointing to root of shared source
462 repo for a shared repository
462 repo for a shared repository
463
463
464 hgvfs is vfs pointing at .hg/ of current repo (shared one)
464 hgvfs is vfs pointing at .hg/ of current repo (shared one)
465 requirements is a set of requirements of current repo (shared one)
465 requirements is a set of requirements of current repo (shared one)
466 """
466 """
467 # The ``shared`` or ``relshared`` requirements indicate the
467 # The ``shared`` or ``relshared`` requirements indicate the
468 # store lives in the path contained in the ``.hg/sharedpath`` file.
468 # store lives in the path contained in the ``.hg/sharedpath`` file.
469 # This is an absolute path for ``shared`` and relative to
469 # This is an absolute path for ``shared`` and relative to
470 # ``.hg/`` for ``relshared``.
470 # ``.hg/`` for ``relshared``.
471 sharedpath = hgvfs.read(b'sharedpath').rstrip(b'\n')
471 sharedpath = hgvfs.read(b'sharedpath').rstrip(b'\n')
472 if requirementsmod.RELATIVE_SHARED_REQUIREMENT in requirements:
472 if requirementsmod.RELATIVE_SHARED_REQUIREMENT in requirements:
473 sharedpath = util.normpath(hgvfs.join(sharedpath))
473 sharedpath = util.normpath(hgvfs.join(sharedpath))
474
474
475 sharedvfs = vfsmod.vfs(sharedpath, realpath=True)
475 sharedvfs = vfsmod.vfs(sharedpath, realpath=True)
476
476
477 if not sharedvfs.exists():
477 if not sharedvfs.exists():
478 raise error.RepoError(
478 raise error.RepoError(
479 _(b'.hg/sharedpath points to nonexistent directory %s')
479 _(b'.hg/sharedpath points to nonexistent directory %s')
480 % sharedvfs.base
480 % sharedvfs.base
481 )
481 )
482 return sharedvfs
482 return sharedvfs
483
483
484
484
485 def _readrequires(vfs, allowmissing):
485 def _readrequires(vfs, allowmissing):
486 """reads the require file present at root of this vfs
486 """reads the require file present at root of this vfs
487 and return a set of requirements
487 and return a set of requirements
488
488
489 If allowmissing is True, we suppress ENOENT if raised"""
489 If allowmissing is True, we suppress ENOENT if raised"""
490 # requires file contains a newline-delimited list of
490 # requires file contains a newline-delimited list of
491 # features/capabilities the opener (us) must have in order to use
491 # features/capabilities the opener (us) must have in order to use
492 # the repository. This file was introduced in Mercurial 0.9.2,
492 # the repository. This file was introduced in Mercurial 0.9.2,
493 # which means very old repositories may not have one. We assume
493 # which means very old repositories may not have one. We assume
494 # a missing file translates to no requirements.
494 # a missing file translates to no requirements.
495 try:
495 try:
496 requirements = set(vfs.read(b'requires').splitlines())
496 requirements = set(vfs.read(b'requires').splitlines())
497 except IOError as e:
497 except IOError as e:
498 if not (allowmissing and e.errno == errno.ENOENT):
498 if not (allowmissing and e.errno == errno.ENOENT):
499 raise
499 raise
500 requirements = set()
500 requirements = set()
501 return requirements
501 return requirements
502
502
503
503
504 def makelocalrepository(baseui, path, intents=None):
504 def makelocalrepository(baseui, path, intents=None):
505 """Create a local repository object.
505 """Create a local repository object.
506
506
507 Given arguments needed to construct a local repository, this function
507 Given arguments needed to construct a local repository, this function
508 performs various early repository loading functionality (such as
508 performs various early repository loading functionality (such as
509 reading the ``.hg/requires`` and ``.hg/hgrc`` files), validates that
509 reading the ``.hg/requires`` and ``.hg/hgrc`` files), validates that
510 the repository can be opened, derives a type suitable for representing
510 the repository can be opened, derives a type suitable for representing
511 that repository, and returns an instance of it.
511 that repository, and returns an instance of it.
512
512
513 The returned object conforms to the ``repository.completelocalrepository``
513 The returned object conforms to the ``repository.completelocalrepository``
514 interface.
514 interface.
515
515
516 The repository type is derived by calling a series of factory functions
516 The repository type is derived by calling a series of factory functions
517 for each aspect/interface of the final repository. These are defined by
517 for each aspect/interface of the final repository. These are defined by
518 ``REPO_INTERFACES``.
518 ``REPO_INTERFACES``.
519
519
520 Each factory function is called to produce a type implementing a specific
520 Each factory function is called to produce a type implementing a specific
521 interface. The cumulative list of returned types will be combined into a
521 interface. The cumulative list of returned types will be combined into a
522 new type and that type will be instantiated to represent the local
522 new type and that type will be instantiated to represent the local
523 repository.
523 repository.
524
524
525 The factory functions each receive various state that may be consulted
525 The factory functions each receive various state that may be consulted
526 as part of deriving a type.
526 as part of deriving a type.
527
527
528 Extensions should wrap these factory functions to customize repository type
528 Extensions should wrap these factory functions to customize repository type
529 creation. Note that an extension's wrapped function may be called even if
529 creation. Note that an extension's wrapped function may be called even if
530 that extension is not loaded for the repo being constructed. Extensions
530 that extension is not loaded for the repo being constructed. Extensions
531 should check if their ``__name__`` appears in the
531 should check if their ``__name__`` appears in the
532 ``extensionmodulenames`` set passed to the factory function and no-op if
532 ``extensionmodulenames`` set passed to the factory function and no-op if
533 not.
533 not.
534 """
534 """
535 ui = baseui.copy()
535 ui = baseui.copy()
536 # Prevent copying repo configuration.
536 # Prevent copying repo configuration.
537 ui.copy = baseui.copy
537 ui.copy = baseui.copy
538
538
539 # Working directory VFS rooted at repository root.
539 # Working directory VFS rooted at repository root.
540 wdirvfs = vfsmod.vfs(path, expandpath=True, realpath=True)
540 wdirvfs = vfsmod.vfs(path, expandpath=True, realpath=True)
541
541
542 # Main VFS for .hg/ directory.
542 # Main VFS for .hg/ directory.
543 hgpath = wdirvfs.join(b'.hg')
543 hgpath = wdirvfs.join(b'.hg')
544 hgvfs = vfsmod.vfs(hgpath, cacheaudited=True)
544 hgvfs = vfsmod.vfs(hgpath, cacheaudited=True)
545 # Whether this repository is shared one or not
545 # Whether this repository is shared one or not
546 shared = False
546 shared = False
547 # If this repository is shared, vfs pointing to shared repo
547 # If this repository is shared, vfs pointing to shared repo
548 sharedvfs = None
548 sharedvfs = None
549
549
550 # The .hg/ path should exist and should be a directory. All other
550 # The .hg/ path should exist and should be a directory. All other
551 # cases are errors.
551 # cases are errors.
552 if not hgvfs.isdir():
552 if not hgvfs.isdir():
553 try:
553 try:
554 hgvfs.stat()
554 hgvfs.stat()
555 except OSError as e:
555 except OSError as e:
556 if e.errno != errno.ENOENT:
556 if e.errno != errno.ENOENT:
557 raise
557 raise
558 except ValueError as e:
558 except ValueError as e:
559 # Can be raised on Python 3.8 when path is invalid.
559 # Can be raised on Python 3.8 when path is invalid.
560 raise error.Abort(
560 raise error.Abort(
561 _(b'invalid path %s: %s') % (path, stringutil.forcebytestr(e))
561 _(b'invalid path %s: %s') % (path, stringutil.forcebytestr(e))
562 )
562 )
563
563
564 raise error.RepoError(_(b'repository %s not found') % path)
564 raise error.RepoError(_(b'repository %s not found') % path)
565
565
566 requirements = _readrequires(hgvfs, True)
566 requirements = _readrequires(hgvfs, True)
567 shared = (
567 shared = (
568 requirementsmod.SHARED_REQUIREMENT in requirements
568 requirementsmod.SHARED_REQUIREMENT in requirements
569 or requirementsmod.RELATIVE_SHARED_REQUIREMENT in requirements
569 or requirementsmod.RELATIVE_SHARED_REQUIREMENT in requirements
570 )
570 )
571 storevfs = None
571 storevfs = None
572 if shared:
572 if shared:
573 # This is a shared repo
573 # This is a shared repo
574 sharedvfs = _getsharedvfs(hgvfs, requirements)
574 sharedvfs = _getsharedvfs(hgvfs, requirements)
575 storevfs = vfsmod.vfs(sharedvfs.join(b'store'))
575 storevfs = vfsmod.vfs(sharedvfs.join(b'store'))
576 else:
576 else:
577 storevfs = vfsmod.vfs(hgvfs.join(b'store'))
577 storevfs = vfsmod.vfs(hgvfs.join(b'store'))
578
578
579 # if .hg/requires contains the sharesafe requirement, it means
579 # if .hg/requires contains the sharesafe requirement, it means
580 # there exists a `.hg/store/requires` too and we should read it
580 # there exists a `.hg/store/requires` too and we should read it
581 # NOTE: presence of SHARESAFE_REQUIREMENT imply that store requirement
581 # NOTE: presence of SHARESAFE_REQUIREMENT imply that store requirement
582 # is present. We never write SHARESAFE_REQUIREMENT for a repo if store
582 # is present. We never write SHARESAFE_REQUIREMENT for a repo if store
583 # is not present, refer checkrequirementscompat() for that
583 # is not present, refer checkrequirementscompat() for that
584 #
584 #
585 # However, if SHARESAFE_REQUIREMENT is not present, it means that the
585 # However, if SHARESAFE_REQUIREMENT is not present, it means that the
586 # repository was shared the old way. We check the share source .hg/requires
586 # repository was shared the old way. We check the share source .hg/requires
587 # for SHARESAFE_REQUIREMENT to detect whether the current repository needs
587 # for SHARESAFE_REQUIREMENT to detect whether the current repository needs
588 # to be reshared
588 # to be reshared
589 hint = _(b"see `hg help config.format.use-share-safe` for more information")
589 hint = _(b"see `hg help config.format.use-share-safe` for more information")
590 if requirementsmod.SHARESAFE_REQUIREMENT in requirements:
590 if requirementsmod.SHARESAFE_REQUIREMENT in requirements:
591
591
592 if (
592 if (
593 shared
593 shared
594 and requirementsmod.SHARESAFE_REQUIREMENT
594 and requirementsmod.SHARESAFE_REQUIREMENT
595 not in _readrequires(sharedvfs, True)
595 not in _readrequires(sharedvfs, True)
596 ):
596 ):
597 mismatch_warn = ui.configbool(
597 mismatch_warn = ui.configbool(
598 b'share', b'safe-mismatch.source-not-safe.warn'
598 b'share', b'safe-mismatch.source-not-safe.warn'
599 )
599 )
600 mismatch_config = ui.config(
600 mismatch_config = ui.config(
601 b'share', b'safe-mismatch.source-not-safe'
601 b'share', b'safe-mismatch.source-not-safe'
602 )
602 )
603 if mismatch_config in (
603 if mismatch_config in (
604 b'downgrade-allow',
604 b'downgrade-allow',
605 b'allow',
605 b'allow',
606 b'downgrade-abort',
606 b'downgrade-abort',
607 ):
607 ):
608 # prevent cyclic import localrepo -> upgrade -> localrepo
608 # prevent cyclic import localrepo -> upgrade -> localrepo
609 from . import upgrade
609 from . import upgrade
610
610
611 upgrade.downgrade_share_to_non_safe(
611 upgrade.downgrade_share_to_non_safe(
612 ui,
612 ui,
613 hgvfs,
613 hgvfs,
614 sharedvfs,
614 sharedvfs,
615 requirements,
615 requirements,
616 mismatch_config,
616 mismatch_config,
617 mismatch_warn,
617 mismatch_warn,
618 )
618 )
619 elif mismatch_config == b'abort':
619 elif mismatch_config == b'abort':
620 raise error.Abort(
620 raise error.Abort(
621 _(b"share source does not support share-safe requirement"),
621 _(b"share source does not support share-safe requirement"),
622 hint=hint,
622 hint=hint,
623 )
623 )
624 else:
624 else:
625 raise error.Abort(
625 raise error.Abort(
626 _(
626 _(
627 b"share-safe mismatch with source.\nUnrecognized"
627 b"share-safe mismatch with source.\nUnrecognized"
628 b" value '%s' of `share.safe-mismatch.source-not-safe`"
628 b" value '%s' of `share.safe-mismatch.source-not-safe`"
629 b" set."
629 b" set."
630 )
630 )
631 % mismatch_config,
631 % mismatch_config,
632 hint=hint,
632 hint=hint,
633 )
633 )
634 else:
634 else:
635 requirements |= _readrequires(storevfs, False)
635 requirements |= _readrequires(storevfs, False)
636 elif shared:
636 elif shared:
637 sourcerequires = _readrequires(sharedvfs, False)
637 sourcerequires = _readrequires(sharedvfs, False)
638 if requirementsmod.SHARESAFE_REQUIREMENT in sourcerequires:
638 if requirementsmod.SHARESAFE_REQUIREMENT in sourcerequires:
639 mismatch_config = ui.config(b'share', b'safe-mismatch.source-safe')
639 mismatch_config = ui.config(b'share', b'safe-mismatch.source-safe')
640 mismatch_warn = ui.configbool(
640 mismatch_warn = ui.configbool(
641 b'share', b'safe-mismatch.source-safe.warn'
641 b'share', b'safe-mismatch.source-safe.warn'
642 )
642 )
643 if mismatch_config in (
643 if mismatch_config in (
644 b'upgrade-allow',
644 b'upgrade-allow',
645 b'allow',
645 b'allow',
646 b'upgrade-abort',
646 b'upgrade-abort',
647 ):
647 ):
648 # prevent cyclic import localrepo -> upgrade -> localrepo
648 # prevent cyclic import localrepo -> upgrade -> localrepo
649 from . import upgrade
649 from . import upgrade
650
650
651 upgrade.upgrade_share_to_safe(
651 upgrade.upgrade_share_to_safe(
652 ui,
652 ui,
653 hgvfs,
653 hgvfs,
654 storevfs,
654 storevfs,
655 requirements,
655 requirements,
656 mismatch_config,
656 mismatch_config,
657 mismatch_warn,
657 mismatch_warn,
658 )
658 )
659 elif mismatch_config == b'abort':
659 elif mismatch_config == b'abort':
660 raise error.Abort(
660 raise error.Abort(
661 _(
661 _(
662 b'version mismatch: source uses share-safe'
662 b'version mismatch: source uses share-safe'
663 b' functionality while the current share does not'
663 b' functionality while the current share does not'
664 ),
664 ),
665 hint=hint,
665 hint=hint,
666 )
666 )
667 else:
667 else:
668 raise error.Abort(
668 raise error.Abort(
669 _(
669 _(
670 b"share-safe mismatch with source.\nUnrecognized"
670 b"share-safe mismatch with source.\nUnrecognized"
671 b" value '%s' of `share.safe-mismatch.source-safe` set."
671 b" value '%s' of `share.safe-mismatch.source-safe` set."
672 )
672 )
673 % mismatch_config,
673 % mismatch_config,
674 hint=hint,
674 hint=hint,
675 )
675 )
676
676
677 # The .hg/hgrc file may load extensions or contain config options
677 # The .hg/hgrc file may load extensions or contain config options
678 # that influence repository construction. Attempt to load it and
678 # that influence repository construction. Attempt to load it and
679 # process any new extensions that it may have pulled in.
679 # process any new extensions that it may have pulled in.
680 if loadhgrc(ui, wdirvfs, hgvfs, requirements, sharedvfs):
680 if loadhgrc(ui, wdirvfs, hgvfs, requirements, sharedvfs):
681 afterhgrcload(ui, wdirvfs, hgvfs, requirements)
681 afterhgrcload(ui, wdirvfs, hgvfs, requirements)
682 extensions.loadall(ui)
682 extensions.loadall(ui)
683 extensions.populateui(ui)
683 extensions.populateui(ui)
684
684
685 # Set of module names of extensions loaded for this repository.
685 # Set of module names of extensions loaded for this repository.
686 extensionmodulenames = {m.__name__ for n, m in extensions.extensions(ui)}
686 extensionmodulenames = {m.__name__ for n, m in extensions.extensions(ui)}
687
687
688 supportedrequirements = gathersupportedrequirements(ui)
688 supportedrequirements = gathersupportedrequirements(ui)
689
689
690 # We first validate the requirements are known.
690 # We first validate the requirements are known.
691 ensurerequirementsrecognized(requirements, supportedrequirements)
691 ensurerequirementsrecognized(requirements, supportedrequirements)
692
692
693 # Then we validate that the known set is reasonable to use together.
693 # Then we validate that the known set is reasonable to use together.
694 ensurerequirementscompatible(ui, requirements)
694 ensurerequirementscompatible(ui, requirements)
695
695
696 # TODO there are unhandled edge cases related to opening repositories with
696 # TODO there are unhandled edge cases related to opening repositories with
697 # shared storage. If storage is shared, we should also test for requirements
697 # shared storage. If storage is shared, we should also test for requirements
698 # compatibility in the pointed-to repo. This entails loading the .hg/hgrc in
698 # compatibility in the pointed-to repo. This entails loading the .hg/hgrc in
699 # that repo, as that repo may load extensions needed to open it. This is a
699 # that repo, as that repo may load extensions needed to open it. This is a
700 # bit complicated because we don't want the other hgrc to overwrite settings
700 # bit complicated because we don't want the other hgrc to overwrite settings
701 # in this hgrc.
701 # in this hgrc.
702 #
702 #
703 # This bug is somewhat mitigated by the fact that we copy the .hg/requires
703 # This bug is somewhat mitigated by the fact that we copy the .hg/requires
704 # file when sharing repos. But if a requirement is added after the share is
704 # file when sharing repos. But if a requirement is added after the share is
705 # performed, thereby introducing a new requirement for the opener, we may
705 # performed, thereby introducing a new requirement for the opener, we may
706 # will not see that and could encounter a run-time error interacting with
706 # will not see that and could encounter a run-time error interacting with
707 # that shared store since it has an unknown-to-us requirement.
707 # that shared store since it has an unknown-to-us requirement.
708
708
709 # At this point, we know we should be capable of opening the repository.
709 # At this point, we know we should be capable of opening the repository.
710 # Now get on with doing that.
710 # Now get on with doing that.
711
711
712 features = set()
712 features = set()
713
713
714 # The "store" part of the repository holds versioned data. How it is
714 # The "store" part of the repository holds versioned data. How it is
715 # accessed is determined by various requirements. If `shared` or
715 # accessed is determined by various requirements. If `shared` or
716 # `relshared` requirements are present, this indicates current repository
716 # `relshared` requirements are present, this indicates current repository
717 # is a share and store exists in path mentioned in `.hg/sharedpath`
717 # is a share and store exists in path mentioned in `.hg/sharedpath`
718 if shared:
718 if shared:
719 storebasepath = sharedvfs.base
719 storebasepath = sharedvfs.base
720 cachepath = sharedvfs.join(b'cache')
720 cachepath = sharedvfs.join(b'cache')
721 features.add(repository.REPO_FEATURE_SHARED_STORAGE)
721 features.add(repository.REPO_FEATURE_SHARED_STORAGE)
722 else:
722 else:
723 storebasepath = hgvfs.base
723 storebasepath = hgvfs.base
724 cachepath = hgvfs.join(b'cache')
724 cachepath = hgvfs.join(b'cache')
725 wcachepath = hgvfs.join(b'wcache')
725 wcachepath = hgvfs.join(b'wcache')
726
726
727 # The store has changed over time and the exact layout is dictated by
727 # The store has changed over time and the exact layout is dictated by
728 # requirements. The store interface abstracts differences across all
728 # requirements. The store interface abstracts differences across all
729 # of them.
729 # of them.
730 store = makestore(
730 store = makestore(
731 requirements,
731 requirements,
732 storebasepath,
732 storebasepath,
733 lambda base: vfsmod.vfs(base, cacheaudited=True),
733 lambda base: vfsmod.vfs(base, cacheaudited=True),
734 )
734 )
735 hgvfs.createmode = store.createmode
735 hgvfs.createmode = store.createmode
736
736
737 storevfs = store.vfs
737 storevfs = store.vfs
738 storevfs.options = resolvestorevfsoptions(ui, requirements, features)
738 storevfs.options = resolvestorevfsoptions(ui, requirements, features)
739
739
740 if requirementsmod.REVLOGV2_REQUIREMENT in requirements:
740 if requirementsmod.REVLOGV2_REQUIREMENT in requirements:
741 features.add(repository.REPO_FEATURE_SIDE_DATA)
741 features.add(repository.REPO_FEATURE_SIDE_DATA)
742
742
743 # The cache vfs is used to manage cache files.
743 # The cache vfs is used to manage cache files.
744 cachevfs = vfsmod.vfs(cachepath, cacheaudited=True)
744 cachevfs = vfsmod.vfs(cachepath, cacheaudited=True)
745 cachevfs.createmode = store.createmode
745 cachevfs.createmode = store.createmode
746 # The cache vfs is used to manage cache files related to the working copy
746 # The cache vfs is used to manage cache files related to the working copy
747 wcachevfs = vfsmod.vfs(wcachepath, cacheaudited=True)
747 wcachevfs = vfsmod.vfs(wcachepath, cacheaudited=True)
748 wcachevfs.createmode = store.createmode
748 wcachevfs.createmode = store.createmode
749
749
750 # Now resolve the type for the repository object. We do this by repeatedly
750 # Now resolve the type for the repository object. We do this by repeatedly
751 # calling a factory function to produces types for specific aspects of the
751 # calling a factory function to produces types for specific aspects of the
752 # repo's operation. The aggregate returned types are used as base classes
752 # repo's operation. The aggregate returned types are used as base classes
753 # for a dynamically-derived type, which will represent our new repository.
753 # for a dynamically-derived type, which will represent our new repository.
754
754
755 bases = []
755 bases = []
756 extrastate = {}
756 extrastate = {}
757
757
758 for iface, fn in REPO_INTERFACES:
758 for iface, fn in REPO_INTERFACES:
759 # We pass all potentially useful state to give extensions tons of
759 # We pass all potentially useful state to give extensions tons of
760 # flexibility.
760 # flexibility.
761 typ = fn()(
761 typ = fn()(
762 ui=ui,
762 ui=ui,
763 intents=intents,
763 intents=intents,
764 requirements=requirements,
764 requirements=requirements,
765 features=features,
765 features=features,
766 wdirvfs=wdirvfs,
766 wdirvfs=wdirvfs,
767 hgvfs=hgvfs,
767 hgvfs=hgvfs,
768 store=store,
768 store=store,
769 storevfs=storevfs,
769 storevfs=storevfs,
770 storeoptions=storevfs.options,
770 storeoptions=storevfs.options,
771 cachevfs=cachevfs,
771 cachevfs=cachevfs,
772 wcachevfs=wcachevfs,
772 wcachevfs=wcachevfs,
773 extensionmodulenames=extensionmodulenames,
773 extensionmodulenames=extensionmodulenames,
774 extrastate=extrastate,
774 extrastate=extrastate,
775 baseclasses=bases,
775 baseclasses=bases,
776 )
776 )
777
777
778 if not isinstance(typ, type):
778 if not isinstance(typ, type):
779 raise error.ProgrammingError(
779 raise error.ProgrammingError(
780 b'unable to construct type for %s' % iface
780 b'unable to construct type for %s' % iface
781 )
781 )
782
782
783 bases.append(typ)
783 bases.append(typ)
784
784
785 # type() allows you to use characters in type names that wouldn't be
785 # type() allows you to use characters in type names that wouldn't be
786 # recognized as Python symbols in source code. We abuse that to add
786 # recognized as Python symbols in source code. We abuse that to add
787 # rich information about our constructed repo.
787 # rich information about our constructed repo.
788 name = pycompat.sysstr(
788 name = pycompat.sysstr(
789 b'derivedrepo:%s<%s>' % (wdirvfs.base, b','.join(sorted(requirements)))
789 b'derivedrepo:%s<%s>' % (wdirvfs.base, b','.join(sorted(requirements)))
790 )
790 )
791
791
792 cls = type(name, tuple(bases), {})
792 cls = type(name, tuple(bases), {})
793
793
794 return cls(
794 return cls(
795 baseui=baseui,
795 baseui=baseui,
796 ui=ui,
796 ui=ui,
797 origroot=path,
797 origroot=path,
798 wdirvfs=wdirvfs,
798 wdirvfs=wdirvfs,
799 hgvfs=hgvfs,
799 hgvfs=hgvfs,
800 requirements=requirements,
800 requirements=requirements,
801 supportedrequirements=supportedrequirements,
801 supportedrequirements=supportedrequirements,
802 sharedpath=storebasepath,
802 sharedpath=storebasepath,
803 store=store,
803 store=store,
804 cachevfs=cachevfs,
804 cachevfs=cachevfs,
805 wcachevfs=wcachevfs,
805 wcachevfs=wcachevfs,
806 features=features,
806 features=features,
807 intents=intents,
807 intents=intents,
808 )
808 )
809
809
810
810
811 def loadhgrc(ui, wdirvfs, hgvfs, requirements, sharedvfs=None):
811 def loadhgrc(ui, wdirvfs, hgvfs, requirements, sharedvfs=None):
812 """Load hgrc files/content into a ui instance.
812 """Load hgrc files/content into a ui instance.
813
813
814 This is called during repository opening to load any additional
814 This is called during repository opening to load any additional
815 config files or settings relevant to the current repository.
815 config files or settings relevant to the current repository.
816
816
817 Returns a bool indicating whether any additional configs were loaded.
817 Returns a bool indicating whether any additional configs were loaded.
818
818
819 Extensions should monkeypatch this function to modify how per-repo
819 Extensions should monkeypatch this function to modify how per-repo
820 configs are loaded. For example, an extension may wish to pull in
820 configs are loaded. For example, an extension may wish to pull in
821 configs from alternate files or sources.
821 configs from alternate files or sources.
822
822
823 sharedvfs is vfs object pointing to source repo if the current one is a
823 sharedvfs is vfs object pointing to source repo if the current one is a
824 shared one
824 shared one
825 """
825 """
826 if not rcutil.use_repo_hgrc():
826 if not rcutil.use_repo_hgrc():
827 return False
827 return False
828
828
829 ret = False
829 ret = False
830 # first load config from shared source if we has to
830 # first load config from shared source if we has to
831 if requirementsmod.SHARESAFE_REQUIREMENT in requirements and sharedvfs:
831 if requirementsmod.SHARESAFE_REQUIREMENT in requirements and sharedvfs:
832 try:
832 try:
833 ui.readconfig(sharedvfs.join(b'hgrc'), root=sharedvfs.base)
833 ui.readconfig(sharedvfs.join(b'hgrc'), root=sharedvfs.base)
834 ret = True
834 ret = True
835 except IOError:
835 except IOError:
836 pass
836 pass
837
837
838 try:
838 try:
839 ui.readconfig(hgvfs.join(b'hgrc'), root=wdirvfs.base)
839 ui.readconfig(hgvfs.join(b'hgrc'), root=wdirvfs.base)
840 ret = True
840 ret = True
841 except IOError:
841 except IOError:
842 pass
842 pass
843
843
844 try:
844 try:
845 ui.readconfig(hgvfs.join(b'hgrc-not-shared'), root=wdirvfs.base)
845 ui.readconfig(hgvfs.join(b'hgrc-not-shared'), root=wdirvfs.base)
846 ret = True
846 ret = True
847 except IOError:
847 except IOError:
848 pass
848 pass
849
849
850 return ret
850 return ret
851
851
852
852
853 def afterhgrcload(ui, wdirvfs, hgvfs, requirements):
853 def afterhgrcload(ui, wdirvfs, hgvfs, requirements):
854 """Perform additional actions after .hg/hgrc is loaded.
854 """Perform additional actions after .hg/hgrc is loaded.
855
855
856 This function is called during repository loading immediately after
856 This function is called during repository loading immediately after
857 the .hg/hgrc file is loaded and before per-repo extensions are loaded.
857 the .hg/hgrc file is loaded and before per-repo extensions are loaded.
858
858
859 The function can be used to validate configs, automatically add
859 The function can be used to validate configs, automatically add
860 options (including extensions) based on requirements, etc.
860 options (including extensions) based on requirements, etc.
861 """
861 """
862
862
863 # Map of requirements to list of extensions to load automatically when
863 # Map of requirements to list of extensions to load automatically when
864 # requirement is present.
864 # requirement is present.
865 autoextensions = {
865 autoextensions = {
866 b'git': [b'git'],
866 b'git': [b'git'],
867 b'largefiles': [b'largefiles'],
867 b'largefiles': [b'largefiles'],
868 b'lfs': [b'lfs'],
868 b'lfs': [b'lfs'],
869 }
869 }
870
870
871 for requirement, names in sorted(autoextensions.items()):
871 for requirement, names in sorted(autoextensions.items()):
872 if requirement not in requirements:
872 if requirement not in requirements:
873 continue
873 continue
874
874
875 for name in names:
875 for name in names:
876 if not ui.hasconfig(b'extensions', name):
876 if not ui.hasconfig(b'extensions', name):
877 ui.setconfig(b'extensions', name, b'', source=b'autoload')
877 ui.setconfig(b'extensions', name, b'', source=b'autoload')
878
878
879
879
880 def gathersupportedrequirements(ui):
880 def gathersupportedrequirements(ui):
881 """Determine the complete set of recognized requirements."""
881 """Determine the complete set of recognized requirements."""
882 # Start with all requirements supported by this file.
882 # Start with all requirements supported by this file.
883 supported = set(localrepository._basesupported)
883 supported = set(localrepository._basesupported)
884
884
885 # Execute ``featuresetupfuncs`` entries if they belong to an extension
885 # Execute ``featuresetupfuncs`` entries if they belong to an extension
886 # relevant to this ui instance.
886 # relevant to this ui instance.
887 modules = {m.__name__ for n, m in extensions.extensions(ui)}
887 modules = {m.__name__ for n, m in extensions.extensions(ui)}
888
888
889 for fn in featuresetupfuncs:
889 for fn in featuresetupfuncs:
890 if fn.__module__ in modules:
890 if fn.__module__ in modules:
891 fn(ui, supported)
891 fn(ui, supported)
892
892
893 # Add derived requirements from registered compression engines.
893 # Add derived requirements from registered compression engines.
894 for name in util.compengines:
894 for name in util.compengines:
895 engine = util.compengines[name]
895 engine = util.compengines[name]
896 if engine.available() and engine.revlogheader():
896 if engine.available() and engine.revlogheader():
897 supported.add(b'exp-compression-%s' % name)
897 supported.add(b'exp-compression-%s' % name)
898 if engine.name() == b'zstd':
898 if engine.name() == b'zstd':
899 supported.add(b'revlog-compression-zstd')
899 supported.add(b'revlog-compression-zstd')
900
900
901 return supported
901 return supported
902
902
903
903
904 def ensurerequirementsrecognized(requirements, supported):
904 def ensurerequirementsrecognized(requirements, supported):
905 """Validate that a set of local requirements is recognized.
905 """Validate that a set of local requirements is recognized.
906
906
907 Receives a set of requirements. Raises an ``error.RepoError`` if there
907 Receives a set of requirements. Raises an ``error.RepoError`` if there
908 exists any requirement in that set that currently loaded code doesn't
908 exists any requirement in that set that currently loaded code doesn't
909 recognize.
909 recognize.
910
910
911 Returns a set of supported requirements.
911 Returns a set of supported requirements.
912 """
912 """
913 missing = set()
913 missing = set()
914
914
915 for requirement in requirements:
915 for requirement in requirements:
916 if requirement in supported:
916 if requirement in supported:
917 continue
917 continue
918
918
919 if not requirement or not requirement[0:1].isalnum():
919 if not requirement or not requirement[0:1].isalnum():
920 raise error.RequirementError(_(b'.hg/requires file is corrupt'))
920 raise error.RequirementError(_(b'.hg/requires file is corrupt'))
921
921
922 missing.add(requirement)
922 missing.add(requirement)
923
923
924 if missing:
924 if missing:
925 raise error.RequirementError(
925 raise error.RequirementError(
926 _(b'repository requires features unknown to this Mercurial: %s')
926 _(b'repository requires features unknown to this Mercurial: %s')
927 % b' '.join(sorted(missing)),
927 % b' '.join(sorted(missing)),
928 hint=_(
928 hint=_(
929 b'see https://mercurial-scm.org/wiki/MissingRequirement '
929 b'see https://mercurial-scm.org/wiki/MissingRequirement '
930 b'for more information'
930 b'for more information'
931 ),
931 ),
932 )
932 )
933
933
934
934
935 def ensurerequirementscompatible(ui, requirements):
935 def ensurerequirementscompatible(ui, requirements):
936 """Validates that a set of recognized requirements is mutually compatible.
936 """Validates that a set of recognized requirements is mutually compatible.
937
937
938 Some requirements may not be compatible with others or require
938 Some requirements may not be compatible with others or require
939 config options that aren't enabled. This function is called during
939 config options that aren't enabled. This function is called during
940 repository opening to ensure that the set of requirements needed
940 repository opening to ensure that the set of requirements needed
941 to open a repository is sane and compatible with config options.
941 to open a repository is sane and compatible with config options.
942
942
943 Extensions can monkeypatch this function to perform additional
943 Extensions can monkeypatch this function to perform additional
944 checking.
944 checking.
945
945
946 ``error.RepoError`` should be raised on failure.
946 ``error.RepoError`` should be raised on failure.
947 """
947 """
948 if (
948 if (
949 requirementsmod.SPARSE_REQUIREMENT in requirements
949 requirementsmod.SPARSE_REQUIREMENT in requirements
950 and not sparse.enabled
950 and not sparse.enabled
951 ):
951 ):
952 raise error.RepoError(
952 raise error.RepoError(
953 _(
953 _(
954 b'repository is using sparse feature but '
954 b'repository is using sparse feature but '
955 b'sparse is not enabled; enable the '
955 b'sparse is not enabled; enable the '
956 b'"sparse" extensions to access'
956 b'"sparse" extensions to access'
957 )
957 )
958 )
958 )
959
959
960
960
961 def makestore(requirements, path, vfstype):
961 def makestore(requirements, path, vfstype):
962 """Construct a storage object for a repository."""
962 """Construct a storage object for a repository."""
963 if requirementsmod.STORE_REQUIREMENT in requirements:
963 if requirementsmod.STORE_REQUIREMENT in requirements:
964 if requirementsmod.FNCACHE_REQUIREMENT in requirements:
964 if requirementsmod.FNCACHE_REQUIREMENT in requirements:
965 dotencode = requirementsmod.DOTENCODE_REQUIREMENT in requirements
965 dotencode = requirementsmod.DOTENCODE_REQUIREMENT in requirements
966 return storemod.fncachestore(path, vfstype, dotencode)
966 return storemod.fncachestore(path, vfstype, dotencode)
967
967
968 return storemod.encodedstore(path, vfstype)
968 return storemod.encodedstore(path, vfstype)
969
969
970 return storemod.basicstore(path, vfstype)
970 return storemod.basicstore(path, vfstype)
971
971
972
972
973 def resolvestorevfsoptions(ui, requirements, features):
973 def resolvestorevfsoptions(ui, requirements, features):
974 """Resolve the options to pass to the store vfs opener.
974 """Resolve the options to pass to the store vfs opener.
975
975
976 The returned dict is used to influence behavior of the storage layer.
976 The returned dict is used to influence behavior of the storage layer.
977 """
977 """
978 options = {}
978 options = {}
979
979
980 if requirementsmod.TREEMANIFEST_REQUIREMENT in requirements:
980 if requirementsmod.TREEMANIFEST_REQUIREMENT in requirements:
981 options[b'treemanifest'] = True
981 options[b'treemanifest'] = True
982
982
983 # experimental config: format.manifestcachesize
983 # experimental config: format.manifestcachesize
984 manifestcachesize = ui.configint(b'format', b'manifestcachesize')
984 manifestcachesize = ui.configint(b'format', b'manifestcachesize')
985 if manifestcachesize is not None:
985 if manifestcachesize is not None:
986 options[b'manifestcachesize'] = manifestcachesize
986 options[b'manifestcachesize'] = manifestcachesize
987
987
988 # In the absence of another requirement superseding a revlog-related
988 # In the absence of another requirement superseding a revlog-related
989 # requirement, we have to assume the repo is using revlog version 0.
989 # requirement, we have to assume the repo is using revlog version 0.
990 # This revlog format is super old and we don't bother trying to parse
990 # This revlog format is super old and we don't bother trying to parse
991 # opener options for it because those options wouldn't do anything
991 # opener options for it because those options wouldn't do anything
992 # meaningful on such old repos.
992 # meaningful on such old repos.
993 if (
993 if (
994 requirementsmod.REVLOGV1_REQUIREMENT in requirements
994 requirementsmod.REVLOGV1_REQUIREMENT in requirements
995 or requirementsmod.REVLOGV2_REQUIREMENT in requirements
995 or requirementsmod.REVLOGV2_REQUIREMENT in requirements
996 ):
996 ):
997 options.update(resolverevlogstorevfsoptions(ui, requirements, features))
997 options.update(resolverevlogstorevfsoptions(ui, requirements, features))
998 else: # explicitly mark repo as using revlogv0
998 else: # explicitly mark repo as using revlogv0
999 options[b'revlogv0'] = True
999 options[b'revlogv0'] = True
1000
1000
1001 if requirementsmod.COPIESSDC_REQUIREMENT in requirements:
1001 if requirementsmod.COPIESSDC_REQUIREMENT in requirements:
1002 options[b'copies-storage'] = b'changeset-sidedata'
1002 options[b'copies-storage'] = b'changeset-sidedata'
1003 else:
1003 else:
1004 writecopiesto = ui.config(b'experimental', b'copies.write-to')
1004 writecopiesto = ui.config(b'experimental', b'copies.write-to')
1005 copiesextramode = (b'changeset-only', b'compatibility')
1005 copiesextramode = (b'changeset-only', b'compatibility')
1006 if writecopiesto in copiesextramode:
1006 if writecopiesto in copiesextramode:
1007 options[b'copies-storage'] = b'extra'
1007 options[b'copies-storage'] = b'extra'
1008
1008
1009 return options
1009 return options
1010
1010
1011
1011
1012 def resolverevlogstorevfsoptions(ui, requirements, features):
1012 def resolverevlogstorevfsoptions(ui, requirements, features):
1013 """Resolve opener options specific to revlogs."""
1013 """Resolve opener options specific to revlogs."""
1014
1014
1015 options = {}
1015 options = {}
1016 options[b'flagprocessors'] = {}
1016 options[b'flagprocessors'] = {}
1017
1017
1018 if requirementsmod.REVLOGV1_REQUIREMENT in requirements:
1018 if requirementsmod.REVLOGV1_REQUIREMENT in requirements:
1019 options[b'revlogv1'] = True
1019 options[b'revlogv1'] = True
1020 if requirementsmod.REVLOGV2_REQUIREMENT in requirements:
1020 if requirementsmod.REVLOGV2_REQUIREMENT in requirements:
1021 options[b'revlogv2'] = True
1021 options[b'revlogv2'] = True
1022
1022
1023 if requirementsmod.GENERALDELTA_REQUIREMENT in requirements:
1023 if requirementsmod.GENERALDELTA_REQUIREMENT in requirements:
1024 options[b'generaldelta'] = True
1024 options[b'generaldelta'] = True
1025
1025
1026 # experimental config: format.chunkcachesize
1026 # experimental config: format.chunkcachesize
1027 chunkcachesize = ui.configint(b'format', b'chunkcachesize')
1027 chunkcachesize = ui.configint(b'format', b'chunkcachesize')
1028 if chunkcachesize is not None:
1028 if chunkcachesize is not None:
1029 options[b'chunkcachesize'] = chunkcachesize
1029 options[b'chunkcachesize'] = chunkcachesize
1030
1030
1031 deltabothparents = ui.configbool(
1031 deltabothparents = ui.configbool(
1032 b'storage', b'revlog.optimize-delta-parent-choice'
1032 b'storage', b'revlog.optimize-delta-parent-choice'
1033 )
1033 )
1034 options[b'deltabothparents'] = deltabothparents
1034 options[b'deltabothparents'] = deltabothparents
1035
1035
1036 lazydelta = ui.configbool(b'storage', b'revlog.reuse-external-delta')
1036 lazydelta = ui.configbool(b'storage', b'revlog.reuse-external-delta')
1037 lazydeltabase = False
1037 lazydeltabase = False
1038 if lazydelta:
1038 if lazydelta:
1039 lazydeltabase = ui.configbool(
1039 lazydeltabase = ui.configbool(
1040 b'storage', b'revlog.reuse-external-delta-parent'
1040 b'storage', b'revlog.reuse-external-delta-parent'
1041 )
1041 )
1042 if lazydeltabase is None:
1042 if lazydeltabase is None:
1043 lazydeltabase = not scmutil.gddeltaconfig(ui)
1043 lazydeltabase = not scmutil.gddeltaconfig(ui)
1044 options[b'lazydelta'] = lazydelta
1044 options[b'lazydelta'] = lazydelta
1045 options[b'lazydeltabase'] = lazydeltabase
1045 options[b'lazydeltabase'] = lazydeltabase
1046
1046
1047 chainspan = ui.configbytes(b'experimental', b'maxdeltachainspan')
1047 chainspan = ui.configbytes(b'experimental', b'maxdeltachainspan')
1048 if 0 <= chainspan:
1048 if 0 <= chainspan:
1049 options[b'maxdeltachainspan'] = chainspan
1049 options[b'maxdeltachainspan'] = chainspan
1050
1050
1051 mmapindexthreshold = ui.configbytes(b'experimental', b'mmapindexthreshold')
1051 mmapindexthreshold = ui.configbytes(b'experimental', b'mmapindexthreshold')
1052 if mmapindexthreshold is not None:
1052 if mmapindexthreshold is not None:
1053 options[b'mmapindexthreshold'] = mmapindexthreshold
1053 options[b'mmapindexthreshold'] = mmapindexthreshold
1054
1054
1055 withsparseread = ui.configbool(b'experimental', b'sparse-read')
1055 withsparseread = ui.configbool(b'experimental', b'sparse-read')
1056 srdensitythres = float(
1056 srdensitythres = float(
1057 ui.config(b'experimental', b'sparse-read.density-threshold')
1057 ui.config(b'experimental', b'sparse-read.density-threshold')
1058 )
1058 )
1059 srmingapsize = ui.configbytes(b'experimental', b'sparse-read.min-gap-size')
1059 srmingapsize = ui.configbytes(b'experimental', b'sparse-read.min-gap-size')
1060 options[b'with-sparse-read'] = withsparseread
1060 options[b'with-sparse-read'] = withsparseread
1061 options[b'sparse-read-density-threshold'] = srdensitythres
1061 options[b'sparse-read-density-threshold'] = srdensitythres
1062 options[b'sparse-read-min-gap-size'] = srmingapsize
1062 options[b'sparse-read-min-gap-size'] = srmingapsize
1063
1063
1064 sparserevlog = requirementsmod.SPARSEREVLOG_REQUIREMENT in requirements
1064 sparserevlog = requirementsmod.SPARSEREVLOG_REQUIREMENT in requirements
1065 options[b'sparse-revlog'] = sparserevlog
1065 options[b'sparse-revlog'] = sparserevlog
1066 if sparserevlog:
1066 if sparserevlog:
1067 options[b'generaldelta'] = True
1067 options[b'generaldelta'] = True
1068
1068
1069 sidedata = requirementsmod.SIDEDATA_REQUIREMENT in requirements
1070 options[b'side-data'] = sidedata
1071
1072 maxchainlen = None
1069 maxchainlen = None
1073 if sparserevlog:
1070 if sparserevlog:
1074 maxchainlen = revlogconst.SPARSE_REVLOG_MAX_CHAIN_LENGTH
1071 maxchainlen = revlogconst.SPARSE_REVLOG_MAX_CHAIN_LENGTH
1075 # experimental config: format.maxchainlen
1072 # experimental config: format.maxchainlen
1076 maxchainlen = ui.configint(b'format', b'maxchainlen', maxchainlen)
1073 maxchainlen = ui.configint(b'format', b'maxchainlen', maxchainlen)
1077 if maxchainlen is not None:
1074 if maxchainlen is not None:
1078 options[b'maxchainlen'] = maxchainlen
1075 options[b'maxchainlen'] = maxchainlen
1079
1076
1080 for r in requirements:
1077 for r in requirements:
1081 # we allow multiple compression engine requirement to co-exist because
1078 # we allow multiple compression engine requirement to co-exist because
1082 # strickly speaking, revlog seems to support mixed compression style.
1079 # strickly speaking, revlog seems to support mixed compression style.
1083 #
1080 #
1084 # The compression used for new entries will be "the last one"
1081 # The compression used for new entries will be "the last one"
1085 prefix = r.startswith
1082 prefix = r.startswith
1086 if prefix(b'revlog-compression-') or prefix(b'exp-compression-'):
1083 if prefix(b'revlog-compression-') or prefix(b'exp-compression-'):
1087 options[b'compengine'] = r.split(b'-', 2)[2]
1084 options[b'compengine'] = r.split(b'-', 2)[2]
1088
1085
1089 options[b'zlib.level'] = ui.configint(b'storage', b'revlog.zlib.level')
1086 options[b'zlib.level'] = ui.configint(b'storage', b'revlog.zlib.level')
1090 if options[b'zlib.level'] is not None:
1087 if options[b'zlib.level'] is not None:
1091 if not (0 <= options[b'zlib.level'] <= 9):
1088 if not (0 <= options[b'zlib.level'] <= 9):
1092 msg = _(b'invalid value for `storage.revlog.zlib.level` config: %d')
1089 msg = _(b'invalid value for `storage.revlog.zlib.level` config: %d')
1093 raise error.Abort(msg % options[b'zlib.level'])
1090 raise error.Abort(msg % options[b'zlib.level'])
1094 options[b'zstd.level'] = ui.configint(b'storage', b'revlog.zstd.level')
1091 options[b'zstd.level'] = ui.configint(b'storage', b'revlog.zstd.level')
1095 if options[b'zstd.level'] is not None:
1092 if options[b'zstd.level'] is not None:
1096 if not (0 <= options[b'zstd.level'] <= 22):
1093 if not (0 <= options[b'zstd.level'] <= 22):
1097 msg = _(b'invalid value for `storage.revlog.zstd.level` config: %d')
1094 msg = _(b'invalid value for `storage.revlog.zstd.level` config: %d')
1098 raise error.Abort(msg % options[b'zstd.level'])
1095 raise error.Abort(msg % options[b'zstd.level'])
1099
1096
1100 if requirementsmod.NARROW_REQUIREMENT in requirements:
1097 if requirementsmod.NARROW_REQUIREMENT in requirements:
1101 options[b'enableellipsis'] = True
1098 options[b'enableellipsis'] = True
1102
1099
1103 if ui.configbool(b'experimental', b'rust.index'):
1100 if ui.configbool(b'experimental', b'rust.index'):
1104 options[b'rust.index'] = True
1101 options[b'rust.index'] = True
1105 if requirementsmod.NODEMAP_REQUIREMENT in requirements:
1102 if requirementsmod.NODEMAP_REQUIREMENT in requirements:
1106 slow_path = ui.config(
1103 slow_path = ui.config(
1107 b'storage', b'revlog.persistent-nodemap.slow-path'
1104 b'storage', b'revlog.persistent-nodemap.slow-path'
1108 )
1105 )
1109 if slow_path not in (b'allow', b'warn', b'abort'):
1106 if slow_path not in (b'allow', b'warn', b'abort'):
1110 default = ui.config_default(
1107 default = ui.config_default(
1111 b'storage', b'revlog.persistent-nodemap.slow-path'
1108 b'storage', b'revlog.persistent-nodemap.slow-path'
1112 )
1109 )
1113 msg = _(
1110 msg = _(
1114 b'unknown value for config '
1111 b'unknown value for config '
1115 b'"storage.revlog.persistent-nodemap.slow-path": "%s"\n'
1112 b'"storage.revlog.persistent-nodemap.slow-path": "%s"\n'
1116 )
1113 )
1117 ui.warn(msg % slow_path)
1114 ui.warn(msg % slow_path)
1118 if not ui.quiet:
1115 if not ui.quiet:
1119 ui.warn(_(b'falling back to default value: %s\n') % default)
1116 ui.warn(_(b'falling back to default value: %s\n') % default)
1120 slow_path = default
1117 slow_path = default
1121
1118
1122 msg = _(
1119 msg = _(
1123 b"accessing `persistent-nodemap` repository without associated "
1120 b"accessing `persistent-nodemap` repository without associated "
1124 b"fast implementation."
1121 b"fast implementation."
1125 )
1122 )
1126 hint = _(
1123 hint = _(
1127 b"check `hg help config.format.use-persistent-nodemap` "
1124 b"check `hg help config.format.use-persistent-nodemap` "
1128 b"for details"
1125 b"for details"
1129 )
1126 )
1130 if not revlog.HAS_FAST_PERSISTENT_NODEMAP:
1127 if not revlog.HAS_FAST_PERSISTENT_NODEMAP:
1131 if slow_path == b'warn':
1128 if slow_path == b'warn':
1132 msg = b"warning: " + msg + b'\n'
1129 msg = b"warning: " + msg + b'\n'
1133 ui.warn(msg)
1130 ui.warn(msg)
1134 if not ui.quiet:
1131 if not ui.quiet:
1135 hint = b'(' + hint + b')\n'
1132 hint = b'(' + hint + b')\n'
1136 ui.warn(hint)
1133 ui.warn(hint)
1137 if slow_path == b'abort':
1134 if slow_path == b'abort':
1138 raise error.Abort(msg, hint=hint)
1135 raise error.Abort(msg, hint=hint)
1139 options[b'persistent-nodemap'] = True
1136 options[b'persistent-nodemap'] = True
1140 if ui.configbool(b'storage', b'revlog.persistent-nodemap.mmap'):
1137 if ui.configbool(b'storage', b'revlog.persistent-nodemap.mmap'):
1141 options[b'persistent-nodemap.mmap'] = True
1138 options[b'persistent-nodemap.mmap'] = True
1142 if ui.configbool(b'devel', b'persistent-nodemap'):
1139 if ui.configbool(b'devel', b'persistent-nodemap'):
1143 options[b'devel-force-nodemap'] = True
1140 options[b'devel-force-nodemap'] = True
1144
1141
1145 return options
1142 return options
1146
1143
1147
1144
1148 def makemain(**kwargs):
1145 def makemain(**kwargs):
1149 """Produce a type conforming to ``ilocalrepositorymain``."""
1146 """Produce a type conforming to ``ilocalrepositorymain``."""
1150 return localrepository
1147 return localrepository
1151
1148
1152
1149
1153 @interfaceutil.implementer(repository.ilocalrepositoryfilestorage)
1150 @interfaceutil.implementer(repository.ilocalrepositoryfilestorage)
1154 class revlogfilestorage(object):
1151 class revlogfilestorage(object):
1155 """File storage when using revlogs."""
1152 """File storage when using revlogs."""
1156
1153
1157 def file(self, path):
1154 def file(self, path):
1158 if path.startswith(b'/'):
1155 if path.startswith(b'/'):
1159 path = path[1:]
1156 path = path[1:]
1160
1157
1161 return filelog.filelog(self.svfs, path)
1158 return filelog.filelog(self.svfs, path)
1162
1159
1163
1160
1164 @interfaceutil.implementer(repository.ilocalrepositoryfilestorage)
1161 @interfaceutil.implementer(repository.ilocalrepositoryfilestorage)
1165 class revlognarrowfilestorage(object):
1162 class revlognarrowfilestorage(object):
1166 """File storage when using revlogs and narrow files."""
1163 """File storage when using revlogs and narrow files."""
1167
1164
1168 def file(self, path):
1165 def file(self, path):
1169 if path.startswith(b'/'):
1166 if path.startswith(b'/'):
1170 path = path[1:]
1167 path = path[1:]
1171
1168
1172 return filelog.narrowfilelog(self.svfs, path, self._storenarrowmatch)
1169 return filelog.narrowfilelog(self.svfs, path, self._storenarrowmatch)
1173
1170
1174
1171
1175 def makefilestorage(requirements, features, **kwargs):
1172 def makefilestorage(requirements, features, **kwargs):
1176 """Produce a type conforming to ``ilocalrepositoryfilestorage``."""
1173 """Produce a type conforming to ``ilocalrepositoryfilestorage``."""
1177 features.add(repository.REPO_FEATURE_REVLOG_FILE_STORAGE)
1174 features.add(repository.REPO_FEATURE_REVLOG_FILE_STORAGE)
1178 features.add(repository.REPO_FEATURE_STREAM_CLONE)
1175 features.add(repository.REPO_FEATURE_STREAM_CLONE)
1179
1176
1180 if requirementsmod.NARROW_REQUIREMENT in requirements:
1177 if requirementsmod.NARROW_REQUIREMENT in requirements:
1181 return revlognarrowfilestorage
1178 return revlognarrowfilestorage
1182 else:
1179 else:
1183 return revlogfilestorage
1180 return revlogfilestorage
1184
1181
1185
1182
1186 # List of repository interfaces and factory functions for them. Each
1183 # List of repository interfaces and factory functions for them. Each
1187 # will be called in order during ``makelocalrepository()`` to iteratively
1184 # will be called in order during ``makelocalrepository()`` to iteratively
1188 # derive the final type for a local repository instance. We capture the
1185 # derive the final type for a local repository instance. We capture the
1189 # function as a lambda so we don't hold a reference and the module-level
1186 # function as a lambda so we don't hold a reference and the module-level
1190 # functions can be wrapped.
1187 # functions can be wrapped.
1191 REPO_INTERFACES = [
1188 REPO_INTERFACES = [
1192 (repository.ilocalrepositorymain, lambda: makemain),
1189 (repository.ilocalrepositorymain, lambda: makemain),
1193 (repository.ilocalrepositoryfilestorage, lambda: makefilestorage),
1190 (repository.ilocalrepositoryfilestorage, lambda: makefilestorage),
1194 ]
1191 ]
1195
1192
1196
1193
1197 @interfaceutil.implementer(repository.ilocalrepositorymain)
1194 @interfaceutil.implementer(repository.ilocalrepositorymain)
1198 class localrepository(object):
1195 class localrepository(object):
1199 """Main class for representing local repositories.
1196 """Main class for representing local repositories.
1200
1197
1201 All local repositories are instances of this class.
1198 All local repositories are instances of this class.
1202
1199
1203 Constructed on its own, instances of this class are not usable as
1200 Constructed on its own, instances of this class are not usable as
1204 repository objects. To obtain a usable repository object, call
1201 repository objects. To obtain a usable repository object, call
1205 ``hg.repository()``, ``localrepo.instance()``, or
1202 ``hg.repository()``, ``localrepo.instance()``, or
1206 ``localrepo.makelocalrepository()``. The latter is the lowest-level.
1203 ``localrepo.makelocalrepository()``. The latter is the lowest-level.
1207 ``instance()`` adds support for creating new repositories.
1204 ``instance()`` adds support for creating new repositories.
1208 ``hg.repository()`` adds more extension integration, including calling
1205 ``hg.repository()`` adds more extension integration, including calling
1209 ``reposetup()``. Generally speaking, ``hg.repository()`` should be
1206 ``reposetup()``. Generally speaking, ``hg.repository()`` should be
1210 used.
1207 used.
1211 """
1208 """
1212
1209
1213 # obsolete experimental requirements:
1210 # obsolete experimental requirements:
1214 # - manifestv2: An experimental new manifest format that allowed
1211 # - manifestv2: An experimental new manifest format that allowed
1215 # for stem compression of long paths. Experiment ended up not
1212 # for stem compression of long paths. Experiment ended up not
1216 # being successful (repository sizes went up due to worse delta
1213 # being successful (repository sizes went up due to worse delta
1217 # chains), and the code was deleted in 4.6.
1214 # chains), and the code was deleted in 4.6.
1218 supportedformats = {
1215 supportedformats = {
1219 requirementsmod.REVLOGV1_REQUIREMENT,
1216 requirementsmod.REVLOGV1_REQUIREMENT,
1220 requirementsmod.GENERALDELTA_REQUIREMENT,
1217 requirementsmod.GENERALDELTA_REQUIREMENT,
1221 requirementsmod.TREEMANIFEST_REQUIREMENT,
1218 requirementsmod.TREEMANIFEST_REQUIREMENT,
1222 requirementsmod.COPIESSDC_REQUIREMENT,
1219 requirementsmod.COPIESSDC_REQUIREMENT,
1223 requirementsmod.REVLOGV2_REQUIREMENT,
1220 requirementsmod.REVLOGV2_REQUIREMENT,
1224 requirementsmod.SIDEDATA_REQUIREMENT,
1225 requirementsmod.SPARSEREVLOG_REQUIREMENT,
1221 requirementsmod.SPARSEREVLOG_REQUIREMENT,
1226 requirementsmod.NODEMAP_REQUIREMENT,
1222 requirementsmod.NODEMAP_REQUIREMENT,
1227 bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT,
1223 bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT,
1228 requirementsmod.SHARESAFE_REQUIREMENT,
1224 requirementsmod.SHARESAFE_REQUIREMENT,
1229 }
1225 }
1230 _basesupported = supportedformats | {
1226 _basesupported = supportedformats | {
1231 requirementsmod.STORE_REQUIREMENT,
1227 requirementsmod.STORE_REQUIREMENT,
1232 requirementsmod.FNCACHE_REQUIREMENT,
1228 requirementsmod.FNCACHE_REQUIREMENT,
1233 requirementsmod.SHARED_REQUIREMENT,
1229 requirementsmod.SHARED_REQUIREMENT,
1234 requirementsmod.RELATIVE_SHARED_REQUIREMENT,
1230 requirementsmod.RELATIVE_SHARED_REQUIREMENT,
1235 requirementsmod.DOTENCODE_REQUIREMENT,
1231 requirementsmod.DOTENCODE_REQUIREMENT,
1236 requirementsmod.SPARSE_REQUIREMENT,
1232 requirementsmod.SPARSE_REQUIREMENT,
1237 requirementsmod.INTERNAL_PHASE_REQUIREMENT,
1233 requirementsmod.INTERNAL_PHASE_REQUIREMENT,
1238 }
1234 }
1239
1235
1240 # list of prefix for file which can be written without 'wlock'
1236 # list of prefix for file which can be written without 'wlock'
1241 # Extensions should extend this list when needed
1237 # Extensions should extend this list when needed
1242 _wlockfreeprefix = {
1238 _wlockfreeprefix = {
1243 # We migh consider requiring 'wlock' for the next
1239 # We migh consider requiring 'wlock' for the next
1244 # two, but pretty much all the existing code assume
1240 # two, but pretty much all the existing code assume
1245 # wlock is not needed so we keep them excluded for
1241 # wlock is not needed so we keep them excluded for
1246 # now.
1242 # now.
1247 b'hgrc',
1243 b'hgrc',
1248 b'requires',
1244 b'requires',
1249 # XXX cache is a complicatged business someone
1245 # XXX cache is a complicatged business someone
1250 # should investigate this in depth at some point
1246 # should investigate this in depth at some point
1251 b'cache/',
1247 b'cache/',
1252 # XXX shouldn't be dirstate covered by the wlock?
1248 # XXX shouldn't be dirstate covered by the wlock?
1253 b'dirstate',
1249 b'dirstate',
1254 # XXX bisect was still a bit too messy at the time
1250 # XXX bisect was still a bit too messy at the time
1255 # this changeset was introduced. Someone should fix
1251 # this changeset was introduced. Someone should fix
1256 # the remainig bit and drop this line
1252 # the remainig bit and drop this line
1257 b'bisect.state',
1253 b'bisect.state',
1258 }
1254 }
1259
1255
1260 def __init__(
1256 def __init__(
1261 self,
1257 self,
1262 baseui,
1258 baseui,
1263 ui,
1259 ui,
1264 origroot,
1260 origroot,
1265 wdirvfs,
1261 wdirvfs,
1266 hgvfs,
1262 hgvfs,
1267 requirements,
1263 requirements,
1268 supportedrequirements,
1264 supportedrequirements,
1269 sharedpath,
1265 sharedpath,
1270 store,
1266 store,
1271 cachevfs,
1267 cachevfs,
1272 wcachevfs,
1268 wcachevfs,
1273 features,
1269 features,
1274 intents=None,
1270 intents=None,
1275 ):
1271 ):
1276 """Create a new local repository instance.
1272 """Create a new local repository instance.
1277
1273
1278 Most callers should use ``hg.repository()``, ``localrepo.instance()``,
1274 Most callers should use ``hg.repository()``, ``localrepo.instance()``,
1279 or ``localrepo.makelocalrepository()`` for obtaining a new repository
1275 or ``localrepo.makelocalrepository()`` for obtaining a new repository
1280 object.
1276 object.
1281
1277
1282 Arguments:
1278 Arguments:
1283
1279
1284 baseui
1280 baseui
1285 ``ui.ui`` instance that ``ui`` argument was based off of.
1281 ``ui.ui`` instance that ``ui`` argument was based off of.
1286
1282
1287 ui
1283 ui
1288 ``ui.ui`` instance for use by the repository.
1284 ``ui.ui`` instance for use by the repository.
1289
1285
1290 origroot
1286 origroot
1291 ``bytes`` path to working directory root of this repository.
1287 ``bytes`` path to working directory root of this repository.
1292
1288
1293 wdirvfs
1289 wdirvfs
1294 ``vfs.vfs`` rooted at the working directory.
1290 ``vfs.vfs`` rooted at the working directory.
1295
1291
1296 hgvfs
1292 hgvfs
1297 ``vfs.vfs`` rooted at .hg/
1293 ``vfs.vfs`` rooted at .hg/
1298
1294
1299 requirements
1295 requirements
1300 ``set`` of bytestrings representing repository opening requirements.
1296 ``set`` of bytestrings representing repository opening requirements.
1301
1297
1302 supportedrequirements
1298 supportedrequirements
1303 ``set`` of bytestrings representing repository requirements that we
1299 ``set`` of bytestrings representing repository requirements that we
1304 know how to open. May be a supetset of ``requirements``.
1300 know how to open. May be a supetset of ``requirements``.
1305
1301
1306 sharedpath
1302 sharedpath
1307 ``bytes`` Defining path to storage base directory. Points to a
1303 ``bytes`` Defining path to storage base directory. Points to a
1308 ``.hg/`` directory somewhere.
1304 ``.hg/`` directory somewhere.
1309
1305
1310 store
1306 store
1311 ``store.basicstore`` (or derived) instance providing access to
1307 ``store.basicstore`` (or derived) instance providing access to
1312 versioned storage.
1308 versioned storage.
1313
1309
1314 cachevfs
1310 cachevfs
1315 ``vfs.vfs`` used for cache files.
1311 ``vfs.vfs`` used for cache files.
1316
1312
1317 wcachevfs
1313 wcachevfs
1318 ``vfs.vfs`` used for cache files related to the working copy.
1314 ``vfs.vfs`` used for cache files related to the working copy.
1319
1315
1320 features
1316 features
1321 ``set`` of bytestrings defining features/capabilities of this
1317 ``set`` of bytestrings defining features/capabilities of this
1322 instance.
1318 instance.
1323
1319
1324 intents
1320 intents
1325 ``set`` of system strings indicating what this repo will be used
1321 ``set`` of system strings indicating what this repo will be used
1326 for.
1322 for.
1327 """
1323 """
1328 self.baseui = baseui
1324 self.baseui = baseui
1329 self.ui = ui
1325 self.ui = ui
1330 self.origroot = origroot
1326 self.origroot = origroot
1331 # vfs rooted at working directory.
1327 # vfs rooted at working directory.
1332 self.wvfs = wdirvfs
1328 self.wvfs = wdirvfs
1333 self.root = wdirvfs.base
1329 self.root = wdirvfs.base
1334 # vfs rooted at .hg/. Used to access most non-store paths.
1330 # vfs rooted at .hg/. Used to access most non-store paths.
1335 self.vfs = hgvfs
1331 self.vfs = hgvfs
1336 self.path = hgvfs.base
1332 self.path = hgvfs.base
1337 self.requirements = requirements
1333 self.requirements = requirements
1338 self.nodeconstants = sha1nodeconstants
1334 self.nodeconstants = sha1nodeconstants
1339 self.nullid = self.nodeconstants.nullid
1335 self.nullid = self.nodeconstants.nullid
1340 self.supported = supportedrequirements
1336 self.supported = supportedrequirements
1341 self.sharedpath = sharedpath
1337 self.sharedpath = sharedpath
1342 self.store = store
1338 self.store = store
1343 self.cachevfs = cachevfs
1339 self.cachevfs = cachevfs
1344 self.wcachevfs = wcachevfs
1340 self.wcachevfs = wcachevfs
1345 self.features = features
1341 self.features = features
1346
1342
1347 self.filtername = None
1343 self.filtername = None
1348
1344
1349 if self.ui.configbool(b'devel', b'all-warnings') or self.ui.configbool(
1345 if self.ui.configbool(b'devel', b'all-warnings') or self.ui.configbool(
1350 b'devel', b'check-locks'
1346 b'devel', b'check-locks'
1351 ):
1347 ):
1352 self.vfs.audit = self._getvfsward(self.vfs.audit)
1348 self.vfs.audit = self._getvfsward(self.vfs.audit)
1353 # A list of callback to shape the phase if no data were found.
1349 # A list of callback to shape the phase if no data were found.
1354 # Callback are in the form: func(repo, roots) --> processed root.
1350 # Callback are in the form: func(repo, roots) --> processed root.
1355 # This list it to be filled by extension during repo setup
1351 # This list it to be filled by extension during repo setup
1356 self._phasedefaults = []
1352 self._phasedefaults = []
1357
1353
1358 color.setup(self.ui)
1354 color.setup(self.ui)
1359
1355
1360 self.spath = self.store.path
1356 self.spath = self.store.path
1361 self.svfs = self.store.vfs
1357 self.svfs = self.store.vfs
1362 self.sjoin = self.store.join
1358 self.sjoin = self.store.join
1363 if self.ui.configbool(b'devel', b'all-warnings') or self.ui.configbool(
1359 if self.ui.configbool(b'devel', b'all-warnings') or self.ui.configbool(
1364 b'devel', b'check-locks'
1360 b'devel', b'check-locks'
1365 ):
1361 ):
1366 if util.safehasattr(self.svfs, b'vfs'): # this is filtervfs
1362 if util.safehasattr(self.svfs, b'vfs'): # this is filtervfs
1367 self.svfs.vfs.audit = self._getsvfsward(self.svfs.vfs.audit)
1363 self.svfs.vfs.audit = self._getsvfsward(self.svfs.vfs.audit)
1368 else: # standard vfs
1364 else: # standard vfs
1369 self.svfs.audit = self._getsvfsward(self.svfs.audit)
1365 self.svfs.audit = self._getsvfsward(self.svfs.audit)
1370
1366
1371 self._dirstatevalidatewarned = False
1367 self._dirstatevalidatewarned = False
1372
1368
1373 self._branchcaches = branchmap.BranchMapCache()
1369 self._branchcaches = branchmap.BranchMapCache()
1374 self._revbranchcache = None
1370 self._revbranchcache = None
1375 self._filterpats = {}
1371 self._filterpats = {}
1376 self._datafilters = {}
1372 self._datafilters = {}
1377 self._transref = self._lockref = self._wlockref = None
1373 self._transref = self._lockref = self._wlockref = None
1378
1374
1379 # A cache for various files under .hg/ that tracks file changes,
1375 # A cache for various files under .hg/ that tracks file changes,
1380 # (used by the filecache decorator)
1376 # (used by the filecache decorator)
1381 #
1377 #
1382 # Maps a property name to its util.filecacheentry
1378 # Maps a property name to its util.filecacheentry
1383 self._filecache = {}
1379 self._filecache = {}
1384
1380
1385 # hold sets of revision to be filtered
1381 # hold sets of revision to be filtered
1386 # should be cleared when something might have changed the filter value:
1382 # should be cleared when something might have changed the filter value:
1387 # - new changesets,
1383 # - new changesets,
1388 # - phase change,
1384 # - phase change,
1389 # - new obsolescence marker,
1385 # - new obsolescence marker,
1390 # - working directory parent change,
1386 # - working directory parent change,
1391 # - bookmark changes
1387 # - bookmark changes
1392 self.filteredrevcache = {}
1388 self.filteredrevcache = {}
1393
1389
1394 # post-dirstate-status hooks
1390 # post-dirstate-status hooks
1395 self._postdsstatus = []
1391 self._postdsstatus = []
1396
1392
1397 # generic mapping between names and nodes
1393 # generic mapping between names and nodes
1398 self.names = namespaces.namespaces()
1394 self.names = namespaces.namespaces()
1399
1395
1400 # Key to signature value.
1396 # Key to signature value.
1401 self._sparsesignaturecache = {}
1397 self._sparsesignaturecache = {}
1402 # Signature to cached matcher instance.
1398 # Signature to cached matcher instance.
1403 self._sparsematchercache = {}
1399 self._sparsematchercache = {}
1404
1400
1405 self._extrafilterid = repoview.extrafilter(ui)
1401 self._extrafilterid = repoview.extrafilter(ui)
1406
1402
1407 self.filecopiesmode = None
1403 self.filecopiesmode = None
1408 if requirementsmod.COPIESSDC_REQUIREMENT in self.requirements:
1404 if requirementsmod.COPIESSDC_REQUIREMENT in self.requirements:
1409 self.filecopiesmode = b'changeset-sidedata'
1405 self.filecopiesmode = b'changeset-sidedata'
1410
1406
1411 self._wanted_sidedata = set()
1407 self._wanted_sidedata = set()
1412 self._sidedata_computers = {}
1408 self._sidedata_computers = {}
1413 sidedatamod.set_sidedata_spec_for_repo(self)
1409 sidedatamod.set_sidedata_spec_for_repo(self)
1414
1410
1415 def _getvfsward(self, origfunc):
1411 def _getvfsward(self, origfunc):
1416 """build a ward for self.vfs"""
1412 """build a ward for self.vfs"""
1417 rref = weakref.ref(self)
1413 rref = weakref.ref(self)
1418
1414
1419 def checkvfs(path, mode=None):
1415 def checkvfs(path, mode=None):
1420 ret = origfunc(path, mode=mode)
1416 ret = origfunc(path, mode=mode)
1421 repo = rref()
1417 repo = rref()
1422 if (
1418 if (
1423 repo is None
1419 repo is None
1424 or not util.safehasattr(repo, b'_wlockref')
1420 or not util.safehasattr(repo, b'_wlockref')
1425 or not util.safehasattr(repo, b'_lockref')
1421 or not util.safehasattr(repo, b'_lockref')
1426 ):
1422 ):
1427 return
1423 return
1428 if mode in (None, b'r', b'rb'):
1424 if mode in (None, b'r', b'rb'):
1429 return
1425 return
1430 if path.startswith(repo.path):
1426 if path.startswith(repo.path):
1431 # truncate name relative to the repository (.hg)
1427 # truncate name relative to the repository (.hg)
1432 path = path[len(repo.path) + 1 :]
1428 path = path[len(repo.path) + 1 :]
1433 if path.startswith(b'cache/'):
1429 if path.startswith(b'cache/'):
1434 msg = b'accessing cache with vfs instead of cachevfs: "%s"'
1430 msg = b'accessing cache with vfs instead of cachevfs: "%s"'
1435 repo.ui.develwarn(msg % path, stacklevel=3, config=b"cache-vfs")
1431 repo.ui.develwarn(msg % path, stacklevel=3, config=b"cache-vfs")
1436 # path prefixes covered by 'lock'
1432 # path prefixes covered by 'lock'
1437 vfs_path_prefixes = (
1433 vfs_path_prefixes = (
1438 b'journal.',
1434 b'journal.',
1439 b'undo.',
1435 b'undo.',
1440 b'strip-backup/',
1436 b'strip-backup/',
1441 b'cache/',
1437 b'cache/',
1442 )
1438 )
1443 if any(path.startswith(prefix) for prefix in vfs_path_prefixes):
1439 if any(path.startswith(prefix) for prefix in vfs_path_prefixes):
1444 if repo._currentlock(repo._lockref) is None:
1440 if repo._currentlock(repo._lockref) is None:
1445 repo.ui.develwarn(
1441 repo.ui.develwarn(
1446 b'write with no lock: "%s"' % path,
1442 b'write with no lock: "%s"' % path,
1447 stacklevel=3,
1443 stacklevel=3,
1448 config=b'check-locks',
1444 config=b'check-locks',
1449 )
1445 )
1450 elif repo._currentlock(repo._wlockref) is None:
1446 elif repo._currentlock(repo._wlockref) is None:
1451 # rest of vfs files are covered by 'wlock'
1447 # rest of vfs files are covered by 'wlock'
1452 #
1448 #
1453 # exclude special files
1449 # exclude special files
1454 for prefix in self._wlockfreeprefix:
1450 for prefix in self._wlockfreeprefix:
1455 if path.startswith(prefix):
1451 if path.startswith(prefix):
1456 return
1452 return
1457 repo.ui.develwarn(
1453 repo.ui.develwarn(
1458 b'write with no wlock: "%s"' % path,
1454 b'write with no wlock: "%s"' % path,
1459 stacklevel=3,
1455 stacklevel=3,
1460 config=b'check-locks',
1456 config=b'check-locks',
1461 )
1457 )
1462 return ret
1458 return ret
1463
1459
1464 return checkvfs
1460 return checkvfs
1465
1461
1466 def _getsvfsward(self, origfunc):
1462 def _getsvfsward(self, origfunc):
1467 """build a ward for self.svfs"""
1463 """build a ward for self.svfs"""
1468 rref = weakref.ref(self)
1464 rref = weakref.ref(self)
1469
1465
1470 def checksvfs(path, mode=None):
1466 def checksvfs(path, mode=None):
1471 ret = origfunc(path, mode=mode)
1467 ret = origfunc(path, mode=mode)
1472 repo = rref()
1468 repo = rref()
1473 if repo is None or not util.safehasattr(repo, b'_lockref'):
1469 if repo is None or not util.safehasattr(repo, b'_lockref'):
1474 return
1470 return
1475 if mode in (None, b'r', b'rb'):
1471 if mode in (None, b'r', b'rb'):
1476 return
1472 return
1477 if path.startswith(repo.sharedpath):
1473 if path.startswith(repo.sharedpath):
1478 # truncate name relative to the repository (.hg)
1474 # truncate name relative to the repository (.hg)
1479 path = path[len(repo.sharedpath) + 1 :]
1475 path = path[len(repo.sharedpath) + 1 :]
1480 if repo._currentlock(repo._lockref) is None:
1476 if repo._currentlock(repo._lockref) is None:
1481 repo.ui.develwarn(
1477 repo.ui.develwarn(
1482 b'write with no lock: "%s"' % path, stacklevel=4
1478 b'write with no lock: "%s"' % path, stacklevel=4
1483 )
1479 )
1484 return ret
1480 return ret
1485
1481
1486 return checksvfs
1482 return checksvfs
1487
1483
1488 def close(self):
1484 def close(self):
1489 self._writecaches()
1485 self._writecaches()
1490
1486
1491 def _writecaches(self):
1487 def _writecaches(self):
1492 if self._revbranchcache:
1488 if self._revbranchcache:
1493 self._revbranchcache.write()
1489 self._revbranchcache.write()
1494
1490
1495 def _restrictcapabilities(self, caps):
1491 def _restrictcapabilities(self, caps):
1496 if self.ui.configbool(b'experimental', b'bundle2-advertise'):
1492 if self.ui.configbool(b'experimental', b'bundle2-advertise'):
1497 caps = set(caps)
1493 caps = set(caps)
1498 capsblob = bundle2.encodecaps(
1494 capsblob = bundle2.encodecaps(
1499 bundle2.getrepocaps(self, role=b'client')
1495 bundle2.getrepocaps(self, role=b'client')
1500 )
1496 )
1501 caps.add(b'bundle2=' + urlreq.quote(capsblob))
1497 caps.add(b'bundle2=' + urlreq.quote(capsblob))
1502 if self.ui.configbool(b'experimental', b'narrow'):
1498 if self.ui.configbool(b'experimental', b'narrow'):
1503 caps.add(wireprototypes.NARROWCAP)
1499 caps.add(wireprototypes.NARROWCAP)
1504 return caps
1500 return caps
1505
1501
1506 # Don't cache auditor/nofsauditor, or you'll end up with reference cycle:
1502 # Don't cache auditor/nofsauditor, or you'll end up with reference cycle:
1507 # self -> auditor -> self._checknested -> self
1503 # self -> auditor -> self._checknested -> self
1508
1504
1509 @property
1505 @property
1510 def auditor(self):
1506 def auditor(self):
1511 # This is only used by context.workingctx.match in order to
1507 # This is only used by context.workingctx.match in order to
1512 # detect files in subrepos.
1508 # detect files in subrepos.
1513 return pathutil.pathauditor(self.root, callback=self._checknested)
1509 return pathutil.pathauditor(self.root, callback=self._checknested)
1514
1510
1515 @property
1511 @property
1516 def nofsauditor(self):
1512 def nofsauditor(self):
1517 # This is only used by context.basectx.match in order to detect
1513 # This is only used by context.basectx.match in order to detect
1518 # files in subrepos.
1514 # files in subrepos.
1519 return pathutil.pathauditor(
1515 return pathutil.pathauditor(
1520 self.root, callback=self._checknested, realfs=False, cached=True
1516 self.root, callback=self._checknested, realfs=False, cached=True
1521 )
1517 )
1522
1518
1523 def _checknested(self, path):
1519 def _checknested(self, path):
1524 """Determine if path is a legal nested repository."""
1520 """Determine if path is a legal nested repository."""
1525 if not path.startswith(self.root):
1521 if not path.startswith(self.root):
1526 return False
1522 return False
1527 subpath = path[len(self.root) + 1 :]
1523 subpath = path[len(self.root) + 1 :]
1528 normsubpath = util.pconvert(subpath)
1524 normsubpath = util.pconvert(subpath)
1529
1525
1530 # XXX: Checking against the current working copy is wrong in
1526 # XXX: Checking against the current working copy is wrong in
1531 # the sense that it can reject things like
1527 # the sense that it can reject things like
1532 #
1528 #
1533 # $ hg cat -r 10 sub/x.txt
1529 # $ hg cat -r 10 sub/x.txt
1534 #
1530 #
1535 # if sub/ is no longer a subrepository in the working copy
1531 # if sub/ is no longer a subrepository in the working copy
1536 # parent revision.
1532 # parent revision.
1537 #
1533 #
1538 # However, it can of course also allow things that would have
1534 # However, it can of course also allow things that would have
1539 # been rejected before, such as the above cat command if sub/
1535 # been rejected before, such as the above cat command if sub/
1540 # is a subrepository now, but was a normal directory before.
1536 # is a subrepository now, but was a normal directory before.
1541 # The old path auditor would have rejected by mistake since it
1537 # The old path auditor would have rejected by mistake since it
1542 # panics when it sees sub/.hg/.
1538 # panics when it sees sub/.hg/.
1543 #
1539 #
1544 # All in all, checking against the working copy seems sensible
1540 # All in all, checking against the working copy seems sensible
1545 # since we want to prevent access to nested repositories on
1541 # since we want to prevent access to nested repositories on
1546 # the filesystem *now*.
1542 # the filesystem *now*.
1547 ctx = self[None]
1543 ctx = self[None]
1548 parts = util.splitpath(subpath)
1544 parts = util.splitpath(subpath)
1549 while parts:
1545 while parts:
1550 prefix = b'/'.join(parts)
1546 prefix = b'/'.join(parts)
1551 if prefix in ctx.substate:
1547 if prefix in ctx.substate:
1552 if prefix == normsubpath:
1548 if prefix == normsubpath:
1553 return True
1549 return True
1554 else:
1550 else:
1555 sub = ctx.sub(prefix)
1551 sub = ctx.sub(prefix)
1556 return sub.checknested(subpath[len(prefix) + 1 :])
1552 return sub.checknested(subpath[len(prefix) + 1 :])
1557 else:
1553 else:
1558 parts.pop()
1554 parts.pop()
1559 return False
1555 return False
1560
1556
1561 def peer(self):
1557 def peer(self):
1562 return localpeer(self) # not cached to avoid reference cycle
1558 return localpeer(self) # not cached to avoid reference cycle
1563
1559
1564 def unfiltered(self):
1560 def unfiltered(self):
1565 """Return unfiltered version of the repository
1561 """Return unfiltered version of the repository
1566
1562
1567 Intended to be overwritten by filtered repo."""
1563 Intended to be overwritten by filtered repo."""
1568 return self
1564 return self
1569
1565
1570 def filtered(self, name, visibilityexceptions=None):
1566 def filtered(self, name, visibilityexceptions=None):
1571 """Return a filtered version of a repository
1567 """Return a filtered version of a repository
1572
1568
1573 The `name` parameter is the identifier of the requested view. This
1569 The `name` parameter is the identifier of the requested view. This
1574 will return a repoview object set "exactly" to the specified view.
1570 will return a repoview object set "exactly" to the specified view.
1575
1571
1576 This function does not apply recursive filtering to a repository. For
1572 This function does not apply recursive filtering to a repository. For
1577 example calling `repo.filtered("served")` will return a repoview using
1573 example calling `repo.filtered("served")` will return a repoview using
1578 the "served" view, regardless of the initial view used by `repo`.
1574 the "served" view, regardless of the initial view used by `repo`.
1579
1575
1580 In other word, there is always only one level of `repoview` "filtering".
1576 In other word, there is always only one level of `repoview` "filtering".
1581 """
1577 """
1582 if self._extrafilterid is not None and b'%' not in name:
1578 if self._extrafilterid is not None and b'%' not in name:
1583 name = name + b'%' + self._extrafilterid
1579 name = name + b'%' + self._extrafilterid
1584
1580
1585 cls = repoview.newtype(self.unfiltered().__class__)
1581 cls = repoview.newtype(self.unfiltered().__class__)
1586 return cls(self, name, visibilityexceptions)
1582 return cls(self, name, visibilityexceptions)
1587
1583
1588 @mixedrepostorecache(
1584 @mixedrepostorecache(
1589 (b'bookmarks', b'plain'),
1585 (b'bookmarks', b'plain'),
1590 (b'bookmarks.current', b'plain'),
1586 (b'bookmarks.current', b'plain'),
1591 (b'bookmarks', b''),
1587 (b'bookmarks', b''),
1592 (b'00changelog.i', b''),
1588 (b'00changelog.i', b''),
1593 )
1589 )
1594 def _bookmarks(self):
1590 def _bookmarks(self):
1595 # Since the multiple files involved in the transaction cannot be
1591 # Since the multiple files involved in the transaction cannot be
1596 # written atomically (with current repository format), there is a race
1592 # written atomically (with current repository format), there is a race
1597 # condition here.
1593 # condition here.
1598 #
1594 #
1599 # 1) changelog content A is read
1595 # 1) changelog content A is read
1600 # 2) outside transaction update changelog to content B
1596 # 2) outside transaction update changelog to content B
1601 # 3) outside transaction update bookmark file referring to content B
1597 # 3) outside transaction update bookmark file referring to content B
1602 # 4) bookmarks file content is read and filtered against changelog-A
1598 # 4) bookmarks file content is read and filtered against changelog-A
1603 #
1599 #
1604 # When this happens, bookmarks against nodes missing from A are dropped.
1600 # When this happens, bookmarks against nodes missing from A are dropped.
1605 #
1601 #
1606 # Having this happening during read is not great, but it become worse
1602 # Having this happening during read is not great, but it become worse
1607 # when this happen during write because the bookmarks to the "unknown"
1603 # when this happen during write because the bookmarks to the "unknown"
1608 # nodes will be dropped for good. However, writes happen within locks.
1604 # nodes will be dropped for good. However, writes happen within locks.
1609 # This locking makes it possible to have a race free consistent read.
1605 # This locking makes it possible to have a race free consistent read.
1610 # For this purpose data read from disc before locking are
1606 # For this purpose data read from disc before locking are
1611 # "invalidated" right after the locks are taken. This invalidations are
1607 # "invalidated" right after the locks are taken. This invalidations are
1612 # "light", the `filecache` mechanism keep the data in memory and will
1608 # "light", the `filecache` mechanism keep the data in memory and will
1613 # reuse them if the underlying files did not changed. Not parsing the
1609 # reuse them if the underlying files did not changed. Not parsing the
1614 # same data multiple times helps performances.
1610 # same data multiple times helps performances.
1615 #
1611 #
1616 # Unfortunately in the case describe above, the files tracked by the
1612 # Unfortunately in the case describe above, the files tracked by the
1617 # bookmarks file cache might not have changed, but the in-memory
1613 # bookmarks file cache might not have changed, but the in-memory
1618 # content is still "wrong" because we used an older changelog content
1614 # content is still "wrong" because we used an older changelog content
1619 # to process the on-disk data. So after locking, the changelog would be
1615 # to process the on-disk data. So after locking, the changelog would be
1620 # refreshed but `_bookmarks` would be preserved.
1616 # refreshed but `_bookmarks` would be preserved.
1621 # Adding `00changelog.i` to the list of tracked file is not
1617 # Adding `00changelog.i` to the list of tracked file is not
1622 # enough, because at the time we build the content for `_bookmarks` in
1618 # enough, because at the time we build the content for `_bookmarks` in
1623 # (4), the changelog file has already diverged from the content used
1619 # (4), the changelog file has already diverged from the content used
1624 # for loading `changelog` in (1)
1620 # for loading `changelog` in (1)
1625 #
1621 #
1626 # To prevent the issue, we force the changelog to be explicitly
1622 # To prevent the issue, we force the changelog to be explicitly
1627 # reloaded while computing `_bookmarks`. The data race can still happen
1623 # reloaded while computing `_bookmarks`. The data race can still happen
1628 # without the lock (with a narrower window), but it would no longer go
1624 # without the lock (with a narrower window), but it would no longer go
1629 # undetected during the lock time refresh.
1625 # undetected during the lock time refresh.
1630 #
1626 #
1631 # The new schedule is as follow
1627 # The new schedule is as follow
1632 #
1628 #
1633 # 1) filecache logic detect that `_bookmarks` needs to be computed
1629 # 1) filecache logic detect that `_bookmarks` needs to be computed
1634 # 2) cachestat for `bookmarks` and `changelog` are captured (for book)
1630 # 2) cachestat for `bookmarks` and `changelog` are captured (for book)
1635 # 3) We force `changelog` filecache to be tested
1631 # 3) We force `changelog` filecache to be tested
1636 # 4) cachestat for `changelog` are captured (for changelog)
1632 # 4) cachestat for `changelog` are captured (for changelog)
1637 # 5) `_bookmarks` is computed and cached
1633 # 5) `_bookmarks` is computed and cached
1638 #
1634 #
1639 # The step in (3) ensure we have a changelog at least as recent as the
1635 # The step in (3) ensure we have a changelog at least as recent as the
1640 # cache stat computed in (1). As a result at locking time:
1636 # cache stat computed in (1). As a result at locking time:
1641 # * if the changelog did not changed since (1) -> we can reuse the data
1637 # * if the changelog did not changed since (1) -> we can reuse the data
1642 # * otherwise -> the bookmarks get refreshed.
1638 # * otherwise -> the bookmarks get refreshed.
1643 self._refreshchangelog()
1639 self._refreshchangelog()
1644 return bookmarks.bmstore(self)
1640 return bookmarks.bmstore(self)
1645
1641
1646 def _refreshchangelog(self):
1642 def _refreshchangelog(self):
1647 """make sure the in memory changelog match the on-disk one"""
1643 """make sure the in memory changelog match the on-disk one"""
1648 if 'changelog' in vars(self) and self.currenttransaction() is None:
1644 if 'changelog' in vars(self) and self.currenttransaction() is None:
1649 del self.changelog
1645 del self.changelog
1650
1646
1651 @property
1647 @property
1652 def _activebookmark(self):
1648 def _activebookmark(self):
1653 return self._bookmarks.active
1649 return self._bookmarks.active
1654
1650
1655 # _phasesets depend on changelog. what we need is to call
1651 # _phasesets depend on changelog. what we need is to call
1656 # _phasecache.invalidate() if '00changelog.i' was changed, but it
1652 # _phasecache.invalidate() if '00changelog.i' was changed, but it
1657 # can't be easily expressed in filecache mechanism.
1653 # can't be easily expressed in filecache mechanism.
1658 @storecache(b'phaseroots', b'00changelog.i')
1654 @storecache(b'phaseroots', b'00changelog.i')
1659 def _phasecache(self):
1655 def _phasecache(self):
1660 return phases.phasecache(self, self._phasedefaults)
1656 return phases.phasecache(self, self._phasedefaults)
1661
1657
1662 @storecache(b'obsstore')
1658 @storecache(b'obsstore')
1663 def obsstore(self):
1659 def obsstore(self):
1664 return obsolete.makestore(self.ui, self)
1660 return obsolete.makestore(self.ui, self)
1665
1661
1666 @storecache(b'00changelog.i')
1662 @storecache(b'00changelog.i')
1667 def changelog(self):
1663 def changelog(self):
1668 # load dirstate before changelog to avoid race see issue6303
1664 # load dirstate before changelog to avoid race see issue6303
1669 self.dirstate.prefetch_parents()
1665 self.dirstate.prefetch_parents()
1670 return self.store.changelog(
1666 return self.store.changelog(
1671 txnutil.mayhavepending(self.root),
1667 txnutil.mayhavepending(self.root),
1672 concurrencychecker=revlogchecker.get_checker(self.ui, b'changelog'),
1668 concurrencychecker=revlogchecker.get_checker(self.ui, b'changelog'),
1673 )
1669 )
1674
1670
1675 @storecache(b'00manifest.i')
1671 @storecache(b'00manifest.i')
1676 def manifestlog(self):
1672 def manifestlog(self):
1677 return self.store.manifestlog(self, self._storenarrowmatch)
1673 return self.store.manifestlog(self, self._storenarrowmatch)
1678
1674
1679 @repofilecache(b'dirstate')
1675 @repofilecache(b'dirstate')
1680 def dirstate(self):
1676 def dirstate(self):
1681 return self._makedirstate()
1677 return self._makedirstate()
1682
1678
1683 def _makedirstate(self):
1679 def _makedirstate(self):
1684 """Extension point for wrapping the dirstate per-repo."""
1680 """Extension point for wrapping the dirstate per-repo."""
1685 sparsematchfn = lambda: sparse.matcher(self)
1681 sparsematchfn = lambda: sparse.matcher(self)
1686
1682
1687 return dirstate.dirstate(
1683 return dirstate.dirstate(
1688 self.vfs,
1684 self.vfs,
1689 self.ui,
1685 self.ui,
1690 self.root,
1686 self.root,
1691 self._dirstatevalidate,
1687 self._dirstatevalidate,
1692 sparsematchfn,
1688 sparsematchfn,
1693 self.nodeconstants,
1689 self.nodeconstants,
1694 )
1690 )
1695
1691
1696 def _dirstatevalidate(self, node):
1692 def _dirstatevalidate(self, node):
1697 try:
1693 try:
1698 self.changelog.rev(node)
1694 self.changelog.rev(node)
1699 return node
1695 return node
1700 except error.LookupError:
1696 except error.LookupError:
1701 if not self._dirstatevalidatewarned:
1697 if not self._dirstatevalidatewarned:
1702 self._dirstatevalidatewarned = True
1698 self._dirstatevalidatewarned = True
1703 self.ui.warn(
1699 self.ui.warn(
1704 _(b"warning: ignoring unknown working parent %s!\n")
1700 _(b"warning: ignoring unknown working parent %s!\n")
1705 % short(node)
1701 % short(node)
1706 )
1702 )
1707 return self.nullid
1703 return self.nullid
1708
1704
1709 @storecache(narrowspec.FILENAME)
1705 @storecache(narrowspec.FILENAME)
1710 def narrowpats(self):
1706 def narrowpats(self):
1711 """matcher patterns for this repository's narrowspec
1707 """matcher patterns for this repository's narrowspec
1712
1708
1713 A tuple of (includes, excludes).
1709 A tuple of (includes, excludes).
1714 """
1710 """
1715 return narrowspec.load(self)
1711 return narrowspec.load(self)
1716
1712
1717 @storecache(narrowspec.FILENAME)
1713 @storecache(narrowspec.FILENAME)
1718 def _storenarrowmatch(self):
1714 def _storenarrowmatch(self):
1719 if requirementsmod.NARROW_REQUIREMENT not in self.requirements:
1715 if requirementsmod.NARROW_REQUIREMENT not in self.requirements:
1720 return matchmod.always()
1716 return matchmod.always()
1721 include, exclude = self.narrowpats
1717 include, exclude = self.narrowpats
1722 return narrowspec.match(self.root, include=include, exclude=exclude)
1718 return narrowspec.match(self.root, include=include, exclude=exclude)
1723
1719
1724 @storecache(narrowspec.FILENAME)
1720 @storecache(narrowspec.FILENAME)
1725 def _narrowmatch(self):
1721 def _narrowmatch(self):
1726 if requirementsmod.NARROW_REQUIREMENT not in self.requirements:
1722 if requirementsmod.NARROW_REQUIREMENT not in self.requirements:
1727 return matchmod.always()
1723 return matchmod.always()
1728 narrowspec.checkworkingcopynarrowspec(self)
1724 narrowspec.checkworkingcopynarrowspec(self)
1729 include, exclude = self.narrowpats
1725 include, exclude = self.narrowpats
1730 return narrowspec.match(self.root, include=include, exclude=exclude)
1726 return narrowspec.match(self.root, include=include, exclude=exclude)
1731
1727
1732 def narrowmatch(self, match=None, includeexact=False):
1728 def narrowmatch(self, match=None, includeexact=False):
1733 """matcher corresponding the the repo's narrowspec
1729 """matcher corresponding the the repo's narrowspec
1734
1730
1735 If `match` is given, then that will be intersected with the narrow
1731 If `match` is given, then that will be intersected with the narrow
1736 matcher.
1732 matcher.
1737
1733
1738 If `includeexact` is True, then any exact matches from `match` will
1734 If `includeexact` is True, then any exact matches from `match` will
1739 be included even if they're outside the narrowspec.
1735 be included even if they're outside the narrowspec.
1740 """
1736 """
1741 if match:
1737 if match:
1742 if includeexact and not self._narrowmatch.always():
1738 if includeexact and not self._narrowmatch.always():
1743 # do not exclude explicitly-specified paths so that they can
1739 # do not exclude explicitly-specified paths so that they can
1744 # be warned later on
1740 # be warned later on
1745 em = matchmod.exact(match.files())
1741 em = matchmod.exact(match.files())
1746 nm = matchmod.unionmatcher([self._narrowmatch, em])
1742 nm = matchmod.unionmatcher([self._narrowmatch, em])
1747 return matchmod.intersectmatchers(match, nm)
1743 return matchmod.intersectmatchers(match, nm)
1748 return matchmod.intersectmatchers(match, self._narrowmatch)
1744 return matchmod.intersectmatchers(match, self._narrowmatch)
1749 return self._narrowmatch
1745 return self._narrowmatch
1750
1746
1751 def setnarrowpats(self, newincludes, newexcludes):
1747 def setnarrowpats(self, newincludes, newexcludes):
1752 narrowspec.save(self, newincludes, newexcludes)
1748 narrowspec.save(self, newincludes, newexcludes)
1753 self.invalidate(clearfilecache=True)
1749 self.invalidate(clearfilecache=True)
1754
1750
1755 @unfilteredpropertycache
1751 @unfilteredpropertycache
1756 def _quick_access_changeid_null(self):
1752 def _quick_access_changeid_null(self):
1757 return {
1753 return {
1758 b'null': (nullrev, self.nodeconstants.nullid),
1754 b'null': (nullrev, self.nodeconstants.nullid),
1759 nullrev: (nullrev, self.nodeconstants.nullid),
1755 nullrev: (nullrev, self.nodeconstants.nullid),
1760 self.nullid: (nullrev, self.nullid),
1756 self.nullid: (nullrev, self.nullid),
1761 }
1757 }
1762
1758
1763 @unfilteredpropertycache
1759 @unfilteredpropertycache
1764 def _quick_access_changeid_wc(self):
1760 def _quick_access_changeid_wc(self):
1765 # also fast path access to the working copy parents
1761 # also fast path access to the working copy parents
1766 # however, only do it for filter that ensure wc is visible.
1762 # however, only do it for filter that ensure wc is visible.
1767 quick = self._quick_access_changeid_null.copy()
1763 quick = self._quick_access_changeid_null.copy()
1768 cl = self.unfiltered().changelog
1764 cl = self.unfiltered().changelog
1769 for node in self.dirstate.parents():
1765 for node in self.dirstate.parents():
1770 if node == self.nullid:
1766 if node == self.nullid:
1771 continue
1767 continue
1772 rev = cl.index.get_rev(node)
1768 rev = cl.index.get_rev(node)
1773 if rev is None:
1769 if rev is None:
1774 # unknown working copy parent case:
1770 # unknown working copy parent case:
1775 #
1771 #
1776 # skip the fast path and let higher code deal with it
1772 # skip the fast path and let higher code deal with it
1777 continue
1773 continue
1778 pair = (rev, node)
1774 pair = (rev, node)
1779 quick[rev] = pair
1775 quick[rev] = pair
1780 quick[node] = pair
1776 quick[node] = pair
1781 # also add the parents of the parents
1777 # also add the parents of the parents
1782 for r in cl.parentrevs(rev):
1778 for r in cl.parentrevs(rev):
1783 if r == nullrev:
1779 if r == nullrev:
1784 continue
1780 continue
1785 n = cl.node(r)
1781 n = cl.node(r)
1786 pair = (r, n)
1782 pair = (r, n)
1787 quick[r] = pair
1783 quick[r] = pair
1788 quick[n] = pair
1784 quick[n] = pair
1789 p1node = self.dirstate.p1()
1785 p1node = self.dirstate.p1()
1790 if p1node != self.nullid:
1786 if p1node != self.nullid:
1791 quick[b'.'] = quick[p1node]
1787 quick[b'.'] = quick[p1node]
1792 return quick
1788 return quick
1793
1789
1794 @unfilteredmethod
1790 @unfilteredmethod
1795 def _quick_access_changeid_invalidate(self):
1791 def _quick_access_changeid_invalidate(self):
1796 if '_quick_access_changeid_wc' in vars(self):
1792 if '_quick_access_changeid_wc' in vars(self):
1797 del self.__dict__['_quick_access_changeid_wc']
1793 del self.__dict__['_quick_access_changeid_wc']
1798
1794
1799 @property
1795 @property
1800 def _quick_access_changeid(self):
1796 def _quick_access_changeid(self):
1801 """an helper dictionnary for __getitem__ calls
1797 """an helper dictionnary for __getitem__ calls
1802
1798
1803 This contains a list of symbol we can recognise right away without
1799 This contains a list of symbol we can recognise right away without
1804 further processing.
1800 further processing.
1805 """
1801 """
1806 if self.filtername in repoview.filter_has_wc:
1802 if self.filtername in repoview.filter_has_wc:
1807 return self._quick_access_changeid_wc
1803 return self._quick_access_changeid_wc
1808 return self._quick_access_changeid_null
1804 return self._quick_access_changeid_null
1809
1805
1810 def __getitem__(self, changeid):
1806 def __getitem__(self, changeid):
1811 # dealing with special cases
1807 # dealing with special cases
1812 if changeid is None:
1808 if changeid is None:
1813 return context.workingctx(self)
1809 return context.workingctx(self)
1814 if isinstance(changeid, context.basectx):
1810 if isinstance(changeid, context.basectx):
1815 return changeid
1811 return changeid
1816
1812
1817 # dealing with multiple revisions
1813 # dealing with multiple revisions
1818 if isinstance(changeid, slice):
1814 if isinstance(changeid, slice):
1819 # wdirrev isn't contiguous so the slice shouldn't include it
1815 # wdirrev isn't contiguous so the slice shouldn't include it
1820 return [
1816 return [
1821 self[i]
1817 self[i]
1822 for i in pycompat.xrange(*changeid.indices(len(self)))
1818 for i in pycompat.xrange(*changeid.indices(len(self)))
1823 if i not in self.changelog.filteredrevs
1819 if i not in self.changelog.filteredrevs
1824 ]
1820 ]
1825
1821
1826 # dealing with some special values
1822 # dealing with some special values
1827 quick_access = self._quick_access_changeid.get(changeid)
1823 quick_access = self._quick_access_changeid.get(changeid)
1828 if quick_access is not None:
1824 if quick_access is not None:
1829 rev, node = quick_access
1825 rev, node = quick_access
1830 return context.changectx(self, rev, node, maybe_filtered=False)
1826 return context.changectx(self, rev, node, maybe_filtered=False)
1831 if changeid == b'tip':
1827 if changeid == b'tip':
1832 node = self.changelog.tip()
1828 node = self.changelog.tip()
1833 rev = self.changelog.rev(node)
1829 rev = self.changelog.rev(node)
1834 return context.changectx(self, rev, node)
1830 return context.changectx(self, rev, node)
1835
1831
1836 # dealing with arbitrary values
1832 # dealing with arbitrary values
1837 try:
1833 try:
1838 if isinstance(changeid, int):
1834 if isinstance(changeid, int):
1839 node = self.changelog.node(changeid)
1835 node = self.changelog.node(changeid)
1840 rev = changeid
1836 rev = changeid
1841 elif changeid == b'.':
1837 elif changeid == b'.':
1842 # this is a hack to delay/avoid loading obsmarkers
1838 # this is a hack to delay/avoid loading obsmarkers
1843 # when we know that '.' won't be hidden
1839 # when we know that '.' won't be hidden
1844 node = self.dirstate.p1()
1840 node = self.dirstate.p1()
1845 rev = self.unfiltered().changelog.rev(node)
1841 rev = self.unfiltered().changelog.rev(node)
1846 elif len(changeid) == self.nodeconstants.nodelen:
1842 elif len(changeid) == self.nodeconstants.nodelen:
1847 try:
1843 try:
1848 node = changeid
1844 node = changeid
1849 rev = self.changelog.rev(changeid)
1845 rev = self.changelog.rev(changeid)
1850 except error.FilteredLookupError:
1846 except error.FilteredLookupError:
1851 changeid = hex(changeid) # for the error message
1847 changeid = hex(changeid) # for the error message
1852 raise
1848 raise
1853 except LookupError:
1849 except LookupError:
1854 # check if it might have come from damaged dirstate
1850 # check if it might have come from damaged dirstate
1855 #
1851 #
1856 # XXX we could avoid the unfiltered if we had a recognizable
1852 # XXX we could avoid the unfiltered if we had a recognizable
1857 # exception for filtered changeset access
1853 # exception for filtered changeset access
1858 if (
1854 if (
1859 self.local()
1855 self.local()
1860 and changeid in self.unfiltered().dirstate.parents()
1856 and changeid in self.unfiltered().dirstate.parents()
1861 ):
1857 ):
1862 msg = _(b"working directory has unknown parent '%s'!")
1858 msg = _(b"working directory has unknown parent '%s'!")
1863 raise error.Abort(msg % short(changeid))
1859 raise error.Abort(msg % short(changeid))
1864 changeid = hex(changeid) # for the error message
1860 changeid = hex(changeid) # for the error message
1865 raise
1861 raise
1866
1862
1867 elif len(changeid) == 2 * self.nodeconstants.nodelen:
1863 elif len(changeid) == 2 * self.nodeconstants.nodelen:
1868 node = bin(changeid)
1864 node = bin(changeid)
1869 rev = self.changelog.rev(node)
1865 rev = self.changelog.rev(node)
1870 else:
1866 else:
1871 raise error.ProgrammingError(
1867 raise error.ProgrammingError(
1872 b"unsupported changeid '%s' of type %s"
1868 b"unsupported changeid '%s' of type %s"
1873 % (changeid, pycompat.bytestr(type(changeid)))
1869 % (changeid, pycompat.bytestr(type(changeid)))
1874 )
1870 )
1875
1871
1876 return context.changectx(self, rev, node)
1872 return context.changectx(self, rev, node)
1877
1873
1878 except (error.FilteredIndexError, error.FilteredLookupError):
1874 except (error.FilteredIndexError, error.FilteredLookupError):
1879 raise error.FilteredRepoLookupError(
1875 raise error.FilteredRepoLookupError(
1880 _(b"filtered revision '%s'") % pycompat.bytestr(changeid)
1876 _(b"filtered revision '%s'") % pycompat.bytestr(changeid)
1881 )
1877 )
1882 except (IndexError, LookupError):
1878 except (IndexError, LookupError):
1883 raise error.RepoLookupError(
1879 raise error.RepoLookupError(
1884 _(b"unknown revision '%s'") % pycompat.bytestr(changeid)
1880 _(b"unknown revision '%s'") % pycompat.bytestr(changeid)
1885 )
1881 )
1886 except error.WdirUnsupported:
1882 except error.WdirUnsupported:
1887 return context.workingctx(self)
1883 return context.workingctx(self)
1888
1884
1889 def __contains__(self, changeid):
1885 def __contains__(self, changeid):
1890 """True if the given changeid exists"""
1886 """True if the given changeid exists"""
1891 try:
1887 try:
1892 self[changeid]
1888 self[changeid]
1893 return True
1889 return True
1894 except error.RepoLookupError:
1890 except error.RepoLookupError:
1895 return False
1891 return False
1896
1892
1897 def __nonzero__(self):
1893 def __nonzero__(self):
1898 return True
1894 return True
1899
1895
1900 __bool__ = __nonzero__
1896 __bool__ = __nonzero__
1901
1897
1902 def __len__(self):
1898 def __len__(self):
1903 # no need to pay the cost of repoview.changelog
1899 # no need to pay the cost of repoview.changelog
1904 unfi = self.unfiltered()
1900 unfi = self.unfiltered()
1905 return len(unfi.changelog)
1901 return len(unfi.changelog)
1906
1902
1907 def __iter__(self):
1903 def __iter__(self):
1908 return iter(self.changelog)
1904 return iter(self.changelog)
1909
1905
1910 def revs(self, expr, *args):
1906 def revs(self, expr, *args):
1911 """Find revisions matching a revset.
1907 """Find revisions matching a revset.
1912
1908
1913 The revset is specified as a string ``expr`` that may contain
1909 The revset is specified as a string ``expr`` that may contain
1914 %-formatting to escape certain types. See ``revsetlang.formatspec``.
1910 %-formatting to escape certain types. See ``revsetlang.formatspec``.
1915
1911
1916 Revset aliases from the configuration are not expanded. To expand
1912 Revset aliases from the configuration are not expanded. To expand
1917 user aliases, consider calling ``scmutil.revrange()`` or
1913 user aliases, consider calling ``scmutil.revrange()`` or
1918 ``repo.anyrevs([expr], user=True)``.
1914 ``repo.anyrevs([expr], user=True)``.
1919
1915
1920 Returns a smartset.abstractsmartset, which is a list-like interface
1916 Returns a smartset.abstractsmartset, which is a list-like interface
1921 that contains integer revisions.
1917 that contains integer revisions.
1922 """
1918 """
1923 tree = revsetlang.spectree(expr, *args)
1919 tree = revsetlang.spectree(expr, *args)
1924 return revset.makematcher(tree)(self)
1920 return revset.makematcher(tree)(self)
1925
1921
1926 def set(self, expr, *args):
1922 def set(self, expr, *args):
1927 """Find revisions matching a revset and emit changectx instances.
1923 """Find revisions matching a revset and emit changectx instances.
1928
1924
1929 This is a convenience wrapper around ``revs()`` that iterates the
1925 This is a convenience wrapper around ``revs()`` that iterates the
1930 result and is a generator of changectx instances.
1926 result and is a generator of changectx instances.
1931
1927
1932 Revset aliases from the configuration are not expanded. To expand
1928 Revset aliases from the configuration are not expanded. To expand
1933 user aliases, consider calling ``scmutil.revrange()``.
1929 user aliases, consider calling ``scmutil.revrange()``.
1934 """
1930 """
1935 for r in self.revs(expr, *args):
1931 for r in self.revs(expr, *args):
1936 yield self[r]
1932 yield self[r]
1937
1933
1938 def anyrevs(self, specs, user=False, localalias=None):
1934 def anyrevs(self, specs, user=False, localalias=None):
1939 """Find revisions matching one of the given revsets.
1935 """Find revisions matching one of the given revsets.
1940
1936
1941 Revset aliases from the configuration are not expanded by default. To
1937 Revset aliases from the configuration are not expanded by default. To
1942 expand user aliases, specify ``user=True``. To provide some local
1938 expand user aliases, specify ``user=True``. To provide some local
1943 definitions overriding user aliases, set ``localalias`` to
1939 definitions overriding user aliases, set ``localalias`` to
1944 ``{name: definitionstring}``.
1940 ``{name: definitionstring}``.
1945 """
1941 """
1946 if specs == [b'null']:
1942 if specs == [b'null']:
1947 return revset.baseset([nullrev])
1943 return revset.baseset([nullrev])
1948 if specs == [b'.']:
1944 if specs == [b'.']:
1949 quick_data = self._quick_access_changeid.get(b'.')
1945 quick_data = self._quick_access_changeid.get(b'.')
1950 if quick_data is not None:
1946 if quick_data is not None:
1951 return revset.baseset([quick_data[0]])
1947 return revset.baseset([quick_data[0]])
1952 if user:
1948 if user:
1953 m = revset.matchany(
1949 m = revset.matchany(
1954 self.ui,
1950 self.ui,
1955 specs,
1951 specs,
1956 lookup=revset.lookupfn(self),
1952 lookup=revset.lookupfn(self),
1957 localalias=localalias,
1953 localalias=localalias,
1958 )
1954 )
1959 else:
1955 else:
1960 m = revset.matchany(None, specs, localalias=localalias)
1956 m = revset.matchany(None, specs, localalias=localalias)
1961 return m(self)
1957 return m(self)
1962
1958
1963 def url(self):
1959 def url(self):
1964 return b'file:' + self.root
1960 return b'file:' + self.root
1965
1961
1966 def hook(self, name, throw=False, **args):
1962 def hook(self, name, throw=False, **args):
1967 """Call a hook, passing this repo instance.
1963 """Call a hook, passing this repo instance.
1968
1964
1969 This a convenience method to aid invoking hooks. Extensions likely
1965 This a convenience method to aid invoking hooks. Extensions likely
1970 won't call this unless they have registered a custom hook or are
1966 won't call this unless they have registered a custom hook or are
1971 replacing code that is expected to call a hook.
1967 replacing code that is expected to call a hook.
1972 """
1968 """
1973 return hook.hook(self.ui, self, name, throw, **args)
1969 return hook.hook(self.ui, self, name, throw, **args)
1974
1970
1975 @filteredpropertycache
1971 @filteredpropertycache
1976 def _tagscache(self):
1972 def _tagscache(self):
1977 """Returns a tagscache object that contains various tags related
1973 """Returns a tagscache object that contains various tags related
1978 caches."""
1974 caches."""
1979
1975
1980 # This simplifies its cache management by having one decorated
1976 # This simplifies its cache management by having one decorated
1981 # function (this one) and the rest simply fetch things from it.
1977 # function (this one) and the rest simply fetch things from it.
1982 class tagscache(object):
1978 class tagscache(object):
1983 def __init__(self):
1979 def __init__(self):
1984 # These two define the set of tags for this repository. tags
1980 # These two define the set of tags for this repository. tags
1985 # maps tag name to node; tagtypes maps tag name to 'global' or
1981 # maps tag name to node; tagtypes maps tag name to 'global' or
1986 # 'local'. (Global tags are defined by .hgtags across all
1982 # 'local'. (Global tags are defined by .hgtags across all
1987 # heads, and local tags are defined in .hg/localtags.)
1983 # heads, and local tags are defined in .hg/localtags.)
1988 # They constitute the in-memory cache of tags.
1984 # They constitute the in-memory cache of tags.
1989 self.tags = self.tagtypes = None
1985 self.tags = self.tagtypes = None
1990
1986
1991 self.nodetagscache = self.tagslist = None
1987 self.nodetagscache = self.tagslist = None
1992
1988
1993 cache = tagscache()
1989 cache = tagscache()
1994 cache.tags, cache.tagtypes = self._findtags()
1990 cache.tags, cache.tagtypes = self._findtags()
1995
1991
1996 return cache
1992 return cache
1997
1993
1998 def tags(self):
1994 def tags(self):
1999 '''return a mapping of tag to node'''
1995 '''return a mapping of tag to node'''
2000 t = {}
1996 t = {}
2001 if self.changelog.filteredrevs:
1997 if self.changelog.filteredrevs:
2002 tags, tt = self._findtags()
1998 tags, tt = self._findtags()
2003 else:
1999 else:
2004 tags = self._tagscache.tags
2000 tags = self._tagscache.tags
2005 rev = self.changelog.rev
2001 rev = self.changelog.rev
2006 for k, v in pycompat.iteritems(tags):
2002 for k, v in pycompat.iteritems(tags):
2007 try:
2003 try:
2008 # ignore tags to unknown nodes
2004 # ignore tags to unknown nodes
2009 rev(v)
2005 rev(v)
2010 t[k] = v
2006 t[k] = v
2011 except (error.LookupError, ValueError):
2007 except (error.LookupError, ValueError):
2012 pass
2008 pass
2013 return t
2009 return t
2014
2010
2015 def _findtags(self):
2011 def _findtags(self):
2016 """Do the hard work of finding tags. Return a pair of dicts
2012 """Do the hard work of finding tags. Return a pair of dicts
2017 (tags, tagtypes) where tags maps tag name to node, and tagtypes
2013 (tags, tagtypes) where tags maps tag name to node, and tagtypes
2018 maps tag name to a string like \'global\' or \'local\'.
2014 maps tag name to a string like \'global\' or \'local\'.
2019 Subclasses or extensions are free to add their own tags, but
2015 Subclasses or extensions are free to add their own tags, but
2020 should be aware that the returned dicts will be retained for the
2016 should be aware that the returned dicts will be retained for the
2021 duration of the localrepo object."""
2017 duration of the localrepo object."""
2022
2018
2023 # XXX what tagtype should subclasses/extensions use? Currently
2019 # XXX what tagtype should subclasses/extensions use? Currently
2024 # mq and bookmarks add tags, but do not set the tagtype at all.
2020 # mq and bookmarks add tags, but do not set the tagtype at all.
2025 # Should each extension invent its own tag type? Should there
2021 # Should each extension invent its own tag type? Should there
2026 # be one tagtype for all such "virtual" tags? Or is the status
2022 # be one tagtype for all such "virtual" tags? Or is the status
2027 # quo fine?
2023 # quo fine?
2028
2024
2029 # map tag name to (node, hist)
2025 # map tag name to (node, hist)
2030 alltags = tagsmod.findglobaltags(self.ui, self)
2026 alltags = tagsmod.findglobaltags(self.ui, self)
2031 # map tag name to tag type
2027 # map tag name to tag type
2032 tagtypes = {tag: b'global' for tag in alltags}
2028 tagtypes = {tag: b'global' for tag in alltags}
2033
2029
2034 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
2030 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
2035
2031
2036 # Build the return dicts. Have to re-encode tag names because
2032 # Build the return dicts. Have to re-encode tag names because
2037 # the tags module always uses UTF-8 (in order not to lose info
2033 # the tags module always uses UTF-8 (in order not to lose info
2038 # writing to the cache), but the rest of Mercurial wants them in
2034 # writing to the cache), but the rest of Mercurial wants them in
2039 # local encoding.
2035 # local encoding.
2040 tags = {}
2036 tags = {}
2041 for (name, (node, hist)) in pycompat.iteritems(alltags):
2037 for (name, (node, hist)) in pycompat.iteritems(alltags):
2042 if node != self.nullid:
2038 if node != self.nullid:
2043 tags[encoding.tolocal(name)] = node
2039 tags[encoding.tolocal(name)] = node
2044 tags[b'tip'] = self.changelog.tip()
2040 tags[b'tip'] = self.changelog.tip()
2045 tagtypes = {
2041 tagtypes = {
2046 encoding.tolocal(name): value
2042 encoding.tolocal(name): value
2047 for (name, value) in pycompat.iteritems(tagtypes)
2043 for (name, value) in pycompat.iteritems(tagtypes)
2048 }
2044 }
2049 return (tags, tagtypes)
2045 return (tags, tagtypes)
2050
2046
2051 def tagtype(self, tagname):
2047 def tagtype(self, tagname):
2052 """
2048 """
2053 return the type of the given tag. result can be:
2049 return the type of the given tag. result can be:
2054
2050
2055 'local' : a local tag
2051 'local' : a local tag
2056 'global' : a global tag
2052 'global' : a global tag
2057 None : tag does not exist
2053 None : tag does not exist
2058 """
2054 """
2059
2055
2060 return self._tagscache.tagtypes.get(tagname)
2056 return self._tagscache.tagtypes.get(tagname)
2061
2057
2062 def tagslist(self):
2058 def tagslist(self):
2063 '''return a list of tags ordered by revision'''
2059 '''return a list of tags ordered by revision'''
2064 if not self._tagscache.tagslist:
2060 if not self._tagscache.tagslist:
2065 l = []
2061 l = []
2066 for t, n in pycompat.iteritems(self.tags()):
2062 for t, n in pycompat.iteritems(self.tags()):
2067 l.append((self.changelog.rev(n), t, n))
2063 l.append((self.changelog.rev(n), t, n))
2068 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
2064 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
2069
2065
2070 return self._tagscache.tagslist
2066 return self._tagscache.tagslist
2071
2067
2072 def nodetags(self, node):
2068 def nodetags(self, node):
2073 '''return the tags associated with a node'''
2069 '''return the tags associated with a node'''
2074 if not self._tagscache.nodetagscache:
2070 if not self._tagscache.nodetagscache:
2075 nodetagscache = {}
2071 nodetagscache = {}
2076 for t, n in pycompat.iteritems(self._tagscache.tags):
2072 for t, n in pycompat.iteritems(self._tagscache.tags):
2077 nodetagscache.setdefault(n, []).append(t)
2073 nodetagscache.setdefault(n, []).append(t)
2078 for tags in pycompat.itervalues(nodetagscache):
2074 for tags in pycompat.itervalues(nodetagscache):
2079 tags.sort()
2075 tags.sort()
2080 self._tagscache.nodetagscache = nodetagscache
2076 self._tagscache.nodetagscache = nodetagscache
2081 return self._tagscache.nodetagscache.get(node, [])
2077 return self._tagscache.nodetagscache.get(node, [])
2082
2078
2083 def nodebookmarks(self, node):
2079 def nodebookmarks(self, node):
2084 """return the list of bookmarks pointing to the specified node"""
2080 """return the list of bookmarks pointing to the specified node"""
2085 return self._bookmarks.names(node)
2081 return self._bookmarks.names(node)
2086
2082
2087 def branchmap(self):
2083 def branchmap(self):
2088 """returns a dictionary {branch: [branchheads]} with branchheads
2084 """returns a dictionary {branch: [branchheads]} with branchheads
2089 ordered by increasing revision number"""
2085 ordered by increasing revision number"""
2090 return self._branchcaches[self]
2086 return self._branchcaches[self]
2091
2087
2092 @unfilteredmethod
2088 @unfilteredmethod
2093 def revbranchcache(self):
2089 def revbranchcache(self):
2094 if not self._revbranchcache:
2090 if not self._revbranchcache:
2095 self._revbranchcache = branchmap.revbranchcache(self.unfiltered())
2091 self._revbranchcache = branchmap.revbranchcache(self.unfiltered())
2096 return self._revbranchcache
2092 return self._revbranchcache
2097
2093
2098 def register_changeset(self, rev, changelogrevision):
2094 def register_changeset(self, rev, changelogrevision):
2099 self.revbranchcache().setdata(rev, changelogrevision)
2095 self.revbranchcache().setdata(rev, changelogrevision)
2100
2096
2101 def branchtip(self, branch, ignoremissing=False):
2097 def branchtip(self, branch, ignoremissing=False):
2102 """return the tip node for a given branch
2098 """return the tip node for a given branch
2103
2099
2104 If ignoremissing is True, then this method will not raise an error.
2100 If ignoremissing is True, then this method will not raise an error.
2105 This is helpful for callers that only expect None for a missing branch
2101 This is helpful for callers that only expect None for a missing branch
2106 (e.g. namespace).
2102 (e.g. namespace).
2107
2103
2108 """
2104 """
2109 try:
2105 try:
2110 return self.branchmap().branchtip(branch)
2106 return self.branchmap().branchtip(branch)
2111 except KeyError:
2107 except KeyError:
2112 if not ignoremissing:
2108 if not ignoremissing:
2113 raise error.RepoLookupError(_(b"unknown branch '%s'") % branch)
2109 raise error.RepoLookupError(_(b"unknown branch '%s'") % branch)
2114 else:
2110 else:
2115 pass
2111 pass
2116
2112
2117 def lookup(self, key):
2113 def lookup(self, key):
2118 node = scmutil.revsymbol(self, key).node()
2114 node = scmutil.revsymbol(self, key).node()
2119 if node is None:
2115 if node is None:
2120 raise error.RepoLookupError(_(b"unknown revision '%s'") % key)
2116 raise error.RepoLookupError(_(b"unknown revision '%s'") % key)
2121 return node
2117 return node
2122
2118
2123 def lookupbranch(self, key):
2119 def lookupbranch(self, key):
2124 if self.branchmap().hasbranch(key):
2120 if self.branchmap().hasbranch(key):
2125 return key
2121 return key
2126
2122
2127 return scmutil.revsymbol(self, key).branch()
2123 return scmutil.revsymbol(self, key).branch()
2128
2124
2129 def known(self, nodes):
2125 def known(self, nodes):
2130 cl = self.changelog
2126 cl = self.changelog
2131 get_rev = cl.index.get_rev
2127 get_rev = cl.index.get_rev
2132 filtered = cl.filteredrevs
2128 filtered = cl.filteredrevs
2133 result = []
2129 result = []
2134 for n in nodes:
2130 for n in nodes:
2135 r = get_rev(n)
2131 r = get_rev(n)
2136 resp = not (r is None or r in filtered)
2132 resp = not (r is None or r in filtered)
2137 result.append(resp)
2133 result.append(resp)
2138 return result
2134 return result
2139
2135
2140 def local(self):
2136 def local(self):
2141 return self
2137 return self
2142
2138
2143 def publishing(self):
2139 def publishing(self):
2144 # it's safe (and desirable) to trust the publish flag unconditionally
2140 # it's safe (and desirable) to trust the publish flag unconditionally
2145 # so that we don't finalize changes shared between users via ssh or nfs
2141 # so that we don't finalize changes shared between users via ssh or nfs
2146 return self.ui.configbool(b'phases', b'publish', untrusted=True)
2142 return self.ui.configbool(b'phases', b'publish', untrusted=True)
2147
2143
2148 def cancopy(self):
2144 def cancopy(self):
2149 # so statichttprepo's override of local() works
2145 # so statichttprepo's override of local() works
2150 if not self.local():
2146 if not self.local():
2151 return False
2147 return False
2152 if not self.publishing():
2148 if not self.publishing():
2153 return True
2149 return True
2154 # if publishing we can't copy if there is filtered content
2150 # if publishing we can't copy if there is filtered content
2155 return not self.filtered(b'visible').changelog.filteredrevs
2151 return not self.filtered(b'visible').changelog.filteredrevs
2156
2152
2157 def shared(self):
2153 def shared(self):
2158 '''the type of shared repository (None if not shared)'''
2154 '''the type of shared repository (None if not shared)'''
2159 if self.sharedpath != self.path:
2155 if self.sharedpath != self.path:
2160 return b'store'
2156 return b'store'
2161 return None
2157 return None
2162
2158
2163 def wjoin(self, f, *insidef):
2159 def wjoin(self, f, *insidef):
2164 return self.vfs.reljoin(self.root, f, *insidef)
2160 return self.vfs.reljoin(self.root, f, *insidef)
2165
2161
2166 def setparents(self, p1, p2=None):
2162 def setparents(self, p1, p2=None):
2167 if p2 is None:
2163 if p2 is None:
2168 p2 = self.nullid
2164 p2 = self.nullid
2169 self[None].setparents(p1, p2)
2165 self[None].setparents(p1, p2)
2170 self._quick_access_changeid_invalidate()
2166 self._quick_access_changeid_invalidate()
2171
2167
2172 def filectx(self, path, changeid=None, fileid=None, changectx=None):
2168 def filectx(self, path, changeid=None, fileid=None, changectx=None):
2173 """changeid must be a changeset revision, if specified.
2169 """changeid must be a changeset revision, if specified.
2174 fileid can be a file revision or node."""
2170 fileid can be a file revision or node."""
2175 return context.filectx(
2171 return context.filectx(
2176 self, path, changeid, fileid, changectx=changectx
2172 self, path, changeid, fileid, changectx=changectx
2177 )
2173 )
2178
2174
2179 def getcwd(self):
2175 def getcwd(self):
2180 return self.dirstate.getcwd()
2176 return self.dirstate.getcwd()
2181
2177
2182 def pathto(self, f, cwd=None):
2178 def pathto(self, f, cwd=None):
2183 return self.dirstate.pathto(f, cwd)
2179 return self.dirstate.pathto(f, cwd)
2184
2180
2185 def _loadfilter(self, filter):
2181 def _loadfilter(self, filter):
2186 if filter not in self._filterpats:
2182 if filter not in self._filterpats:
2187 l = []
2183 l = []
2188 for pat, cmd in self.ui.configitems(filter):
2184 for pat, cmd in self.ui.configitems(filter):
2189 if cmd == b'!':
2185 if cmd == b'!':
2190 continue
2186 continue
2191 mf = matchmod.match(self.root, b'', [pat])
2187 mf = matchmod.match(self.root, b'', [pat])
2192 fn = None
2188 fn = None
2193 params = cmd
2189 params = cmd
2194 for name, filterfn in pycompat.iteritems(self._datafilters):
2190 for name, filterfn in pycompat.iteritems(self._datafilters):
2195 if cmd.startswith(name):
2191 if cmd.startswith(name):
2196 fn = filterfn
2192 fn = filterfn
2197 params = cmd[len(name) :].lstrip()
2193 params = cmd[len(name) :].lstrip()
2198 break
2194 break
2199 if not fn:
2195 if not fn:
2200 fn = lambda s, c, **kwargs: procutil.filter(s, c)
2196 fn = lambda s, c, **kwargs: procutil.filter(s, c)
2201 fn.__name__ = 'commandfilter'
2197 fn.__name__ = 'commandfilter'
2202 # Wrap old filters not supporting keyword arguments
2198 # Wrap old filters not supporting keyword arguments
2203 if not pycompat.getargspec(fn)[2]:
2199 if not pycompat.getargspec(fn)[2]:
2204 oldfn = fn
2200 oldfn = fn
2205 fn = lambda s, c, oldfn=oldfn, **kwargs: oldfn(s, c)
2201 fn = lambda s, c, oldfn=oldfn, **kwargs: oldfn(s, c)
2206 fn.__name__ = 'compat-' + oldfn.__name__
2202 fn.__name__ = 'compat-' + oldfn.__name__
2207 l.append((mf, fn, params))
2203 l.append((mf, fn, params))
2208 self._filterpats[filter] = l
2204 self._filterpats[filter] = l
2209 return self._filterpats[filter]
2205 return self._filterpats[filter]
2210
2206
2211 def _filter(self, filterpats, filename, data):
2207 def _filter(self, filterpats, filename, data):
2212 for mf, fn, cmd in filterpats:
2208 for mf, fn, cmd in filterpats:
2213 if mf(filename):
2209 if mf(filename):
2214 self.ui.debug(
2210 self.ui.debug(
2215 b"filtering %s through %s\n"
2211 b"filtering %s through %s\n"
2216 % (filename, cmd or pycompat.sysbytes(fn.__name__))
2212 % (filename, cmd or pycompat.sysbytes(fn.__name__))
2217 )
2213 )
2218 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
2214 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
2219 break
2215 break
2220
2216
2221 return data
2217 return data
2222
2218
2223 @unfilteredpropertycache
2219 @unfilteredpropertycache
2224 def _encodefilterpats(self):
2220 def _encodefilterpats(self):
2225 return self._loadfilter(b'encode')
2221 return self._loadfilter(b'encode')
2226
2222
2227 @unfilteredpropertycache
2223 @unfilteredpropertycache
2228 def _decodefilterpats(self):
2224 def _decodefilterpats(self):
2229 return self._loadfilter(b'decode')
2225 return self._loadfilter(b'decode')
2230
2226
2231 def adddatafilter(self, name, filter):
2227 def adddatafilter(self, name, filter):
2232 self._datafilters[name] = filter
2228 self._datafilters[name] = filter
2233
2229
2234 def wread(self, filename):
2230 def wread(self, filename):
2235 if self.wvfs.islink(filename):
2231 if self.wvfs.islink(filename):
2236 data = self.wvfs.readlink(filename)
2232 data = self.wvfs.readlink(filename)
2237 else:
2233 else:
2238 data = self.wvfs.read(filename)
2234 data = self.wvfs.read(filename)
2239 return self._filter(self._encodefilterpats, filename, data)
2235 return self._filter(self._encodefilterpats, filename, data)
2240
2236
2241 def wwrite(self, filename, data, flags, backgroundclose=False, **kwargs):
2237 def wwrite(self, filename, data, flags, backgroundclose=False, **kwargs):
2242 """write ``data`` into ``filename`` in the working directory
2238 """write ``data`` into ``filename`` in the working directory
2243
2239
2244 This returns length of written (maybe decoded) data.
2240 This returns length of written (maybe decoded) data.
2245 """
2241 """
2246 data = self._filter(self._decodefilterpats, filename, data)
2242 data = self._filter(self._decodefilterpats, filename, data)
2247 if b'l' in flags:
2243 if b'l' in flags:
2248 self.wvfs.symlink(data, filename)
2244 self.wvfs.symlink(data, filename)
2249 else:
2245 else:
2250 self.wvfs.write(
2246 self.wvfs.write(
2251 filename, data, backgroundclose=backgroundclose, **kwargs
2247 filename, data, backgroundclose=backgroundclose, **kwargs
2252 )
2248 )
2253 if b'x' in flags:
2249 if b'x' in flags:
2254 self.wvfs.setflags(filename, False, True)
2250 self.wvfs.setflags(filename, False, True)
2255 else:
2251 else:
2256 self.wvfs.setflags(filename, False, False)
2252 self.wvfs.setflags(filename, False, False)
2257 return len(data)
2253 return len(data)
2258
2254
2259 def wwritedata(self, filename, data):
2255 def wwritedata(self, filename, data):
2260 return self._filter(self._decodefilterpats, filename, data)
2256 return self._filter(self._decodefilterpats, filename, data)
2261
2257
2262 def currenttransaction(self):
2258 def currenttransaction(self):
2263 """return the current transaction or None if non exists"""
2259 """return the current transaction or None if non exists"""
2264 if self._transref:
2260 if self._transref:
2265 tr = self._transref()
2261 tr = self._transref()
2266 else:
2262 else:
2267 tr = None
2263 tr = None
2268
2264
2269 if tr and tr.running():
2265 if tr and tr.running():
2270 return tr
2266 return tr
2271 return None
2267 return None
2272
2268
2273 def transaction(self, desc, report=None):
2269 def transaction(self, desc, report=None):
2274 if self.ui.configbool(b'devel', b'all-warnings') or self.ui.configbool(
2270 if self.ui.configbool(b'devel', b'all-warnings') or self.ui.configbool(
2275 b'devel', b'check-locks'
2271 b'devel', b'check-locks'
2276 ):
2272 ):
2277 if self._currentlock(self._lockref) is None:
2273 if self._currentlock(self._lockref) is None:
2278 raise error.ProgrammingError(b'transaction requires locking')
2274 raise error.ProgrammingError(b'transaction requires locking')
2279 tr = self.currenttransaction()
2275 tr = self.currenttransaction()
2280 if tr is not None:
2276 if tr is not None:
2281 return tr.nest(name=desc)
2277 return tr.nest(name=desc)
2282
2278
2283 # abort here if the journal already exists
2279 # abort here if the journal already exists
2284 if self.svfs.exists(b"journal"):
2280 if self.svfs.exists(b"journal"):
2285 raise error.RepoError(
2281 raise error.RepoError(
2286 _(b"abandoned transaction found"),
2282 _(b"abandoned transaction found"),
2287 hint=_(b"run 'hg recover' to clean up transaction"),
2283 hint=_(b"run 'hg recover' to clean up transaction"),
2288 )
2284 )
2289
2285
2290 idbase = b"%.40f#%f" % (random.random(), time.time())
2286 idbase = b"%.40f#%f" % (random.random(), time.time())
2291 ha = hex(hashutil.sha1(idbase).digest())
2287 ha = hex(hashutil.sha1(idbase).digest())
2292 txnid = b'TXN:' + ha
2288 txnid = b'TXN:' + ha
2293 self.hook(b'pretxnopen', throw=True, txnname=desc, txnid=txnid)
2289 self.hook(b'pretxnopen', throw=True, txnname=desc, txnid=txnid)
2294
2290
2295 self._writejournal(desc)
2291 self._writejournal(desc)
2296 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
2292 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
2297 if report:
2293 if report:
2298 rp = report
2294 rp = report
2299 else:
2295 else:
2300 rp = self.ui.warn
2296 rp = self.ui.warn
2301 vfsmap = {b'plain': self.vfs, b'store': self.svfs} # root of .hg/
2297 vfsmap = {b'plain': self.vfs, b'store': self.svfs} # root of .hg/
2302 # we must avoid cyclic reference between repo and transaction.
2298 # we must avoid cyclic reference between repo and transaction.
2303 reporef = weakref.ref(self)
2299 reporef = weakref.ref(self)
2304 # Code to track tag movement
2300 # Code to track tag movement
2305 #
2301 #
2306 # Since tags are all handled as file content, it is actually quite hard
2302 # Since tags are all handled as file content, it is actually quite hard
2307 # to track these movement from a code perspective. So we fallback to a
2303 # to track these movement from a code perspective. So we fallback to a
2308 # tracking at the repository level. One could envision to track changes
2304 # tracking at the repository level. One could envision to track changes
2309 # to the '.hgtags' file through changegroup apply but that fails to
2305 # to the '.hgtags' file through changegroup apply but that fails to
2310 # cope with case where transaction expose new heads without changegroup
2306 # cope with case where transaction expose new heads without changegroup
2311 # being involved (eg: phase movement).
2307 # being involved (eg: phase movement).
2312 #
2308 #
2313 # For now, We gate the feature behind a flag since this likely comes
2309 # For now, We gate the feature behind a flag since this likely comes
2314 # with performance impacts. The current code run more often than needed
2310 # with performance impacts. The current code run more often than needed
2315 # and do not use caches as much as it could. The current focus is on
2311 # and do not use caches as much as it could. The current focus is on
2316 # the behavior of the feature so we disable it by default. The flag
2312 # the behavior of the feature so we disable it by default. The flag
2317 # will be removed when we are happy with the performance impact.
2313 # will be removed when we are happy with the performance impact.
2318 #
2314 #
2319 # Once this feature is no longer experimental move the following
2315 # Once this feature is no longer experimental move the following
2320 # documentation to the appropriate help section:
2316 # documentation to the appropriate help section:
2321 #
2317 #
2322 # The ``HG_TAG_MOVED`` variable will be set if the transaction touched
2318 # The ``HG_TAG_MOVED`` variable will be set if the transaction touched
2323 # tags (new or changed or deleted tags). In addition the details of
2319 # tags (new or changed or deleted tags). In addition the details of
2324 # these changes are made available in a file at:
2320 # these changes are made available in a file at:
2325 # ``REPOROOT/.hg/changes/tags.changes``.
2321 # ``REPOROOT/.hg/changes/tags.changes``.
2326 # Make sure you check for HG_TAG_MOVED before reading that file as it
2322 # Make sure you check for HG_TAG_MOVED before reading that file as it
2327 # might exist from a previous transaction even if no tag were touched
2323 # might exist from a previous transaction even if no tag were touched
2328 # in this one. Changes are recorded in a line base format::
2324 # in this one. Changes are recorded in a line base format::
2329 #
2325 #
2330 # <action> <hex-node> <tag-name>\n
2326 # <action> <hex-node> <tag-name>\n
2331 #
2327 #
2332 # Actions are defined as follow:
2328 # Actions are defined as follow:
2333 # "-R": tag is removed,
2329 # "-R": tag is removed,
2334 # "+A": tag is added,
2330 # "+A": tag is added,
2335 # "-M": tag is moved (old value),
2331 # "-M": tag is moved (old value),
2336 # "+M": tag is moved (new value),
2332 # "+M": tag is moved (new value),
2337 tracktags = lambda x: None
2333 tracktags = lambda x: None
2338 # experimental config: experimental.hook-track-tags
2334 # experimental config: experimental.hook-track-tags
2339 shouldtracktags = self.ui.configbool(
2335 shouldtracktags = self.ui.configbool(
2340 b'experimental', b'hook-track-tags'
2336 b'experimental', b'hook-track-tags'
2341 )
2337 )
2342 if desc != b'strip' and shouldtracktags:
2338 if desc != b'strip' and shouldtracktags:
2343 oldheads = self.changelog.headrevs()
2339 oldheads = self.changelog.headrevs()
2344
2340
2345 def tracktags(tr2):
2341 def tracktags(tr2):
2346 repo = reporef()
2342 repo = reporef()
2347 assert repo is not None # help pytype
2343 assert repo is not None # help pytype
2348 oldfnodes = tagsmod.fnoderevs(repo.ui, repo, oldheads)
2344 oldfnodes = tagsmod.fnoderevs(repo.ui, repo, oldheads)
2349 newheads = repo.changelog.headrevs()
2345 newheads = repo.changelog.headrevs()
2350 newfnodes = tagsmod.fnoderevs(repo.ui, repo, newheads)
2346 newfnodes = tagsmod.fnoderevs(repo.ui, repo, newheads)
2351 # notes: we compare lists here.
2347 # notes: we compare lists here.
2352 # As we do it only once buiding set would not be cheaper
2348 # As we do it only once buiding set would not be cheaper
2353 changes = tagsmod.difftags(repo.ui, repo, oldfnodes, newfnodes)
2349 changes = tagsmod.difftags(repo.ui, repo, oldfnodes, newfnodes)
2354 if changes:
2350 if changes:
2355 tr2.hookargs[b'tag_moved'] = b'1'
2351 tr2.hookargs[b'tag_moved'] = b'1'
2356 with repo.vfs(
2352 with repo.vfs(
2357 b'changes/tags.changes', b'w', atomictemp=True
2353 b'changes/tags.changes', b'w', atomictemp=True
2358 ) as changesfile:
2354 ) as changesfile:
2359 # note: we do not register the file to the transaction
2355 # note: we do not register the file to the transaction
2360 # because we needs it to still exist on the transaction
2356 # because we needs it to still exist on the transaction
2361 # is close (for txnclose hooks)
2357 # is close (for txnclose hooks)
2362 tagsmod.writediff(changesfile, changes)
2358 tagsmod.writediff(changesfile, changes)
2363
2359
2364 def validate(tr2):
2360 def validate(tr2):
2365 """will run pre-closing hooks"""
2361 """will run pre-closing hooks"""
2366 # XXX the transaction API is a bit lacking here so we take a hacky
2362 # XXX the transaction API is a bit lacking here so we take a hacky
2367 # path for now
2363 # path for now
2368 #
2364 #
2369 # We cannot add this as a "pending" hooks since the 'tr.hookargs'
2365 # We cannot add this as a "pending" hooks since the 'tr.hookargs'
2370 # dict is copied before these run. In addition we needs the data
2366 # dict is copied before these run. In addition we needs the data
2371 # available to in memory hooks too.
2367 # available to in memory hooks too.
2372 #
2368 #
2373 # Moreover, we also need to make sure this runs before txnclose
2369 # Moreover, we also need to make sure this runs before txnclose
2374 # hooks and there is no "pending" mechanism that would execute
2370 # hooks and there is no "pending" mechanism that would execute
2375 # logic only if hooks are about to run.
2371 # logic only if hooks are about to run.
2376 #
2372 #
2377 # Fixing this limitation of the transaction is also needed to track
2373 # Fixing this limitation of the transaction is also needed to track
2378 # other families of changes (bookmarks, phases, obsolescence).
2374 # other families of changes (bookmarks, phases, obsolescence).
2379 #
2375 #
2380 # This will have to be fixed before we remove the experimental
2376 # This will have to be fixed before we remove the experimental
2381 # gating.
2377 # gating.
2382 tracktags(tr2)
2378 tracktags(tr2)
2383 repo = reporef()
2379 repo = reporef()
2384 assert repo is not None # help pytype
2380 assert repo is not None # help pytype
2385
2381
2386 singleheadopt = (b'experimental', b'single-head-per-branch')
2382 singleheadopt = (b'experimental', b'single-head-per-branch')
2387 singlehead = repo.ui.configbool(*singleheadopt)
2383 singlehead = repo.ui.configbool(*singleheadopt)
2388 if singlehead:
2384 if singlehead:
2389 singleheadsub = repo.ui.configsuboptions(*singleheadopt)[1]
2385 singleheadsub = repo.ui.configsuboptions(*singleheadopt)[1]
2390 accountclosed = singleheadsub.get(
2386 accountclosed = singleheadsub.get(
2391 b"account-closed-heads", False
2387 b"account-closed-heads", False
2392 )
2388 )
2393 if singleheadsub.get(b"public-changes-only", False):
2389 if singleheadsub.get(b"public-changes-only", False):
2394 filtername = b"immutable"
2390 filtername = b"immutable"
2395 else:
2391 else:
2396 filtername = b"visible"
2392 filtername = b"visible"
2397 scmutil.enforcesinglehead(
2393 scmutil.enforcesinglehead(
2398 repo, tr2, desc, accountclosed, filtername
2394 repo, tr2, desc, accountclosed, filtername
2399 )
2395 )
2400 if hook.hashook(repo.ui, b'pretxnclose-bookmark'):
2396 if hook.hashook(repo.ui, b'pretxnclose-bookmark'):
2401 for name, (old, new) in sorted(
2397 for name, (old, new) in sorted(
2402 tr.changes[b'bookmarks'].items()
2398 tr.changes[b'bookmarks'].items()
2403 ):
2399 ):
2404 args = tr.hookargs.copy()
2400 args = tr.hookargs.copy()
2405 args.update(bookmarks.preparehookargs(name, old, new))
2401 args.update(bookmarks.preparehookargs(name, old, new))
2406 repo.hook(
2402 repo.hook(
2407 b'pretxnclose-bookmark',
2403 b'pretxnclose-bookmark',
2408 throw=True,
2404 throw=True,
2409 **pycompat.strkwargs(args)
2405 **pycompat.strkwargs(args)
2410 )
2406 )
2411 if hook.hashook(repo.ui, b'pretxnclose-phase'):
2407 if hook.hashook(repo.ui, b'pretxnclose-phase'):
2412 cl = repo.unfiltered().changelog
2408 cl = repo.unfiltered().changelog
2413 for revs, (old, new) in tr.changes[b'phases']:
2409 for revs, (old, new) in tr.changes[b'phases']:
2414 for rev in revs:
2410 for rev in revs:
2415 args = tr.hookargs.copy()
2411 args = tr.hookargs.copy()
2416 node = hex(cl.node(rev))
2412 node = hex(cl.node(rev))
2417 args.update(phases.preparehookargs(node, old, new))
2413 args.update(phases.preparehookargs(node, old, new))
2418 repo.hook(
2414 repo.hook(
2419 b'pretxnclose-phase',
2415 b'pretxnclose-phase',
2420 throw=True,
2416 throw=True,
2421 **pycompat.strkwargs(args)
2417 **pycompat.strkwargs(args)
2422 )
2418 )
2423
2419
2424 repo.hook(
2420 repo.hook(
2425 b'pretxnclose', throw=True, **pycompat.strkwargs(tr.hookargs)
2421 b'pretxnclose', throw=True, **pycompat.strkwargs(tr.hookargs)
2426 )
2422 )
2427
2423
2428 def releasefn(tr, success):
2424 def releasefn(tr, success):
2429 repo = reporef()
2425 repo = reporef()
2430 if repo is None:
2426 if repo is None:
2431 # If the repo has been GC'd (and this release function is being
2427 # If the repo has been GC'd (and this release function is being
2432 # called from transaction.__del__), there's not much we can do,
2428 # called from transaction.__del__), there's not much we can do,
2433 # so just leave the unfinished transaction there and let the
2429 # so just leave the unfinished transaction there and let the
2434 # user run `hg recover`.
2430 # user run `hg recover`.
2435 return
2431 return
2436 if success:
2432 if success:
2437 # this should be explicitly invoked here, because
2433 # this should be explicitly invoked here, because
2438 # in-memory changes aren't written out at closing
2434 # in-memory changes aren't written out at closing
2439 # transaction, if tr.addfilegenerator (via
2435 # transaction, if tr.addfilegenerator (via
2440 # dirstate.write or so) isn't invoked while
2436 # dirstate.write or so) isn't invoked while
2441 # transaction running
2437 # transaction running
2442 repo.dirstate.write(None)
2438 repo.dirstate.write(None)
2443 else:
2439 else:
2444 # discard all changes (including ones already written
2440 # discard all changes (including ones already written
2445 # out) in this transaction
2441 # out) in this transaction
2446 narrowspec.restorebackup(self, b'journal.narrowspec')
2442 narrowspec.restorebackup(self, b'journal.narrowspec')
2447 narrowspec.restorewcbackup(self, b'journal.narrowspec.dirstate')
2443 narrowspec.restorewcbackup(self, b'journal.narrowspec.dirstate')
2448 repo.dirstate.restorebackup(None, b'journal.dirstate')
2444 repo.dirstate.restorebackup(None, b'journal.dirstate')
2449
2445
2450 repo.invalidate(clearfilecache=True)
2446 repo.invalidate(clearfilecache=True)
2451
2447
2452 tr = transaction.transaction(
2448 tr = transaction.transaction(
2453 rp,
2449 rp,
2454 self.svfs,
2450 self.svfs,
2455 vfsmap,
2451 vfsmap,
2456 b"journal",
2452 b"journal",
2457 b"undo",
2453 b"undo",
2458 aftertrans(renames),
2454 aftertrans(renames),
2459 self.store.createmode,
2455 self.store.createmode,
2460 validator=validate,
2456 validator=validate,
2461 releasefn=releasefn,
2457 releasefn=releasefn,
2462 checkambigfiles=_cachedfiles,
2458 checkambigfiles=_cachedfiles,
2463 name=desc,
2459 name=desc,
2464 )
2460 )
2465 tr.changes[b'origrepolen'] = len(self)
2461 tr.changes[b'origrepolen'] = len(self)
2466 tr.changes[b'obsmarkers'] = set()
2462 tr.changes[b'obsmarkers'] = set()
2467 tr.changes[b'phases'] = []
2463 tr.changes[b'phases'] = []
2468 tr.changes[b'bookmarks'] = {}
2464 tr.changes[b'bookmarks'] = {}
2469
2465
2470 tr.hookargs[b'txnid'] = txnid
2466 tr.hookargs[b'txnid'] = txnid
2471 tr.hookargs[b'txnname'] = desc
2467 tr.hookargs[b'txnname'] = desc
2472 tr.hookargs[b'changes'] = tr.changes
2468 tr.hookargs[b'changes'] = tr.changes
2473 # note: writing the fncache only during finalize mean that the file is
2469 # note: writing the fncache only during finalize mean that the file is
2474 # outdated when running hooks. As fncache is used for streaming clone,
2470 # outdated when running hooks. As fncache is used for streaming clone,
2475 # this is not expected to break anything that happen during the hooks.
2471 # this is not expected to break anything that happen during the hooks.
2476 tr.addfinalize(b'flush-fncache', self.store.write)
2472 tr.addfinalize(b'flush-fncache', self.store.write)
2477
2473
2478 def txnclosehook(tr2):
2474 def txnclosehook(tr2):
2479 """To be run if transaction is successful, will schedule a hook run"""
2475 """To be run if transaction is successful, will schedule a hook run"""
2480 # Don't reference tr2 in hook() so we don't hold a reference.
2476 # Don't reference tr2 in hook() so we don't hold a reference.
2481 # This reduces memory consumption when there are multiple
2477 # This reduces memory consumption when there are multiple
2482 # transactions per lock. This can likely go away if issue5045
2478 # transactions per lock. This can likely go away if issue5045
2483 # fixes the function accumulation.
2479 # fixes the function accumulation.
2484 hookargs = tr2.hookargs
2480 hookargs = tr2.hookargs
2485
2481
2486 def hookfunc(unused_success):
2482 def hookfunc(unused_success):
2487 repo = reporef()
2483 repo = reporef()
2488 assert repo is not None # help pytype
2484 assert repo is not None # help pytype
2489
2485
2490 if hook.hashook(repo.ui, b'txnclose-bookmark'):
2486 if hook.hashook(repo.ui, b'txnclose-bookmark'):
2491 bmchanges = sorted(tr.changes[b'bookmarks'].items())
2487 bmchanges = sorted(tr.changes[b'bookmarks'].items())
2492 for name, (old, new) in bmchanges:
2488 for name, (old, new) in bmchanges:
2493 args = tr.hookargs.copy()
2489 args = tr.hookargs.copy()
2494 args.update(bookmarks.preparehookargs(name, old, new))
2490 args.update(bookmarks.preparehookargs(name, old, new))
2495 repo.hook(
2491 repo.hook(
2496 b'txnclose-bookmark',
2492 b'txnclose-bookmark',
2497 throw=False,
2493 throw=False,
2498 **pycompat.strkwargs(args)
2494 **pycompat.strkwargs(args)
2499 )
2495 )
2500
2496
2501 if hook.hashook(repo.ui, b'txnclose-phase'):
2497 if hook.hashook(repo.ui, b'txnclose-phase'):
2502 cl = repo.unfiltered().changelog
2498 cl = repo.unfiltered().changelog
2503 phasemv = sorted(
2499 phasemv = sorted(
2504 tr.changes[b'phases'], key=lambda r: r[0][0]
2500 tr.changes[b'phases'], key=lambda r: r[0][0]
2505 )
2501 )
2506 for revs, (old, new) in phasemv:
2502 for revs, (old, new) in phasemv:
2507 for rev in revs:
2503 for rev in revs:
2508 args = tr.hookargs.copy()
2504 args = tr.hookargs.copy()
2509 node = hex(cl.node(rev))
2505 node = hex(cl.node(rev))
2510 args.update(phases.preparehookargs(node, old, new))
2506 args.update(phases.preparehookargs(node, old, new))
2511 repo.hook(
2507 repo.hook(
2512 b'txnclose-phase',
2508 b'txnclose-phase',
2513 throw=False,
2509 throw=False,
2514 **pycompat.strkwargs(args)
2510 **pycompat.strkwargs(args)
2515 )
2511 )
2516
2512
2517 repo.hook(
2513 repo.hook(
2518 b'txnclose', throw=False, **pycompat.strkwargs(hookargs)
2514 b'txnclose', throw=False, **pycompat.strkwargs(hookargs)
2519 )
2515 )
2520
2516
2521 repo = reporef()
2517 repo = reporef()
2522 assert repo is not None # help pytype
2518 assert repo is not None # help pytype
2523 repo._afterlock(hookfunc)
2519 repo._afterlock(hookfunc)
2524
2520
2525 tr.addfinalize(b'txnclose-hook', txnclosehook)
2521 tr.addfinalize(b'txnclose-hook', txnclosehook)
2526 # Include a leading "-" to make it happen before the transaction summary
2522 # Include a leading "-" to make it happen before the transaction summary
2527 # reports registered via scmutil.registersummarycallback() whose names
2523 # reports registered via scmutil.registersummarycallback() whose names
2528 # are 00-txnreport etc. That way, the caches will be warm when the
2524 # are 00-txnreport etc. That way, the caches will be warm when the
2529 # callbacks run.
2525 # callbacks run.
2530 tr.addpostclose(b'-warm-cache', self._buildcacheupdater(tr))
2526 tr.addpostclose(b'-warm-cache', self._buildcacheupdater(tr))
2531
2527
2532 def txnaborthook(tr2):
2528 def txnaborthook(tr2):
2533 """To be run if transaction is aborted"""
2529 """To be run if transaction is aborted"""
2534 repo = reporef()
2530 repo = reporef()
2535 assert repo is not None # help pytype
2531 assert repo is not None # help pytype
2536 repo.hook(
2532 repo.hook(
2537 b'txnabort', throw=False, **pycompat.strkwargs(tr2.hookargs)
2533 b'txnabort', throw=False, **pycompat.strkwargs(tr2.hookargs)
2538 )
2534 )
2539
2535
2540 tr.addabort(b'txnabort-hook', txnaborthook)
2536 tr.addabort(b'txnabort-hook', txnaborthook)
2541 # avoid eager cache invalidation. in-memory data should be identical
2537 # avoid eager cache invalidation. in-memory data should be identical
2542 # to stored data if transaction has no error.
2538 # to stored data if transaction has no error.
2543 tr.addpostclose(b'refresh-filecachestats', self._refreshfilecachestats)
2539 tr.addpostclose(b'refresh-filecachestats', self._refreshfilecachestats)
2544 self._transref = weakref.ref(tr)
2540 self._transref = weakref.ref(tr)
2545 scmutil.registersummarycallback(self, tr, desc)
2541 scmutil.registersummarycallback(self, tr, desc)
2546 return tr
2542 return tr
2547
2543
2548 def _journalfiles(self):
2544 def _journalfiles(self):
2549 return (
2545 return (
2550 (self.svfs, b'journal'),
2546 (self.svfs, b'journal'),
2551 (self.svfs, b'journal.narrowspec'),
2547 (self.svfs, b'journal.narrowspec'),
2552 (self.vfs, b'journal.narrowspec.dirstate'),
2548 (self.vfs, b'journal.narrowspec.dirstate'),
2553 (self.vfs, b'journal.dirstate'),
2549 (self.vfs, b'journal.dirstate'),
2554 (self.vfs, b'journal.branch'),
2550 (self.vfs, b'journal.branch'),
2555 (self.vfs, b'journal.desc'),
2551 (self.vfs, b'journal.desc'),
2556 (bookmarks.bookmarksvfs(self), b'journal.bookmarks'),
2552 (bookmarks.bookmarksvfs(self), b'journal.bookmarks'),
2557 (self.svfs, b'journal.phaseroots'),
2553 (self.svfs, b'journal.phaseroots'),
2558 )
2554 )
2559
2555
2560 def undofiles(self):
2556 def undofiles(self):
2561 return [(vfs, undoname(x)) for vfs, x in self._journalfiles()]
2557 return [(vfs, undoname(x)) for vfs, x in self._journalfiles()]
2562
2558
2563 @unfilteredmethod
2559 @unfilteredmethod
2564 def _writejournal(self, desc):
2560 def _writejournal(self, desc):
2565 self.dirstate.savebackup(None, b'journal.dirstate')
2561 self.dirstate.savebackup(None, b'journal.dirstate')
2566 narrowspec.savewcbackup(self, b'journal.narrowspec.dirstate')
2562 narrowspec.savewcbackup(self, b'journal.narrowspec.dirstate')
2567 narrowspec.savebackup(self, b'journal.narrowspec')
2563 narrowspec.savebackup(self, b'journal.narrowspec')
2568 self.vfs.write(
2564 self.vfs.write(
2569 b"journal.branch", encoding.fromlocal(self.dirstate.branch())
2565 b"journal.branch", encoding.fromlocal(self.dirstate.branch())
2570 )
2566 )
2571 self.vfs.write(b"journal.desc", b"%d\n%s\n" % (len(self), desc))
2567 self.vfs.write(b"journal.desc", b"%d\n%s\n" % (len(self), desc))
2572 bookmarksvfs = bookmarks.bookmarksvfs(self)
2568 bookmarksvfs = bookmarks.bookmarksvfs(self)
2573 bookmarksvfs.write(
2569 bookmarksvfs.write(
2574 b"journal.bookmarks", bookmarksvfs.tryread(b"bookmarks")
2570 b"journal.bookmarks", bookmarksvfs.tryread(b"bookmarks")
2575 )
2571 )
2576 self.svfs.write(b"journal.phaseroots", self.svfs.tryread(b"phaseroots"))
2572 self.svfs.write(b"journal.phaseroots", self.svfs.tryread(b"phaseroots"))
2577
2573
2578 def recover(self):
2574 def recover(self):
2579 with self.lock():
2575 with self.lock():
2580 if self.svfs.exists(b"journal"):
2576 if self.svfs.exists(b"journal"):
2581 self.ui.status(_(b"rolling back interrupted transaction\n"))
2577 self.ui.status(_(b"rolling back interrupted transaction\n"))
2582 vfsmap = {
2578 vfsmap = {
2583 b'': self.svfs,
2579 b'': self.svfs,
2584 b'plain': self.vfs,
2580 b'plain': self.vfs,
2585 }
2581 }
2586 transaction.rollback(
2582 transaction.rollback(
2587 self.svfs,
2583 self.svfs,
2588 vfsmap,
2584 vfsmap,
2589 b"journal",
2585 b"journal",
2590 self.ui.warn,
2586 self.ui.warn,
2591 checkambigfiles=_cachedfiles,
2587 checkambigfiles=_cachedfiles,
2592 )
2588 )
2593 self.invalidate()
2589 self.invalidate()
2594 return True
2590 return True
2595 else:
2591 else:
2596 self.ui.warn(_(b"no interrupted transaction available\n"))
2592 self.ui.warn(_(b"no interrupted transaction available\n"))
2597 return False
2593 return False
2598
2594
2599 def rollback(self, dryrun=False, force=False):
2595 def rollback(self, dryrun=False, force=False):
2600 wlock = lock = dsguard = None
2596 wlock = lock = dsguard = None
2601 try:
2597 try:
2602 wlock = self.wlock()
2598 wlock = self.wlock()
2603 lock = self.lock()
2599 lock = self.lock()
2604 if self.svfs.exists(b"undo"):
2600 if self.svfs.exists(b"undo"):
2605 dsguard = dirstateguard.dirstateguard(self, b'rollback')
2601 dsguard = dirstateguard.dirstateguard(self, b'rollback')
2606
2602
2607 return self._rollback(dryrun, force, dsguard)
2603 return self._rollback(dryrun, force, dsguard)
2608 else:
2604 else:
2609 self.ui.warn(_(b"no rollback information available\n"))
2605 self.ui.warn(_(b"no rollback information available\n"))
2610 return 1
2606 return 1
2611 finally:
2607 finally:
2612 release(dsguard, lock, wlock)
2608 release(dsguard, lock, wlock)
2613
2609
2614 @unfilteredmethod # Until we get smarter cache management
2610 @unfilteredmethod # Until we get smarter cache management
2615 def _rollback(self, dryrun, force, dsguard):
2611 def _rollback(self, dryrun, force, dsguard):
2616 ui = self.ui
2612 ui = self.ui
2617 try:
2613 try:
2618 args = self.vfs.read(b'undo.desc').splitlines()
2614 args = self.vfs.read(b'undo.desc').splitlines()
2619 (oldlen, desc, detail) = (int(args[0]), args[1], None)
2615 (oldlen, desc, detail) = (int(args[0]), args[1], None)
2620 if len(args) >= 3:
2616 if len(args) >= 3:
2621 detail = args[2]
2617 detail = args[2]
2622 oldtip = oldlen - 1
2618 oldtip = oldlen - 1
2623
2619
2624 if detail and ui.verbose:
2620 if detail and ui.verbose:
2625 msg = _(
2621 msg = _(
2626 b'repository tip rolled back to revision %d'
2622 b'repository tip rolled back to revision %d'
2627 b' (undo %s: %s)\n'
2623 b' (undo %s: %s)\n'
2628 ) % (oldtip, desc, detail)
2624 ) % (oldtip, desc, detail)
2629 else:
2625 else:
2630 msg = _(
2626 msg = _(
2631 b'repository tip rolled back to revision %d (undo %s)\n'
2627 b'repository tip rolled back to revision %d (undo %s)\n'
2632 ) % (oldtip, desc)
2628 ) % (oldtip, desc)
2633 except IOError:
2629 except IOError:
2634 msg = _(b'rolling back unknown transaction\n')
2630 msg = _(b'rolling back unknown transaction\n')
2635 desc = None
2631 desc = None
2636
2632
2637 if not force and self[b'.'] != self[b'tip'] and desc == b'commit':
2633 if not force and self[b'.'] != self[b'tip'] and desc == b'commit':
2638 raise error.Abort(
2634 raise error.Abort(
2639 _(
2635 _(
2640 b'rollback of last commit while not checked out '
2636 b'rollback of last commit while not checked out '
2641 b'may lose data'
2637 b'may lose data'
2642 ),
2638 ),
2643 hint=_(b'use -f to force'),
2639 hint=_(b'use -f to force'),
2644 )
2640 )
2645
2641
2646 ui.status(msg)
2642 ui.status(msg)
2647 if dryrun:
2643 if dryrun:
2648 return 0
2644 return 0
2649
2645
2650 parents = self.dirstate.parents()
2646 parents = self.dirstate.parents()
2651 self.destroying()
2647 self.destroying()
2652 vfsmap = {b'plain': self.vfs, b'': self.svfs}
2648 vfsmap = {b'plain': self.vfs, b'': self.svfs}
2653 transaction.rollback(
2649 transaction.rollback(
2654 self.svfs, vfsmap, b'undo', ui.warn, checkambigfiles=_cachedfiles
2650 self.svfs, vfsmap, b'undo', ui.warn, checkambigfiles=_cachedfiles
2655 )
2651 )
2656 bookmarksvfs = bookmarks.bookmarksvfs(self)
2652 bookmarksvfs = bookmarks.bookmarksvfs(self)
2657 if bookmarksvfs.exists(b'undo.bookmarks'):
2653 if bookmarksvfs.exists(b'undo.bookmarks'):
2658 bookmarksvfs.rename(
2654 bookmarksvfs.rename(
2659 b'undo.bookmarks', b'bookmarks', checkambig=True
2655 b'undo.bookmarks', b'bookmarks', checkambig=True
2660 )
2656 )
2661 if self.svfs.exists(b'undo.phaseroots'):
2657 if self.svfs.exists(b'undo.phaseroots'):
2662 self.svfs.rename(b'undo.phaseroots', b'phaseroots', checkambig=True)
2658 self.svfs.rename(b'undo.phaseroots', b'phaseroots', checkambig=True)
2663 self.invalidate()
2659 self.invalidate()
2664
2660
2665 has_node = self.changelog.index.has_node
2661 has_node = self.changelog.index.has_node
2666 parentgone = any(not has_node(p) for p in parents)
2662 parentgone = any(not has_node(p) for p in parents)
2667 if parentgone:
2663 if parentgone:
2668 # prevent dirstateguard from overwriting already restored one
2664 # prevent dirstateguard from overwriting already restored one
2669 dsguard.close()
2665 dsguard.close()
2670
2666
2671 narrowspec.restorebackup(self, b'undo.narrowspec')
2667 narrowspec.restorebackup(self, b'undo.narrowspec')
2672 narrowspec.restorewcbackup(self, b'undo.narrowspec.dirstate')
2668 narrowspec.restorewcbackup(self, b'undo.narrowspec.dirstate')
2673 self.dirstate.restorebackup(None, b'undo.dirstate')
2669 self.dirstate.restorebackup(None, b'undo.dirstate')
2674 try:
2670 try:
2675 branch = self.vfs.read(b'undo.branch')
2671 branch = self.vfs.read(b'undo.branch')
2676 self.dirstate.setbranch(encoding.tolocal(branch))
2672 self.dirstate.setbranch(encoding.tolocal(branch))
2677 except IOError:
2673 except IOError:
2678 ui.warn(
2674 ui.warn(
2679 _(
2675 _(
2680 b'named branch could not be reset: '
2676 b'named branch could not be reset: '
2681 b'current branch is still \'%s\'\n'
2677 b'current branch is still \'%s\'\n'
2682 )
2678 )
2683 % self.dirstate.branch()
2679 % self.dirstate.branch()
2684 )
2680 )
2685
2681
2686 parents = tuple([p.rev() for p in self[None].parents()])
2682 parents = tuple([p.rev() for p in self[None].parents()])
2687 if len(parents) > 1:
2683 if len(parents) > 1:
2688 ui.status(
2684 ui.status(
2689 _(
2685 _(
2690 b'working directory now based on '
2686 b'working directory now based on '
2691 b'revisions %d and %d\n'
2687 b'revisions %d and %d\n'
2692 )
2688 )
2693 % parents
2689 % parents
2694 )
2690 )
2695 else:
2691 else:
2696 ui.status(
2692 ui.status(
2697 _(b'working directory now based on revision %d\n') % parents
2693 _(b'working directory now based on revision %d\n') % parents
2698 )
2694 )
2699 mergestatemod.mergestate.clean(self)
2695 mergestatemod.mergestate.clean(self)
2700
2696
2701 # TODO: if we know which new heads may result from this rollback, pass
2697 # TODO: if we know which new heads may result from this rollback, pass
2702 # them to destroy(), which will prevent the branchhead cache from being
2698 # them to destroy(), which will prevent the branchhead cache from being
2703 # invalidated.
2699 # invalidated.
2704 self.destroyed()
2700 self.destroyed()
2705 return 0
2701 return 0
2706
2702
2707 def _buildcacheupdater(self, newtransaction):
2703 def _buildcacheupdater(self, newtransaction):
2708 """called during transaction to build the callback updating cache
2704 """called during transaction to build the callback updating cache
2709
2705
2710 Lives on the repository to help extension who might want to augment
2706 Lives on the repository to help extension who might want to augment
2711 this logic. For this purpose, the created transaction is passed to the
2707 this logic. For this purpose, the created transaction is passed to the
2712 method.
2708 method.
2713 """
2709 """
2714 # we must avoid cyclic reference between repo and transaction.
2710 # we must avoid cyclic reference between repo and transaction.
2715 reporef = weakref.ref(self)
2711 reporef = weakref.ref(self)
2716
2712
2717 def updater(tr):
2713 def updater(tr):
2718 repo = reporef()
2714 repo = reporef()
2719 assert repo is not None # help pytype
2715 assert repo is not None # help pytype
2720 repo.updatecaches(tr)
2716 repo.updatecaches(tr)
2721
2717
2722 return updater
2718 return updater
2723
2719
2724 @unfilteredmethod
2720 @unfilteredmethod
2725 def updatecaches(self, tr=None, full=False):
2721 def updatecaches(self, tr=None, full=False):
2726 """warm appropriate caches
2722 """warm appropriate caches
2727
2723
2728 If this function is called after a transaction closed. The transaction
2724 If this function is called after a transaction closed. The transaction
2729 will be available in the 'tr' argument. This can be used to selectively
2725 will be available in the 'tr' argument. This can be used to selectively
2730 update caches relevant to the changes in that transaction.
2726 update caches relevant to the changes in that transaction.
2731
2727
2732 If 'full' is set, make sure all caches the function knows about have
2728 If 'full' is set, make sure all caches the function knows about have
2733 up-to-date data. Even the ones usually loaded more lazily.
2729 up-to-date data. Even the ones usually loaded more lazily.
2734 """
2730 """
2735 if tr is not None and tr.hookargs.get(b'source') == b'strip':
2731 if tr is not None and tr.hookargs.get(b'source') == b'strip':
2736 # During strip, many caches are invalid but
2732 # During strip, many caches are invalid but
2737 # later call to `destroyed` will refresh them.
2733 # later call to `destroyed` will refresh them.
2738 return
2734 return
2739
2735
2740 if tr is None or tr.changes[b'origrepolen'] < len(self):
2736 if tr is None or tr.changes[b'origrepolen'] < len(self):
2741 # accessing the 'served' branchmap should refresh all the others,
2737 # accessing the 'served' branchmap should refresh all the others,
2742 self.ui.debug(b'updating the branch cache\n')
2738 self.ui.debug(b'updating the branch cache\n')
2743 self.filtered(b'served').branchmap()
2739 self.filtered(b'served').branchmap()
2744 self.filtered(b'served.hidden').branchmap()
2740 self.filtered(b'served.hidden').branchmap()
2745
2741
2746 if full:
2742 if full:
2747 unfi = self.unfiltered()
2743 unfi = self.unfiltered()
2748
2744
2749 self.changelog.update_caches(transaction=tr)
2745 self.changelog.update_caches(transaction=tr)
2750 self.manifestlog.update_caches(transaction=tr)
2746 self.manifestlog.update_caches(transaction=tr)
2751
2747
2752 rbc = unfi.revbranchcache()
2748 rbc = unfi.revbranchcache()
2753 for r in unfi.changelog:
2749 for r in unfi.changelog:
2754 rbc.branchinfo(r)
2750 rbc.branchinfo(r)
2755 rbc.write()
2751 rbc.write()
2756
2752
2757 # ensure the working copy parents are in the manifestfulltextcache
2753 # ensure the working copy parents are in the manifestfulltextcache
2758 for ctx in self[b'.'].parents():
2754 for ctx in self[b'.'].parents():
2759 ctx.manifest() # accessing the manifest is enough
2755 ctx.manifest() # accessing the manifest is enough
2760
2756
2761 # accessing fnode cache warms the cache
2757 # accessing fnode cache warms the cache
2762 tagsmod.fnoderevs(self.ui, unfi, unfi.changelog.revs())
2758 tagsmod.fnoderevs(self.ui, unfi, unfi.changelog.revs())
2763 # accessing tags warm the cache
2759 # accessing tags warm the cache
2764 self.tags()
2760 self.tags()
2765 self.filtered(b'served').tags()
2761 self.filtered(b'served').tags()
2766
2762
2767 # The `full` arg is documented as updating even the lazily-loaded
2763 # The `full` arg is documented as updating even the lazily-loaded
2768 # caches immediately, so we're forcing a write to cause these caches
2764 # caches immediately, so we're forcing a write to cause these caches
2769 # to be warmed up even if they haven't explicitly been requested
2765 # to be warmed up even if they haven't explicitly been requested
2770 # yet (if they've never been used by hg, they won't ever have been
2766 # yet (if they've never been used by hg, they won't ever have been
2771 # written, even if they're a subset of another kind of cache that
2767 # written, even if they're a subset of another kind of cache that
2772 # *has* been used).
2768 # *has* been used).
2773 for filt in repoview.filtertable.keys():
2769 for filt in repoview.filtertable.keys():
2774 filtered = self.filtered(filt)
2770 filtered = self.filtered(filt)
2775 filtered.branchmap().write(filtered)
2771 filtered.branchmap().write(filtered)
2776
2772
2777 def invalidatecaches(self):
2773 def invalidatecaches(self):
2778
2774
2779 if '_tagscache' in vars(self):
2775 if '_tagscache' in vars(self):
2780 # can't use delattr on proxy
2776 # can't use delattr on proxy
2781 del self.__dict__['_tagscache']
2777 del self.__dict__['_tagscache']
2782
2778
2783 self._branchcaches.clear()
2779 self._branchcaches.clear()
2784 self.invalidatevolatilesets()
2780 self.invalidatevolatilesets()
2785 self._sparsesignaturecache.clear()
2781 self._sparsesignaturecache.clear()
2786
2782
2787 def invalidatevolatilesets(self):
2783 def invalidatevolatilesets(self):
2788 self.filteredrevcache.clear()
2784 self.filteredrevcache.clear()
2789 obsolete.clearobscaches(self)
2785 obsolete.clearobscaches(self)
2790 self._quick_access_changeid_invalidate()
2786 self._quick_access_changeid_invalidate()
2791
2787
2792 def invalidatedirstate(self):
2788 def invalidatedirstate(self):
2793 """Invalidates the dirstate, causing the next call to dirstate
2789 """Invalidates the dirstate, causing the next call to dirstate
2794 to check if it was modified since the last time it was read,
2790 to check if it was modified since the last time it was read,
2795 rereading it if it has.
2791 rereading it if it has.
2796
2792
2797 This is different to dirstate.invalidate() that it doesn't always
2793 This is different to dirstate.invalidate() that it doesn't always
2798 rereads the dirstate. Use dirstate.invalidate() if you want to
2794 rereads the dirstate. Use dirstate.invalidate() if you want to
2799 explicitly read the dirstate again (i.e. restoring it to a previous
2795 explicitly read the dirstate again (i.e. restoring it to a previous
2800 known good state)."""
2796 known good state)."""
2801 if hasunfilteredcache(self, 'dirstate'):
2797 if hasunfilteredcache(self, 'dirstate'):
2802 for k in self.dirstate._filecache:
2798 for k in self.dirstate._filecache:
2803 try:
2799 try:
2804 delattr(self.dirstate, k)
2800 delattr(self.dirstate, k)
2805 except AttributeError:
2801 except AttributeError:
2806 pass
2802 pass
2807 delattr(self.unfiltered(), 'dirstate')
2803 delattr(self.unfiltered(), 'dirstate')
2808
2804
2809 def invalidate(self, clearfilecache=False):
2805 def invalidate(self, clearfilecache=False):
2810 """Invalidates both store and non-store parts other than dirstate
2806 """Invalidates both store and non-store parts other than dirstate
2811
2807
2812 If a transaction is running, invalidation of store is omitted,
2808 If a transaction is running, invalidation of store is omitted,
2813 because discarding in-memory changes might cause inconsistency
2809 because discarding in-memory changes might cause inconsistency
2814 (e.g. incomplete fncache causes unintentional failure, but
2810 (e.g. incomplete fncache causes unintentional failure, but
2815 redundant one doesn't).
2811 redundant one doesn't).
2816 """
2812 """
2817 unfiltered = self.unfiltered() # all file caches are stored unfiltered
2813 unfiltered = self.unfiltered() # all file caches are stored unfiltered
2818 for k in list(self._filecache.keys()):
2814 for k in list(self._filecache.keys()):
2819 # dirstate is invalidated separately in invalidatedirstate()
2815 # dirstate is invalidated separately in invalidatedirstate()
2820 if k == b'dirstate':
2816 if k == b'dirstate':
2821 continue
2817 continue
2822 if (
2818 if (
2823 k == b'changelog'
2819 k == b'changelog'
2824 and self.currenttransaction()
2820 and self.currenttransaction()
2825 and self.changelog._delayed
2821 and self.changelog._delayed
2826 ):
2822 ):
2827 # The changelog object may store unwritten revisions. We don't
2823 # The changelog object may store unwritten revisions. We don't
2828 # want to lose them.
2824 # want to lose them.
2829 # TODO: Solve the problem instead of working around it.
2825 # TODO: Solve the problem instead of working around it.
2830 continue
2826 continue
2831
2827
2832 if clearfilecache:
2828 if clearfilecache:
2833 del self._filecache[k]
2829 del self._filecache[k]
2834 try:
2830 try:
2835 delattr(unfiltered, k)
2831 delattr(unfiltered, k)
2836 except AttributeError:
2832 except AttributeError:
2837 pass
2833 pass
2838 self.invalidatecaches()
2834 self.invalidatecaches()
2839 if not self.currenttransaction():
2835 if not self.currenttransaction():
2840 # TODO: Changing contents of store outside transaction
2836 # TODO: Changing contents of store outside transaction
2841 # causes inconsistency. We should make in-memory store
2837 # causes inconsistency. We should make in-memory store
2842 # changes detectable, and abort if changed.
2838 # changes detectable, and abort if changed.
2843 self.store.invalidatecaches()
2839 self.store.invalidatecaches()
2844
2840
2845 def invalidateall(self):
2841 def invalidateall(self):
2846 """Fully invalidates both store and non-store parts, causing the
2842 """Fully invalidates both store and non-store parts, causing the
2847 subsequent operation to reread any outside changes."""
2843 subsequent operation to reread any outside changes."""
2848 # extension should hook this to invalidate its caches
2844 # extension should hook this to invalidate its caches
2849 self.invalidate()
2845 self.invalidate()
2850 self.invalidatedirstate()
2846 self.invalidatedirstate()
2851
2847
2852 @unfilteredmethod
2848 @unfilteredmethod
2853 def _refreshfilecachestats(self, tr):
2849 def _refreshfilecachestats(self, tr):
2854 """Reload stats of cached files so that they are flagged as valid"""
2850 """Reload stats of cached files so that they are flagged as valid"""
2855 for k, ce in self._filecache.items():
2851 for k, ce in self._filecache.items():
2856 k = pycompat.sysstr(k)
2852 k = pycompat.sysstr(k)
2857 if k == 'dirstate' or k not in self.__dict__:
2853 if k == 'dirstate' or k not in self.__dict__:
2858 continue
2854 continue
2859 ce.refresh()
2855 ce.refresh()
2860
2856
2861 def _lock(
2857 def _lock(
2862 self,
2858 self,
2863 vfs,
2859 vfs,
2864 lockname,
2860 lockname,
2865 wait,
2861 wait,
2866 releasefn,
2862 releasefn,
2867 acquirefn,
2863 acquirefn,
2868 desc,
2864 desc,
2869 ):
2865 ):
2870 timeout = 0
2866 timeout = 0
2871 warntimeout = 0
2867 warntimeout = 0
2872 if wait:
2868 if wait:
2873 timeout = self.ui.configint(b"ui", b"timeout")
2869 timeout = self.ui.configint(b"ui", b"timeout")
2874 warntimeout = self.ui.configint(b"ui", b"timeout.warn")
2870 warntimeout = self.ui.configint(b"ui", b"timeout.warn")
2875 # internal config: ui.signal-safe-lock
2871 # internal config: ui.signal-safe-lock
2876 signalsafe = self.ui.configbool(b'ui', b'signal-safe-lock')
2872 signalsafe = self.ui.configbool(b'ui', b'signal-safe-lock')
2877
2873
2878 l = lockmod.trylock(
2874 l = lockmod.trylock(
2879 self.ui,
2875 self.ui,
2880 vfs,
2876 vfs,
2881 lockname,
2877 lockname,
2882 timeout,
2878 timeout,
2883 warntimeout,
2879 warntimeout,
2884 releasefn=releasefn,
2880 releasefn=releasefn,
2885 acquirefn=acquirefn,
2881 acquirefn=acquirefn,
2886 desc=desc,
2882 desc=desc,
2887 signalsafe=signalsafe,
2883 signalsafe=signalsafe,
2888 )
2884 )
2889 return l
2885 return l
2890
2886
2891 def _afterlock(self, callback):
2887 def _afterlock(self, callback):
2892 """add a callback to be run when the repository is fully unlocked
2888 """add a callback to be run when the repository is fully unlocked
2893
2889
2894 The callback will be executed when the outermost lock is released
2890 The callback will be executed when the outermost lock is released
2895 (with wlock being higher level than 'lock')."""
2891 (with wlock being higher level than 'lock')."""
2896 for ref in (self._wlockref, self._lockref):
2892 for ref in (self._wlockref, self._lockref):
2897 l = ref and ref()
2893 l = ref and ref()
2898 if l and l.held:
2894 if l and l.held:
2899 l.postrelease.append(callback)
2895 l.postrelease.append(callback)
2900 break
2896 break
2901 else: # no lock have been found.
2897 else: # no lock have been found.
2902 callback(True)
2898 callback(True)
2903
2899
2904 def lock(self, wait=True):
2900 def lock(self, wait=True):
2905 """Lock the repository store (.hg/store) and return a weak reference
2901 """Lock the repository store (.hg/store) and return a weak reference
2906 to the lock. Use this before modifying the store (e.g. committing or
2902 to the lock. Use this before modifying the store (e.g. committing or
2907 stripping). If you are opening a transaction, get a lock as well.)
2903 stripping). If you are opening a transaction, get a lock as well.)
2908
2904
2909 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
2905 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
2910 'wlock' first to avoid a dead-lock hazard."""
2906 'wlock' first to avoid a dead-lock hazard."""
2911 l = self._currentlock(self._lockref)
2907 l = self._currentlock(self._lockref)
2912 if l is not None:
2908 if l is not None:
2913 l.lock()
2909 l.lock()
2914 return l
2910 return l
2915
2911
2916 l = self._lock(
2912 l = self._lock(
2917 vfs=self.svfs,
2913 vfs=self.svfs,
2918 lockname=b"lock",
2914 lockname=b"lock",
2919 wait=wait,
2915 wait=wait,
2920 releasefn=None,
2916 releasefn=None,
2921 acquirefn=self.invalidate,
2917 acquirefn=self.invalidate,
2922 desc=_(b'repository %s') % self.origroot,
2918 desc=_(b'repository %s') % self.origroot,
2923 )
2919 )
2924 self._lockref = weakref.ref(l)
2920 self._lockref = weakref.ref(l)
2925 return l
2921 return l
2926
2922
2927 def wlock(self, wait=True):
2923 def wlock(self, wait=True):
2928 """Lock the non-store parts of the repository (everything under
2924 """Lock the non-store parts of the repository (everything under
2929 .hg except .hg/store) and return a weak reference to the lock.
2925 .hg except .hg/store) and return a weak reference to the lock.
2930
2926
2931 Use this before modifying files in .hg.
2927 Use this before modifying files in .hg.
2932
2928
2933 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
2929 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
2934 'wlock' first to avoid a dead-lock hazard."""
2930 'wlock' first to avoid a dead-lock hazard."""
2935 l = self._wlockref() if self._wlockref else None
2931 l = self._wlockref() if self._wlockref else None
2936 if l is not None and l.held:
2932 if l is not None and l.held:
2937 l.lock()
2933 l.lock()
2938 return l
2934 return l
2939
2935
2940 # We do not need to check for non-waiting lock acquisition. Such
2936 # We do not need to check for non-waiting lock acquisition. Such
2941 # acquisition would not cause dead-lock as they would just fail.
2937 # acquisition would not cause dead-lock as they would just fail.
2942 if wait and (
2938 if wait and (
2943 self.ui.configbool(b'devel', b'all-warnings')
2939 self.ui.configbool(b'devel', b'all-warnings')
2944 or self.ui.configbool(b'devel', b'check-locks')
2940 or self.ui.configbool(b'devel', b'check-locks')
2945 ):
2941 ):
2946 if self._currentlock(self._lockref) is not None:
2942 if self._currentlock(self._lockref) is not None:
2947 self.ui.develwarn(b'"wlock" acquired after "lock"')
2943 self.ui.develwarn(b'"wlock" acquired after "lock"')
2948
2944
2949 def unlock():
2945 def unlock():
2950 if self.dirstate.pendingparentchange():
2946 if self.dirstate.pendingparentchange():
2951 self.dirstate.invalidate()
2947 self.dirstate.invalidate()
2952 else:
2948 else:
2953 self.dirstate.write(None)
2949 self.dirstate.write(None)
2954
2950
2955 self._filecache[b'dirstate'].refresh()
2951 self._filecache[b'dirstate'].refresh()
2956
2952
2957 l = self._lock(
2953 l = self._lock(
2958 self.vfs,
2954 self.vfs,
2959 b"wlock",
2955 b"wlock",
2960 wait,
2956 wait,
2961 unlock,
2957 unlock,
2962 self.invalidatedirstate,
2958 self.invalidatedirstate,
2963 _(b'working directory of %s') % self.origroot,
2959 _(b'working directory of %s') % self.origroot,
2964 )
2960 )
2965 self._wlockref = weakref.ref(l)
2961 self._wlockref = weakref.ref(l)
2966 return l
2962 return l
2967
2963
2968 def _currentlock(self, lockref):
2964 def _currentlock(self, lockref):
2969 """Returns the lock if it's held, or None if it's not."""
2965 """Returns the lock if it's held, or None if it's not."""
2970 if lockref is None:
2966 if lockref is None:
2971 return None
2967 return None
2972 l = lockref()
2968 l = lockref()
2973 if l is None or not l.held:
2969 if l is None or not l.held:
2974 return None
2970 return None
2975 return l
2971 return l
2976
2972
2977 def currentwlock(self):
2973 def currentwlock(self):
2978 """Returns the wlock if it's held, or None if it's not."""
2974 """Returns the wlock if it's held, or None if it's not."""
2979 return self._currentlock(self._wlockref)
2975 return self._currentlock(self._wlockref)
2980
2976
2981 def checkcommitpatterns(self, wctx, match, status, fail):
2977 def checkcommitpatterns(self, wctx, match, status, fail):
2982 """check for commit arguments that aren't committable"""
2978 """check for commit arguments that aren't committable"""
2983 if match.isexact() or match.prefix():
2979 if match.isexact() or match.prefix():
2984 matched = set(status.modified + status.added + status.removed)
2980 matched = set(status.modified + status.added + status.removed)
2985
2981
2986 for f in match.files():
2982 for f in match.files():
2987 f = self.dirstate.normalize(f)
2983 f = self.dirstate.normalize(f)
2988 if f == b'.' or f in matched or f in wctx.substate:
2984 if f == b'.' or f in matched or f in wctx.substate:
2989 continue
2985 continue
2990 if f in status.deleted:
2986 if f in status.deleted:
2991 fail(f, _(b'file not found!'))
2987 fail(f, _(b'file not found!'))
2992 # Is it a directory that exists or used to exist?
2988 # Is it a directory that exists or used to exist?
2993 if self.wvfs.isdir(f) or wctx.p1().hasdir(f):
2989 if self.wvfs.isdir(f) or wctx.p1().hasdir(f):
2994 d = f + b'/'
2990 d = f + b'/'
2995 for mf in matched:
2991 for mf in matched:
2996 if mf.startswith(d):
2992 if mf.startswith(d):
2997 break
2993 break
2998 else:
2994 else:
2999 fail(f, _(b"no match under directory!"))
2995 fail(f, _(b"no match under directory!"))
3000 elif f not in self.dirstate:
2996 elif f not in self.dirstate:
3001 fail(f, _(b"file not tracked!"))
2997 fail(f, _(b"file not tracked!"))
3002
2998
3003 @unfilteredmethod
2999 @unfilteredmethod
3004 def commit(
3000 def commit(
3005 self,
3001 self,
3006 text=b"",
3002 text=b"",
3007 user=None,
3003 user=None,
3008 date=None,
3004 date=None,
3009 match=None,
3005 match=None,
3010 force=False,
3006 force=False,
3011 editor=None,
3007 editor=None,
3012 extra=None,
3008 extra=None,
3013 ):
3009 ):
3014 """Add a new revision to current repository.
3010 """Add a new revision to current repository.
3015
3011
3016 Revision information is gathered from the working directory,
3012 Revision information is gathered from the working directory,
3017 match can be used to filter the committed files. If editor is
3013 match can be used to filter the committed files. If editor is
3018 supplied, it is called to get a commit message.
3014 supplied, it is called to get a commit message.
3019 """
3015 """
3020 if extra is None:
3016 if extra is None:
3021 extra = {}
3017 extra = {}
3022
3018
3023 def fail(f, msg):
3019 def fail(f, msg):
3024 raise error.InputError(b'%s: %s' % (f, msg))
3020 raise error.InputError(b'%s: %s' % (f, msg))
3025
3021
3026 if not match:
3022 if not match:
3027 match = matchmod.always()
3023 match = matchmod.always()
3028
3024
3029 if not force:
3025 if not force:
3030 match.bad = fail
3026 match.bad = fail
3031
3027
3032 # lock() for recent changelog (see issue4368)
3028 # lock() for recent changelog (see issue4368)
3033 with self.wlock(), self.lock():
3029 with self.wlock(), self.lock():
3034 wctx = self[None]
3030 wctx = self[None]
3035 merge = len(wctx.parents()) > 1
3031 merge = len(wctx.parents()) > 1
3036
3032
3037 if not force and merge and not match.always():
3033 if not force and merge and not match.always():
3038 raise error.Abort(
3034 raise error.Abort(
3039 _(
3035 _(
3040 b'cannot partially commit a merge '
3036 b'cannot partially commit a merge '
3041 b'(do not specify files or patterns)'
3037 b'(do not specify files or patterns)'
3042 )
3038 )
3043 )
3039 )
3044
3040
3045 status = self.status(match=match, clean=force)
3041 status = self.status(match=match, clean=force)
3046 if force:
3042 if force:
3047 status.modified.extend(
3043 status.modified.extend(
3048 status.clean
3044 status.clean
3049 ) # mq may commit clean files
3045 ) # mq may commit clean files
3050
3046
3051 # check subrepos
3047 # check subrepos
3052 subs, commitsubs, newstate = subrepoutil.precommit(
3048 subs, commitsubs, newstate = subrepoutil.precommit(
3053 self.ui, wctx, status, match, force=force
3049 self.ui, wctx, status, match, force=force
3054 )
3050 )
3055
3051
3056 # make sure all explicit patterns are matched
3052 # make sure all explicit patterns are matched
3057 if not force:
3053 if not force:
3058 self.checkcommitpatterns(wctx, match, status, fail)
3054 self.checkcommitpatterns(wctx, match, status, fail)
3059
3055
3060 cctx = context.workingcommitctx(
3056 cctx = context.workingcommitctx(
3061 self, status, text, user, date, extra
3057 self, status, text, user, date, extra
3062 )
3058 )
3063
3059
3064 ms = mergestatemod.mergestate.read(self)
3060 ms = mergestatemod.mergestate.read(self)
3065 mergeutil.checkunresolved(ms)
3061 mergeutil.checkunresolved(ms)
3066
3062
3067 # internal config: ui.allowemptycommit
3063 # internal config: ui.allowemptycommit
3068 if cctx.isempty() and not self.ui.configbool(
3064 if cctx.isempty() and not self.ui.configbool(
3069 b'ui', b'allowemptycommit'
3065 b'ui', b'allowemptycommit'
3070 ):
3066 ):
3071 self.ui.debug(b'nothing to commit, clearing merge state\n')
3067 self.ui.debug(b'nothing to commit, clearing merge state\n')
3072 ms.reset()
3068 ms.reset()
3073 return None
3069 return None
3074
3070
3075 if merge and cctx.deleted():
3071 if merge and cctx.deleted():
3076 raise error.Abort(_(b"cannot commit merge with missing files"))
3072 raise error.Abort(_(b"cannot commit merge with missing files"))
3077
3073
3078 if editor:
3074 if editor:
3079 cctx._text = editor(self, cctx, subs)
3075 cctx._text = editor(self, cctx, subs)
3080 edited = text != cctx._text
3076 edited = text != cctx._text
3081
3077
3082 # Save commit message in case this transaction gets rolled back
3078 # Save commit message in case this transaction gets rolled back
3083 # (e.g. by a pretxncommit hook). Leave the content alone on
3079 # (e.g. by a pretxncommit hook). Leave the content alone on
3084 # the assumption that the user will use the same editor again.
3080 # the assumption that the user will use the same editor again.
3085 msgfn = self.savecommitmessage(cctx._text)
3081 msgfn = self.savecommitmessage(cctx._text)
3086
3082
3087 # commit subs and write new state
3083 # commit subs and write new state
3088 if subs:
3084 if subs:
3089 uipathfn = scmutil.getuipathfn(self)
3085 uipathfn = scmutil.getuipathfn(self)
3090 for s in sorted(commitsubs):
3086 for s in sorted(commitsubs):
3091 sub = wctx.sub(s)
3087 sub = wctx.sub(s)
3092 self.ui.status(
3088 self.ui.status(
3093 _(b'committing subrepository %s\n')
3089 _(b'committing subrepository %s\n')
3094 % uipathfn(subrepoutil.subrelpath(sub))
3090 % uipathfn(subrepoutil.subrelpath(sub))
3095 )
3091 )
3096 sr = sub.commit(cctx._text, user, date)
3092 sr = sub.commit(cctx._text, user, date)
3097 newstate[s] = (newstate[s][0], sr)
3093 newstate[s] = (newstate[s][0], sr)
3098 subrepoutil.writestate(self, newstate)
3094 subrepoutil.writestate(self, newstate)
3099
3095
3100 p1, p2 = self.dirstate.parents()
3096 p1, p2 = self.dirstate.parents()
3101 hookp1, hookp2 = hex(p1), (p2 != self.nullid and hex(p2) or b'')
3097 hookp1, hookp2 = hex(p1), (p2 != self.nullid and hex(p2) or b'')
3102 try:
3098 try:
3103 self.hook(
3099 self.hook(
3104 b"precommit", throw=True, parent1=hookp1, parent2=hookp2
3100 b"precommit", throw=True, parent1=hookp1, parent2=hookp2
3105 )
3101 )
3106 with self.transaction(b'commit'):
3102 with self.transaction(b'commit'):
3107 ret = self.commitctx(cctx, True)
3103 ret = self.commitctx(cctx, True)
3108 # update bookmarks, dirstate and mergestate
3104 # update bookmarks, dirstate and mergestate
3109 bookmarks.update(self, [p1, p2], ret)
3105 bookmarks.update(self, [p1, p2], ret)
3110 cctx.markcommitted(ret)
3106 cctx.markcommitted(ret)
3111 ms.reset()
3107 ms.reset()
3112 except: # re-raises
3108 except: # re-raises
3113 if edited:
3109 if edited:
3114 self.ui.write(
3110 self.ui.write(
3115 _(b'note: commit message saved in %s\n') % msgfn
3111 _(b'note: commit message saved in %s\n') % msgfn
3116 )
3112 )
3117 self.ui.write(
3113 self.ui.write(
3118 _(
3114 _(
3119 b"note: use 'hg commit --logfile "
3115 b"note: use 'hg commit --logfile "
3120 b".hg/last-message.txt --edit' to reuse it\n"
3116 b".hg/last-message.txt --edit' to reuse it\n"
3121 )
3117 )
3122 )
3118 )
3123 raise
3119 raise
3124
3120
3125 def commithook(unused_success):
3121 def commithook(unused_success):
3126 # hack for command that use a temporary commit (eg: histedit)
3122 # hack for command that use a temporary commit (eg: histedit)
3127 # temporary commit got stripped before hook release
3123 # temporary commit got stripped before hook release
3128 if self.changelog.hasnode(ret):
3124 if self.changelog.hasnode(ret):
3129 self.hook(
3125 self.hook(
3130 b"commit", node=hex(ret), parent1=hookp1, parent2=hookp2
3126 b"commit", node=hex(ret), parent1=hookp1, parent2=hookp2
3131 )
3127 )
3132
3128
3133 self._afterlock(commithook)
3129 self._afterlock(commithook)
3134 return ret
3130 return ret
3135
3131
3136 @unfilteredmethod
3132 @unfilteredmethod
3137 def commitctx(self, ctx, error=False, origctx=None):
3133 def commitctx(self, ctx, error=False, origctx=None):
3138 return commit.commitctx(self, ctx, error=error, origctx=origctx)
3134 return commit.commitctx(self, ctx, error=error, origctx=origctx)
3139
3135
3140 @unfilteredmethod
3136 @unfilteredmethod
3141 def destroying(self):
3137 def destroying(self):
3142 """Inform the repository that nodes are about to be destroyed.
3138 """Inform the repository that nodes are about to be destroyed.
3143 Intended for use by strip and rollback, so there's a common
3139 Intended for use by strip and rollback, so there's a common
3144 place for anything that has to be done before destroying history.
3140 place for anything that has to be done before destroying history.
3145
3141
3146 This is mostly useful for saving state that is in memory and waiting
3142 This is mostly useful for saving state that is in memory and waiting
3147 to be flushed when the current lock is released. Because a call to
3143 to be flushed when the current lock is released. Because a call to
3148 destroyed is imminent, the repo will be invalidated causing those
3144 destroyed is imminent, the repo will be invalidated causing those
3149 changes to stay in memory (waiting for the next unlock), or vanish
3145 changes to stay in memory (waiting for the next unlock), or vanish
3150 completely.
3146 completely.
3151 """
3147 """
3152 # When using the same lock to commit and strip, the phasecache is left
3148 # When using the same lock to commit and strip, the phasecache is left
3153 # dirty after committing. Then when we strip, the repo is invalidated,
3149 # dirty after committing. Then when we strip, the repo is invalidated,
3154 # causing those changes to disappear.
3150 # causing those changes to disappear.
3155 if '_phasecache' in vars(self):
3151 if '_phasecache' in vars(self):
3156 self._phasecache.write()
3152 self._phasecache.write()
3157
3153
3158 @unfilteredmethod
3154 @unfilteredmethod
3159 def destroyed(self):
3155 def destroyed(self):
3160 """Inform the repository that nodes have been destroyed.
3156 """Inform the repository that nodes have been destroyed.
3161 Intended for use by strip and rollback, so there's a common
3157 Intended for use by strip and rollback, so there's a common
3162 place for anything that has to be done after destroying history.
3158 place for anything that has to be done after destroying history.
3163 """
3159 """
3164 # When one tries to:
3160 # When one tries to:
3165 # 1) destroy nodes thus calling this method (e.g. strip)
3161 # 1) destroy nodes thus calling this method (e.g. strip)
3166 # 2) use phasecache somewhere (e.g. commit)
3162 # 2) use phasecache somewhere (e.g. commit)
3167 #
3163 #
3168 # then 2) will fail because the phasecache contains nodes that were
3164 # then 2) will fail because the phasecache contains nodes that were
3169 # removed. We can either remove phasecache from the filecache,
3165 # removed. We can either remove phasecache from the filecache,
3170 # causing it to reload next time it is accessed, or simply filter
3166 # causing it to reload next time it is accessed, or simply filter
3171 # the removed nodes now and write the updated cache.
3167 # the removed nodes now and write the updated cache.
3172 self._phasecache.filterunknown(self)
3168 self._phasecache.filterunknown(self)
3173 self._phasecache.write()
3169 self._phasecache.write()
3174
3170
3175 # refresh all repository caches
3171 # refresh all repository caches
3176 self.updatecaches()
3172 self.updatecaches()
3177
3173
3178 # Ensure the persistent tag cache is updated. Doing it now
3174 # Ensure the persistent tag cache is updated. Doing it now
3179 # means that the tag cache only has to worry about destroyed
3175 # means that the tag cache only has to worry about destroyed
3180 # heads immediately after a strip/rollback. That in turn
3176 # heads immediately after a strip/rollback. That in turn
3181 # guarantees that "cachetip == currenttip" (comparing both rev
3177 # guarantees that "cachetip == currenttip" (comparing both rev
3182 # and node) always means no nodes have been added or destroyed.
3178 # and node) always means no nodes have been added or destroyed.
3183
3179
3184 # XXX this is suboptimal when qrefresh'ing: we strip the current
3180 # XXX this is suboptimal when qrefresh'ing: we strip the current
3185 # head, refresh the tag cache, then immediately add a new head.
3181 # head, refresh the tag cache, then immediately add a new head.
3186 # But I think doing it this way is necessary for the "instant
3182 # But I think doing it this way is necessary for the "instant
3187 # tag cache retrieval" case to work.
3183 # tag cache retrieval" case to work.
3188 self.invalidate()
3184 self.invalidate()
3189
3185
3190 def status(
3186 def status(
3191 self,
3187 self,
3192 node1=b'.',
3188 node1=b'.',
3193 node2=None,
3189 node2=None,
3194 match=None,
3190 match=None,
3195 ignored=False,
3191 ignored=False,
3196 clean=False,
3192 clean=False,
3197 unknown=False,
3193 unknown=False,
3198 listsubrepos=False,
3194 listsubrepos=False,
3199 ):
3195 ):
3200 '''a convenience method that calls node1.status(node2)'''
3196 '''a convenience method that calls node1.status(node2)'''
3201 return self[node1].status(
3197 return self[node1].status(
3202 node2, match, ignored, clean, unknown, listsubrepos
3198 node2, match, ignored, clean, unknown, listsubrepos
3203 )
3199 )
3204
3200
3205 def addpostdsstatus(self, ps):
3201 def addpostdsstatus(self, ps):
3206 """Add a callback to run within the wlock, at the point at which status
3202 """Add a callback to run within the wlock, at the point at which status
3207 fixups happen.
3203 fixups happen.
3208
3204
3209 On status completion, callback(wctx, status) will be called with the
3205 On status completion, callback(wctx, status) will be called with the
3210 wlock held, unless the dirstate has changed from underneath or the wlock
3206 wlock held, unless the dirstate has changed from underneath or the wlock
3211 couldn't be grabbed.
3207 couldn't be grabbed.
3212
3208
3213 Callbacks should not capture and use a cached copy of the dirstate --
3209 Callbacks should not capture and use a cached copy of the dirstate --
3214 it might change in the meanwhile. Instead, they should access the
3210 it might change in the meanwhile. Instead, they should access the
3215 dirstate via wctx.repo().dirstate.
3211 dirstate via wctx.repo().dirstate.
3216
3212
3217 This list is emptied out after each status run -- extensions should
3213 This list is emptied out after each status run -- extensions should
3218 make sure it adds to this list each time dirstate.status is called.
3214 make sure it adds to this list each time dirstate.status is called.
3219 Extensions should also make sure they don't call this for statuses
3215 Extensions should also make sure they don't call this for statuses
3220 that don't involve the dirstate.
3216 that don't involve the dirstate.
3221 """
3217 """
3222
3218
3223 # The list is located here for uniqueness reasons -- it is actually
3219 # The list is located here for uniqueness reasons -- it is actually
3224 # managed by the workingctx, but that isn't unique per-repo.
3220 # managed by the workingctx, but that isn't unique per-repo.
3225 self._postdsstatus.append(ps)
3221 self._postdsstatus.append(ps)
3226
3222
3227 def postdsstatus(self):
3223 def postdsstatus(self):
3228 """Used by workingctx to get the list of post-dirstate-status hooks."""
3224 """Used by workingctx to get the list of post-dirstate-status hooks."""
3229 return self._postdsstatus
3225 return self._postdsstatus
3230
3226
3231 def clearpostdsstatus(self):
3227 def clearpostdsstatus(self):
3232 """Used by workingctx to clear post-dirstate-status hooks."""
3228 """Used by workingctx to clear post-dirstate-status hooks."""
3233 del self._postdsstatus[:]
3229 del self._postdsstatus[:]
3234
3230
3235 def heads(self, start=None):
3231 def heads(self, start=None):
3236 if start is None:
3232 if start is None:
3237 cl = self.changelog
3233 cl = self.changelog
3238 headrevs = reversed(cl.headrevs())
3234 headrevs = reversed(cl.headrevs())
3239 return [cl.node(rev) for rev in headrevs]
3235 return [cl.node(rev) for rev in headrevs]
3240
3236
3241 heads = self.changelog.heads(start)
3237 heads = self.changelog.heads(start)
3242 # sort the output in rev descending order
3238 # sort the output in rev descending order
3243 return sorted(heads, key=self.changelog.rev, reverse=True)
3239 return sorted(heads, key=self.changelog.rev, reverse=True)
3244
3240
3245 def branchheads(self, branch=None, start=None, closed=False):
3241 def branchheads(self, branch=None, start=None, closed=False):
3246 """return a (possibly filtered) list of heads for the given branch
3242 """return a (possibly filtered) list of heads for the given branch
3247
3243
3248 Heads are returned in topological order, from newest to oldest.
3244 Heads are returned in topological order, from newest to oldest.
3249 If branch is None, use the dirstate branch.
3245 If branch is None, use the dirstate branch.
3250 If start is not None, return only heads reachable from start.
3246 If start is not None, return only heads reachable from start.
3251 If closed is True, return heads that are marked as closed as well.
3247 If closed is True, return heads that are marked as closed as well.
3252 """
3248 """
3253 if branch is None:
3249 if branch is None:
3254 branch = self[None].branch()
3250 branch = self[None].branch()
3255 branches = self.branchmap()
3251 branches = self.branchmap()
3256 if not branches.hasbranch(branch):
3252 if not branches.hasbranch(branch):
3257 return []
3253 return []
3258 # the cache returns heads ordered lowest to highest
3254 # the cache returns heads ordered lowest to highest
3259 bheads = list(reversed(branches.branchheads(branch, closed=closed)))
3255 bheads = list(reversed(branches.branchheads(branch, closed=closed)))
3260 if start is not None:
3256 if start is not None:
3261 # filter out the heads that cannot be reached from startrev
3257 # filter out the heads that cannot be reached from startrev
3262 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
3258 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
3263 bheads = [h for h in bheads if h in fbheads]
3259 bheads = [h for h in bheads if h in fbheads]
3264 return bheads
3260 return bheads
3265
3261
3266 def branches(self, nodes):
3262 def branches(self, nodes):
3267 if not nodes:
3263 if not nodes:
3268 nodes = [self.changelog.tip()]
3264 nodes = [self.changelog.tip()]
3269 b = []
3265 b = []
3270 for n in nodes:
3266 for n in nodes:
3271 t = n
3267 t = n
3272 while True:
3268 while True:
3273 p = self.changelog.parents(n)
3269 p = self.changelog.parents(n)
3274 if p[1] != self.nullid or p[0] == self.nullid:
3270 if p[1] != self.nullid or p[0] == self.nullid:
3275 b.append((t, n, p[0], p[1]))
3271 b.append((t, n, p[0], p[1]))
3276 break
3272 break
3277 n = p[0]
3273 n = p[0]
3278 return b
3274 return b
3279
3275
3280 def between(self, pairs):
3276 def between(self, pairs):
3281 r = []
3277 r = []
3282
3278
3283 for top, bottom in pairs:
3279 for top, bottom in pairs:
3284 n, l, i = top, [], 0
3280 n, l, i = top, [], 0
3285 f = 1
3281 f = 1
3286
3282
3287 while n != bottom and n != self.nullid:
3283 while n != bottom and n != self.nullid:
3288 p = self.changelog.parents(n)[0]
3284 p = self.changelog.parents(n)[0]
3289 if i == f:
3285 if i == f:
3290 l.append(n)
3286 l.append(n)
3291 f = f * 2
3287 f = f * 2
3292 n = p
3288 n = p
3293 i += 1
3289 i += 1
3294
3290
3295 r.append(l)
3291 r.append(l)
3296
3292
3297 return r
3293 return r
3298
3294
3299 def checkpush(self, pushop):
3295 def checkpush(self, pushop):
3300 """Extensions can override this function if additional checks have
3296 """Extensions can override this function if additional checks have
3301 to be performed before pushing, or call it if they override push
3297 to be performed before pushing, or call it if they override push
3302 command.
3298 command.
3303 """
3299 """
3304
3300
3305 @unfilteredpropertycache
3301 @unfilteredpropertycache
3306 def prepushoutgoinghooks(self):
3302 def prepushoutgoinghooks(self):
3307 """Return util.hooks consists of a pushop with repo, remote, outgoing
3303 """Return util.hooks consists of a pushop with repo, remote, outgoing
3308 methods, which are called before pushing changesets.
3304 methods, which are called before pushing changesets.
3309 """
3305 """
3310 return util.hooks()
3306 return util.hooks()
3311
3307
3312 def pushkey(self, namespace, key, old, new):
3308 def pushkey(self, namespace, key, old, new):
3313 try:
3309 try:
3314 tr = self.currenttransaction()
3310 tr = self.currenttransaction()
3315 hookargs = {}
3311 hookargs = {}
3316 if tr is not None:
3312 if tr is not None:
3317 hookargs.update(tr.hookargs)
3313 hookargs.update(tr.hookargs)
3318 hookargs = pycompat.strkwargs(hookargs)
3314 hookargs = pycompat.strkwargs(hookargs)
3319 hookargs['namespace'] = namespace
3315 hookargs['namespace'] = namespace
3320 hookargs['key'] = key
3316 hookargs['key'] = key
3321 hookargs['old'] = old
3317 hookargs['old'] = old
3322 hookargs['new'] = new
3318 hookargs['new'] = new
3323 self.hook(b'prepushkey', throw=True, **hookargs)
3319 self.hook(b'prepushkey', throw=True, **hookargs)
3324 except error.HookAbort as exc:
3320 except error.HookAbort as exc:
3325 self.ui.write_err(_(b"pushkey-abort: %s\n") % exc)
3321 self.ui.write_err(_(b"pushkey-abort: %s\n") % exc)
3326 if exc.hint:
3322 if exc.hint:
3327 self.ui.write_err(_(b"(%s)\n") % exc.hint)
3323 self.ui.write_err(_(b"(%s)\n") % exc.hint)
3328 return False
3324 return False
3329 self.ui.debug(b'pushing key for "%s:%s"\n' % (namespace, key))
3325 self.ui.debug(b'pushing key for "%s:%s"\n' % (namespace, key))
3330 ret = pushkey.push(self, namespace, key, old, new)
3326 ret = pushkey.push(self, namespace, key, old, new)
3331
3327
3332 def runhook(unused_success):
3328 def runhook(unused_success):
3333 self.hook(
3329 self.hook(
3334 b'pushkey',
3330 b'pushkey',
3335 namespace=namespace,
3331 namespace=namespace,
3336 key=key,
3332 key=key,
3337 old=old,
3333 old=old,
3338 new=new,
3334 new=new,
3339 ret=ret,
3335 ret=ret,
3340 )
3336 )
3341
3337
3342 self._afterlock(runhook)
3338 self._afterlock(runhook)
3343 return ret
3339 return ret
3344
3340
3345 def listkeys(self, namespace):
3341 def listkeys(self, namespace):
3346 self.hook(b'prelistkeys', throw=True, namespace=namespace)
3342 self.hook(b'prelistkeys', throw=True, namespace=namespace)
3347 self.ui.debug(b'listing keys for "%s"\n' % namespace)
3343 self.ui.debug(b'listing keys for "%s"\n' % namespace)
3348 values = pushkey.list(self, namespace)
3344 values = pushkey.list(self, namespace)
3349 self.hook(b'listkeys', namespace=namespace, values=values)
3345 self.hook(b'listkeys', namespace=namespace, values=values)
3350 return values
3346 return values
3351
3347
3352 def debugwireargs(self, one, two, three=None, four=None, five=None):
3348 def debugwireargs(self, one, two, three=None, four=None, five=None):
3353 '''used to test argument passing over the wire'''
3349 '''used to test argument passing over the wire'''
3354 return b"%s %s %s %s %s" % (
3350 return b"%s %s %s %s %s" % (
3355 one,
3351 one,
3356 two,
3352 two,
3357 pycompat.bytestr(three),
3353 pycompat.bytestr(three),
3358 pycompat.bytestr(four),
3354 pycompat.bytestr(four),
3359 pycompat.bytestr(five),
3355 pycompat.bytestr(five),
3360 )
3356 )
3361
3357
3362 def savecommitmessage(self, text):
3358 def savecommitmessage(self, text):
3363 fp = self.vfs(b'last-message.txt', b'wb')
3359 fp = self.vfs(b'last-message.txt', b'wb')
3364 try:
3360 try:
3365 fp.write(text)
3361 fp.write(text)
3366 finally:
3362 finally:
3367 fp.close()
3363 fp.close()
3368 return self.pathto(fp.name[len(self.root) + 1 :])
3364 return self.pathto(fp.name[len(self.root) + 1 :])
3369
3365
3370 def register_wanted_sidedata(self, category):
3366 def register_wanted_sidedata(self, category):
3371 if requirementsmod.REVLOGV2_REQUIREMENT not in self.requirements:
3367 if requirementsmod.REVLOGV2_REQUIREMENT not in self.requirements:
3372 # Only revlogv2 repos can want sidedata.
3368 # Only revlogv2 repos can want sidedata.
3373 return
3369 return
3374 self._wanted_sidedata.add(pycompat.bytestr(category))
3370 self._wanted_sidedata.add(pycompat.bytestr(category))
3375
3371
3376 def register_sidedata_computer(
3372 def register_sidedata_computer(
3377 self, kind, category, keys, computer, flags, replace=False
3373 self, kind, category, keys, computer, flags, replace=False
3378 ):
3374 ):
3379 if kind not in revlogconst.ALL_KINDS:
3375 if kind not in revlogconst.ALL_KINDS:
3380 msg = _(b"unexpected revlog kind '%s'.")
3376 msg = _(b"unexpected revlog kind '%s'.")
3381 raise error.ProgrammingError(msg % kind)
3377 raise error.ProgrammingError(msg % kind)
3382 category = pycompat.bytestr(category)
3378 category = pycompat.bytestr(category)
3383 already_registered = category in self._sidedata_computers.get(kind, [])
3379 already_registered = category in self._sidedata_computers.get(kind, [])
3384 if already_registered and not replace:
3380 if already_registered and not replace:
3385 msg = _(
3381 msg = _(
3386 b"cannot register a sidedata computer twice for category '%s'."
3382 b"cannot register a sidedata computer twice for category '%s'."
3387 )
3383 )
3388 raise error.ProgrammingError(msg % category)
3384 raise error.ProgrammingError(msg % category)
3389 if replace and not already_registered:
3385 if replace and not already_registered:
3390 msg = _(
3386 msg = _(
3391 b"cannot replace a sidedata computer that isn't registered "
3387 b"cannot replace a sidedata computer that isn't registered "
3392 b"for category '%s'."
3388 b"for category '%s'."
3393 )
3389 )
3394 raise error.ProgrammingError(msg % category)
3390 raise error.ProgrammingError(msg % category)
3395 self._sidedata_computers.setdefault(kind, {})
3391 self._sidedata_computers.setdefault(kind, {})
3396 self._sidedata_computers[kind][category] = (keys, computer, flags)
3392 self._sidedata_computers[kind][category] = (keys, computer, flags)
3397
3393
3398
3394
3399 # used to avoid circular references so destructors work
3395 # used to avoid circular references so destructors work
3400 def aftertrans(files):
3396 def aftertrans(files):
3401 renamefiles = [tuple(t) for t in files]
3397 renamefiles = [tuple(t) for t in files]
3402
3398
3403 def a():
3399 def a():
3404 for vfs, src, dest in renamefiles:
3400 for vfs, src, dest in renamefiles:
3405 # if src and dest refer to a same file, vfs.rename is a no-op,
3401 # if src and dest refer to a same file, vfs.rename is a no-op,
3406 # leaving both src and dest on disk. delete dest to make sure
3402 # leaving both src and dest on disk. delete dest to make sure
3407 # the rename couldn't be such a no-op.
3403 # the rename couldn't be such a no-op.
3408 vfs.tryunlink(dest)
3404 vfs.tryunlink(dest)
3409 try:
3405 try:
3410 vfs.rename(src, dest)
3406 vfs.rename(src, dest)
3411 except OSError: # journal file does not yet exist
3407 except OSError: # journal file does not yet exist
3412 pass
3408 pass
3413
3409
3414 return a
3410 return a
3415
3411
3416
3412
3417 def undoname(fn):
3413 def undoname(fn):
3418 base, name = os.path.split(fn)
3414 base, name = os.path.split(fn)
3419 assert name.startswith(b'journal')
3415 assert name.startswith(b'journal')
3420 return os.path.join(base, name.replace(b'journal', b'undo', 1))
3416 return os.path.join(base, name.replace(b'journal', b'undo', 1))
3421
3417
3422
3418
3423 def instance(ui, path, create, intents=None, createopts=None):
3419 def instance(ui, path, create, intents=None, createopts=None):
3424 localpath = urlutil.urllocalpath(path)
3420 localpath = urlutil.urllocalpath(path)
3425 if create:
3421 if create:
3426 createrepository(ui, localpath, createopts=createopts)
3422 createrepository(ui, localpath, createopts=createopts)
3427
3423
3428 return makelocalrepository(ui, localpath, intents=intents)
3424 return makelocalrepository(ui, localpath, intents=intents)
3429
3425
3430
3426
3431 def islocal(path):
3427 def islocal(path):
3432 return True
3428 return True
3433
3429
3434
3430
3435 def defaultcreateopts(ui, createopts=None):
3431 def defaultcreateopts(ui, createopts=None):
3436 """Populate the default creation options for a repository.
3432 """Populate the default creation options for a repository.
3437
3433
3438 A dictionary of explicitly requested creation options can be passed
3434 A dictionary of explicitly requested creation options can be passed
3439 in. Missing keys will be populated.
3435 in. Missing keys will be populated.
3440 """
3436 """
3441 createopts = dict(createopts or {})
3437 createopts = dict(createopts or {})
3442
3438
3443 if b'backend' not in createopts:
3439 if b'backend' not in createopts:
3444 # experimental config: storage.new-repo-backend
3440 # experimental config: storage.new-repo-backend
3445 createopts[b'backend'] = ui.config(b'storage', b'new-repo-backend')
3441 createopts[b'backend'] = ui.config(b'storage', b'new-repo-backend')
3446
3442
3447 return createopts
3443 return createopts
3448
3444
3449
3445
3450 def newreporequirements(ui, createopts):
3446 def newreporequirements(ui, createopts):
3451 """Determine the set of requirements for a new local repository.
3447 """Determine the set of requirements for a new local repository.
3452
3448
3453 Extensions can wrap this function to specify custom requirements for
3449 Extensions can wrap this function to specify custom requirements for
3454 new repositories.
3450 new repositories.
3455 """
3451 """
3456 # If the repo is being created from a shared repository, we copy
3452 # If the repo is being created from a shared repository, we copy
3457 # its requirements.
3453 # its requirements.
3458 if b'sharedrepo' in createopts:
3454 if b'sharedrepo' in createopts:
3459 requirements = set(createopts[b'sharedrepo'].requirements)
3455 requirements = set(createopts[b'sharedrepo'].requirements)
3460 if createopts.get(b'sharedrelative'):
3456 if createopts.get(b'sharedrelative'):
3461 requirements.add(requirementsmod.RELATIVE_SHARED_REQUIREMENT)
3457 requirements.add(requirementsmod.RELATIVE_SHARED_REQUIREMENT)
3462 else:
3458 else:
3463 requirements.add(requirementsmod.SHARED_REQUIREMENT)
3459 requirements.add(requirementsmod.SHARED_REQUIREMENT)
3464
3460
3465 return requirements
3461 return requirements
3466
3462
3467 if b'backend' not in createopts:
3463 if b'backend' not in createopts:
3468 raise error.ProgrammingError(
3464 raise error.ProgrammingError(
3469 b'backend key not present in createopts; '
3465 b'backend key not present in createopts; '
3470 b'was defaultcreateopts() called?'
3466 b'was defaultcreateopts() called?'
3471 )
3467 )
3472
3468
3473 if createopts[b'backend'] != b'revlogv1':
3469 if createopts[b'backend'] != b'revlogv1':
3474 raise error.Abort(
3470 raise error.Abort(
3475 _(
3471 _(
3476 b'unable to determine repository requirements for '
3472 b'unable to determine repository requirements for '
3477 b'storage backend: %s'
3473 b'storage backend: %s'
3478 )
3474 )
3479 % createopts[b'backend']
3475 % createopts[b'backend']
3480 )
3476 )
3481
3477
3482 requirements = {requirementsmod.REVLOGV1_REQUIREMENT}
3478 requirements = {requirementsmod.REVLOGV1_REQUIREMENT}
3483 if ui.configbool(b'format', b'usestore'):
3479 if ui.configbool(b'format', b'usestore'):
3484 requirements.add(requirementsmod.STORE_REQUIREMENT)
3480 requirements.add(requirementsmod.STORE_REQUIREMENT)
3485 if ui.configbool(b'format', b'usefncache'):
3481 if ui.configbool(b'format', b'usefncache'):
3486 requirements.add(requirementsmod.FNCACHE_REQUIREMENT)
3482 requirements.add(requirementsmod.FNCACHE_REQUIREMENT)
3487 if ui.configbool(b'format', b'dotencode'):
3483 if ui.configbool(b'format', b'dotencode'):
3488 requirements.add(requirementsmod.DOTENCODE_REQUIREMENT)
3484 requirements.add(requirementsmod.DOTENCODE_REQUIREMENT)
3489
3485
3490 compengines = ui.configlist(b'format', b'revlog-compression')
3486 compengines = ui.configlist(b'format', b'revlog-compression')
3491 for compengine in compengines:
3487 for compengine in compengines:
3492 if compengine in util.compengines:
3488 if compengine in util.compengines:
3493 engine = util.compengines[compengine]
3489 engine = util.compengines[compengine]
3494 if engine.available() and engine.revlogheader():
3490 if engine.available() and engine.revlogheader():
3495 break
3491 break
3496 else:
3492 else:
3497 raise error.Abort(
3493 raise error.Abort(
3498 _(
3494 _(
3499 b'compression engines %s defined by '
3495 b'compression engines %s defined by '
3500 b'format.revlog-compression not available'
3496 b'format.revlog-compression not available'
3501 )
3497 )
3502 % b', '.join(b'"%s"' % e for e in compengines),
3498 % b', '.join(b'"%s"' % e for e in compengines),
3503 hint=_(
3499 hint=_(
3504 b'run "hg debuginstall" to list available '
3500 b'run "hg debuginstall" to list available '
3505 b'compression engines'
3501 b'compression engines'
3506 ),
3502 ),
3507 )
3503 )
3508
3504
3509 # zlib is the historical default and doesn't need an explicit requirement.
3505 # zlib is the historical default and doesn't need an explicit requirement.
3510 if compengine == b'zstd':
3506 if compengine == b'zstd':
3511 requirements.add(b'revlog-compression-zstd')
3507 requirements.add(b'revlog-compression-zstd')
3512 elif compengine != b'zlib':
3508 elif compengine != b'zlib':
3513 requirements.add(b'exp-compression-%s' % compengine)
3509 requirements.add(b'exp-compression-%s' % compengine)
3514
3510
3515 if scmutil.gdinitconfig(ui):
3511 if scmutil.gdinitconfig(ui):
3516 requirements.add(requirementsmod.GENERALDELTA_REQUIREMENT)
3512 requirements.add(requirementsmod.GENERALDELTA_REQUIREMENT)
3517 if ui.configbool(b'format', b'sparse-revlog'):
3513 if ui.configbool(b'format', b'sparse-revlog'):
3518 requirements.add(requirementsmod.SPARSEREVLOG_REQUIREMENT)
3514 requirements.add(requirementsmod.SPARSEREVLOG_REQUIREMENT)
3519
3515
3520 # experimental config: format.exp-use-side-data
3521 if ui.configbool(b'format', b'exp-use-side-data'):
3522 requirements.discard(requirementsmod.REVLOGV1_REQUIREMENT)
3523 requirements.add(requirementsmod.REVLOGV2_REQUIREMENT)
3524 requirements.add(requirementsmod.SIDEDATA_REQUIREMENT)
3525 # experimental config: format.exp-use-copies-side-data-changeset
3516 # experimental config: format.exp-use-copies-side-data-changeset
3526 if ui.configbool(b'format', b'exp-use-copies-side-data-changeset'):
3517 if ui.configbool(b'format', b'exp-use-copies-side-data-changeset'):
3527 requirements.discard(requirementsmod.REVLOGV1_REQUIREMENT)
3518 requirements.discard(requirementsmod.REVLOGV1_REQUIREMENT)
3528 requirements.add(requirementsmod.REVLOGV2_REQUIREMENT)
3519 requirements.add(requirementsmod.REVLOGV2_REQUIREMENT)
3529 requirements.add(requirementsmod.SIDEDATA_REQUIREMENT)
3530 requirements.add(requirementsmod.COPIESSDC_REQUIREMENT)
3520 requirements.add(requirementsmod.COPIESSDC_REQUIREMENT)
3531 if ui.configbool(b'experimental', b'treemanifest'):
3521 if ui.configbool(b'experimental', b'treemanifest'):
3532 requirements.add(requirementsmod.TREEMANIFEST_REQUIREMENT)
3522 requirements.add(requirementsmod.TREEMANIFEST_REQUIREMENT)
3533
3523
3534 revlogv2 = ui.config(b'experimental', b'revlogv2')
3524 revlogv2 = ui.config(b'experimental', b'revlogv2')
3535 if revlogv2 == b'enable-unstable-format-and-corrupt-my-data':
3525 if revlogv2 == b'enable-unstable-format-and-corrupt-my-data':
3536 requirements.discard(requirementsmod.REVLOGV1_REQUIREMENT)
3526 requirements.discard(requirementsmod.REVLOGV1_REQUIREMENT)
3537 requirements.add(requirementsmod.REVLOGV2_REQUIREMENT)
3527 requirements.add(requirementsmod.REVLOGV2_REQUIREMENT)
3538 # experimental config: format.internal-phase
3528 # experimental config: format.internal-phase
3539 if ui.configbool(b'format', b'internal-phase'):
3529 if ui.configbool(b'format', b'internal-phase'):
3540 requirements.add(requirementsmod.INTERNAL_PHASE_REQUIREMENT)
3530 requirements.add(requirementsmod.INTERNAL_PHASE_REQUIREMENT)
3541
3531
3542 if createopts.get(b'narrowfiles'):
3532 if createopts.get(b'narrowfiles'):
3543 requirements.add(requirementsmod.NARROW_REQUIREMENT)
3533 requirements.add(requirementsmod.NARROW_REQUIREMENT)
3544
3534
3545 if createopts.get(b'lfs'):
3535 if createopts.get(b'lfs'):
3546 requirements.add(b'lfs')
3536 requirements.add(b'lfs')
3547
3537
3548 if ui.configbool(b'format', b'bookmarks-in-store'):
3538 if ui.configbool(b'format', b'bookmarks-in-store'):
3549 requirements.add(bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT)
3539 requirements.add(bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT)
3550
3540
3551 if ui.configbool(b'format', b'use-persistent-nodemap'):
3541 if ui.configbool(b'format', b'use-persistent-nodemap'):
3552 requirements.add(requirementsmod.NODEMAP_REQUIREMENT)
3542 requirements.add(requirementsmod.NODEMAP_REQUIREMENT)
3553
3543
3554 # if share-safe is enabled, let's create the new repository with the new
3544 # if share-safe is enabled, let's create the new repository with the new
3555 # requirement
3545 # requirement
3556 if ui.configbool(b'format', b'use-share-safe'):
3546 if ui.configbool(b'format', b'use-share-safe'):
3557 requirements.add(requirementsmod.SHARESAFE_REQUIREMENT)
3547 requirements.add(requirementsmod.SHARESAFE_REQUIREMENT)
3558
3548
3559 return requirements
3549 return requirements
3560
3550
3561
3551
3562 def checkrequirementscompat(ui, requirements):
3552 def checkrequirementscompat(ui, requirements):
3563 """Checks compatibility of repository requirements enabled and disabled.
3553 """Checks compatibility of repository requirements enabled and disabled.
3564
3554
3565 Returns a set of requirements which needs to be dropped because dependend
3555 Returns a set of requirements which needs to be dropped because dependend
3566 requirements are not enabled. Also warns users about it"""
3556 requirements are not enabled. Also warns users about it"""
3567
3557
3568 dropped = set()
3558 dropped = set()
3569
3559
3570 if requirementsmod.STORE_REQUIREMENT not in requirements:
3560 if requirementsmod.STORE_REQUIREMENT not in requirements:
3571 if bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT in requirements:
3561 if bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT in requirements:
3572 ui.warn(
3562 ui.warn(
3573 _(
3563 _(
3574 b'ignoring enabled \'format.bookmarks-in-store\' config '
3564 b'ignoring enabled \'format.bookmarks-in-store\' config '
3575 b'beacuse it is incompatible with disabled '
3565 b'beacuse it is incompatible with disabled '
3576 b'\'format.usestore\' config\n'
3566 b'\'format.usestore\' config\n'
3577 )
3567 )
3578 )
3568 )
3579 dropped.add(bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT)
3569 dropped.add(bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT)
3580
3570
3581 if (
3571 if (
3582 requirementsmod.SHARED_REQUIREMENT in requirements
3572 requirementsmod.SHARED_REQUIREMENT in requirements
3583 or requirementsmod.RELATIVE_SHARED_REQUIREMENT in requirements
3573 or requirementsmod.RELATIVE_SHARED_REQUIREMENT in requirements
3584 ):
3574 ):
3585 raise error.Abort(
3575 raise error.Abort(
3586 _(
3576 _(
3587 b"cannot create shared repository as source was created"
3577 b"cannot create shared repository as source was created"
3588 b" with 'format.usestore' config disabled"
3578 b" with 'format.usestore' config disabled"
3589 )
3579 )
3590 )
3580 )
3591
3581
3592 if requirementsmod.SHARESAFE_REQUIREMENT in requirements:
3582 if requirementsmod.SHARESAFE_REQUIREMENT in requirements:
3593 ui.warn(
3583 ui.warn(
3594 _(
3584 _(
3595 b"ignoring enabled 'format.use-share-safe' config because "
3585 b"ignoring enabled 'format.use-share-safe' config because "
3596 b"it is incompatible with disabled 'format.usestore'"
3586 b"it is incompatible with disabled 'format.usestore'"
3597 b" config\n"
3587 b" config\n"
3598 )
3588 )
3599 )
3589 )
3600 dropped.add(requirementsmod.SHARESAFE_REQUIREMENT)
3590 dropped.add(requirementsmod.SHARESAFE_REQUIREMENT)
3601
3591
3602 return dropped
3592 return dropped
3603
3593
3604
3594
3605 def filterknowncreateopts(ui, createopts):
3595 def filterknowncreateopts(ui, createopts):
3606 """Filters a dict of repo creation options against options that are known.
3596 """Filters a dict of repo creation options against options that are known.
3607
3597
3608 Receives a dict of repo creation options and returns a dict of those
3598 Receives a dict of repo creation options and returns a dict of those
3609 options that we don't know how to handle.
3599 options that we don't know how to handle.
3610
3600
3611 This function is called as part of repository creation. If the
3601 This function is called as part of repository creation. If the
3612 returned dict contains any items, repository creation will not
3602 returned dict contains any items, repository creation will not
3613 be allowed, as it means there was a request to create a repository
3603 be allowed, as it means there was a request to create a repository
3614 with options not recognized by loaded code.
3604 with options not recognized by loaded code.
3615
3605
3616 Extensions can wrap this function to filter out creation options
3606 Extensions can wrap this function to filter out creation options
3617 they know how to handle.
3607 they know how to handle.
3618 """
3608 """
3619 known = {
3609 known = {
3620 b'backend',
3610 b'backend',
3621 b'lfs',
3611 b'lfs',
3622 b'narrowfiles',
3612 b'narrowfiles',
3623 b'sharedrepo',
3613 b'sharedrepo',
3624 b'sharedrelative',
3614 b'sharedrelative',
3625 b'shareditems',
3615 b'shareditems',
3626 b'shallowfilestore',
3616 b'shallowfilestore',
3627 }
3617 }
3628
3618
3629 return {k: v for k, v in createopts.items() if k not in known}
3619 return {k: v for k, v in createopts.items() if k not in known}
3630
3620
3631
3621
3632 def createrepository(ui, path, createopts=None):
3622 def createrepository(ui, path, createopts=None):
3633 """Create a new repository in a vfs.
3623 """Create a new repository in a vfs.
3634
3624
3635 ``path`` path to the new repo's working directory.
3625 ``path`` path to the new repo's working directory.
3636 ``createopts`` options for the new repository.
3626 ``createopts`` options for the new repository.
3637
3627
3638 The following keys for ``createopts`` are recognized:
3628 The following keys for ``createopts`` are recognized:
3639
3629
3640 backend
3630 backend
3641 The storage backend to use.
3631 The storage backend to use.
3642 lfs
3632 lfs
3643 Repository will be created with ``lfs`` requirement. The lfs extension
3633 Repository will be created with ``lfs`` requirement. The lfs extension
3644 will automatically be loaded when the repository is accessed.
3634 will automatically be loaded when the repository is accessed.
3645 narrowfiles
3635 narrowfiles
3646 Set up repository to support narrow file storage.
3636 Set up repository to support narrow file storage.
3647 sharedrepo
3637 sharedrepo
3648 Repository object from which storage should be shared.
3638 Repository object from which storage should be shared.
3649 sharedrelative
3639 sharedrelative
3650 Boolean indicating if the path to the shared repo should be
3640 Boolean indicating if the path to the shared repo should be
3651 stored as relative. By default, the pointer to the "parent" repo
3641 stored as relative. By default, the pointer to the "parent" repo
3652 is stored as an absolute path.
3642 is stored as an absolute path.
3653 shareditems
3643 shareditems
3654 Set of items to share to the new repository (in addition to storage).
3644 Set of items to share to the new repository (in addition to storage).
3655 shallowfilestore
3645 shallowfilestore
3656 Indicates that storage for files should be shallow (not all ancestor
3646 Indicates that storage for files should be shallow (not all ancestor
3657 revisions are known).
3647 revisions are known).
3658 """
3648 """
3659 createopts = defaultcreateopts(ui, createopts=createopts)
3649 createopts = defaultcreateopts(ui, createopts=createopts)
3660
3650
3661 unknownopts = filterknowncreateopts(ui, createopts)
3651 unknownopts = filterknowncreateopts(ui, createopts)
3662
3652
3663 if not isinstance(unknownopts, dict):
3653 if not isinstance(unknownopts, dict):
3664 raise error.ProgrammingError(
3654 raise error.ProgrammingError(
3665 b'filterknowncreateopts() did not return a dict'
3655 b'filterknowncreateopts() did not return a dict'
3666 )
3656 )
3667
3657
3668 if unknownopts:
3658 if unknownopts:
3669 raise error.Abort(
3659 raise error.Abort(
3670 _(
3660 _(
3671 b'unable to create repository because of unknown '
3661 b'unable to create repository because of unknown '
3672 b'creation option: %s'
3662 b'creation option: %s'
3673 )
3663 )
3674 % b', '.join(sorted(unknownopts)),
3664 % b', '.join(sorted(unknownopts)),
3675 hint=_(b'is a required extension not loaded?'),
3665 hint=_(b'is a required extension not loaded?'),
3676 )
3666 )
3677
3667
3678 requirements = newreporequirements(ui, createopts=createopts)
3668 requirements = newreporequirements(ui, createopts=createopts)
3679 requirements -= checkrequirementscompat(ui, requirements)
3669 requirements -= checkrequirementscompat(ui, requirements)
3680
3670
3681 wdirvfs = vfsmod.vfs(path, expandpath=True, realpath=True)
3671 wdirvfs = vfsmod.vfs(path, expandpath=True, realpath=True)
3682
3672
3683 hgvfs = vfsmod.vfs(wdirvfs.join(b'.hg'))
3673 hgvfs = vfsmod.vfs(wdirvfs.join(b'.hg'))
3684 if hgvfs.exists():
3674 if hgvfs.exists():
3685 raise error.RepoError(_(b'repository %s already exists') % path)
3675 raise error.RepoError(_(b'repository %s already exists') % path)
3686
3676
3687 if b'sharedrepo' in createopts:
3677 if b'sharedrepo' in createopts:
3688 sharedpath = createopts[b'sharedrepo'].sharedpath
3678 sharedpath = createopts[b'sharedrepo'].sharedpath
3689
3679
3690 if createopts.get(b'sharedrelative'):
3680 if createopts.get(b'sharedrelative'):
3691 try:
3681 try:
3692 sharedpath = os.path.relpath(sharedpath, hgvfs.base)
3682 sharedpath = os.path.relpath(sharedpath, hgvfs.base)
3693 sharedpath = util.pconvert(sharedpath)
3683 sharedpath = util.pconvert(sharedpath)
3694 except (IOError, ValueError) as e:
3684 except (IOError, ValueError) as e:
3695 # ValueError is raised on Windows if the drive letters differ
3685 # ValueError is raised on Windows if the drive letters differ
3696 # on each path.
3686 # on each path.
3697 raise error.Abort(
3687 raise error.Abort(
3698 _(b'cannot calculate relative path'),
3688 _(b'cannot calculate relative path'),
3699 hint=stringutil.forcebytestr(e),
3689 hint=stringutil.forcebytestr(e),
3700 )
3690 )
3701
3691
3702 if not wdirvfs.exists():
3692 if not wdirvfs.exists():
3703 wdirvfs.makedirs()
3693 wdirvfs.makedirs()
3704
3694
3705 hgvfs.makedir(notindexed=True)
3695 hgvfs.makedir(notindexed=True)
3706 if b'sharedrepo' not in createopts:
3696 if b'sharedrepo' not in createopts:
3707 hgvfs.mkdir(b'cache')
3697 hgvfs.mkdir(b'cache')
3708 hgvfs.mkdir(b'wcache')
3698 hgvfs.mkdir(b'wcache')
3709
3699
3710 has_store = requirementsmod.STORE_REQUIREMENT in requirements
3700 has_store = requirementsmod.STORE_REQUIREMENT in requirements
3711 if has_store and b'sharedrepo' not in createopts:
3701 if has_store and b'sharedrepo' not in createopts:
3712 hgvfs.mkdir(b'store')
3702 hgvfs.mkdir(b'store')
3713
3703
3714 # We create an invalid changelog outside the store so very old
3704 # We create an invalid changelog outside the store so very old
3715 # Mercurial versions (which didn't know about the requirements
3705 # Mercurial versions (which didn't know about the requirements
3716 # file) encounter an error on reading the changelog. This
3706 # file) encounter an error on reading the changelog. This
3717 # effectively locks out old clients and prevents them from
3707 # effectively locks out old clients and prevents them from
3718 # mucking with a repo in an unknown format.
3708 # mucking with a repo in an unknown format.
3719 #
3709 #
3720 # The revlog header has version 65535, which won't be recognized by
3710 # The revlog header has version 65535, which won't be recognized by
3721 # such old clients.
3711 # such old clients.
3722 hgvfs.append(
3712 hgvfs.append(
3723 b'00changelog.i',
3713 b'00changelog.i',
3724 b'\0\0\xFF\xFF dummy changelog to prevent using the old repo '
3714 b'\0\0\xFF\xFF dummy changelog to prevent using the old repo '
3725 b'layout',
3715 b'layout',
3726 )
3716 )
3727
3717
3728 # Filter the requirements into working copy and store ones
3718 # Filter the requirements into working copy and store ones
3729 wcreq, storereq = scmutil.filterrequirements(requirements)
3719 wcreq, storereq = scmutil.filterrequirements(requirements)
3730 # write working copy ones
3720 # write working copy ones
3731 scmutil.writerequires(hgvfs, wcreq)
3721 scmutil.writerequires(hgvfs, wcreq)
3732 # If there are store requirements and the current repository
3722 # If there are store requirements and the current repository
3733 # is not a shared one, write stored requirements
3723 # is not a shared one, write stored requirements
3734 # For new shared repository, we don't need to write the store
3724 # For new shared repository, we don't need to write the store
3735 # requirements as they are already present in store requires
3725 # requirements as they are already present in store requires
3736 if storereq and b'sharedrepo' not in createopts:
3726 if storereq and b'sharedrepo' not in createopts:
3737 storevfs = vfsmod.vfs(hgvfs.join(b'store'), cacheaudited=True)
3727 storevfs = vfsmod.vfs(hgvfs.join(b'store'), cacheaudited=True)
3738 scmutil.writerequires(storevfs, storereq)
3728 scmutil.writerequires(storevfs, storereq)
3739
3729
3740 # Write out file telling readers where to find the shared store.
3730 # Write out file telling readers where to find the shared store.
3741 if b'sharedrepo' in createopts:
3731 if b'sharedrepo' in createopts:
3742 hgvfs.write(b'sharedpath', sharedpath)
3732 hgvfs.write(b'sharedpath', sharedpath)
3743
3733
3744 if createopts.get(b'shareditems'):
3734 if createopts.get(b'shareditems'):
3745 shared = b'\n'.join(sorted(createopts[b'shareditems'])) + b'\n'
3735 shared = b'\n'.join(sorted(createopts[b'shareditems'])) + b'\n'
3746 hgvfs.write(b'shared', shared)
3736 hgvfs.write(b'shared', shared)
3747
3737
3748
3738
3749 def poisonrepository(repo):
3739 def poisonrepository(repo):
3750 """Poison a repository instance so it can no longer be used."""
3740 """Poison a repository instance so it can no longer be used."""
3751 # Perform any cleanup on the instance.
3741 # Perform any cleanup on the instance.
3752 repo.close()
3742 repo.close()
3753
3743
3754 # Our strategy is to replace the type of the object with one that
3744 # Our strategy is to replace the type of the object with one that
3755 # has all attribute lookups result in error.
3745 # has all attribute lookups result in error.
3756 #
3746 #
3757 # But we have to allow the close() method because some constructors
3747 # But we have to allow the close() method because some constructors
3758 # of repos call close() on repo references.
3748 # of repos call close() on repo references.
3759 class poisonedrepository(object):
3749 class poisonedrepository(object):
3760 def __getattribute__(self, item):
3750 def __getattribute__(self, item):
3761 if item == 'close':
3751 if item == 'close':
3762 return object.__getattribute__(self, item)
3752 return object.__getattribute__(self, item)
3763
3753
3764 raise error.ProgrammingError(
3754 raise error.ProgrammingError(
3765 b'repo instances should not be used after unshare'
3755 b'repo instances should not be used after unshare'
3766 )
3756 )
3767
3757
3768 def close(self):
3758 def close(self):
3769 pass
3759 pass
3770
3760
3771 # We may have a repoview, which intercepts __setattr__. So be sure
3761 # We may have a repoview, which intercepts __setattr__. So be sure
3772 # we operate at the lowest level possible.
3762 # we operate at the lowest level possible.
3773 object.__setattr__(repo, '__class__', poisonedrepository)
3763 object.__setattr__(repo, '__class__', poisonedrepository)
@@ -1,82 +1,78 b''
1 # requirements.py - objects and functions related to repository requirements
1 # requirements.py - objects and functions related to repository requirements
2 #
2 #
3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
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 GENERALDELTA_REQUIREMENT = b'generaldelta'
10 GENERALDELTA_REQUIREMENT = b'generaldelta'
11 DOTENCODE_REQUIREMENT = b'dotencode'
11 DOTENCODE_REQUIREMENT = b'dotencode'
12 STORE_REQUIREMENT = b'store'
12 STORE_REQUIREMENT = b'store'
13 FNCACHE_REQUIREMENT = b'fncache'
13 FNCACHE_REQUIREMENT = b'fncache'
14
14
15 # When narrowing is finalized and no longer subject to format changes,
15 # When narrowing is finalized and no longer subject to format changes,
16 # we should move this to just "narrow" or similar.
16 # we should move this to just "narrow" or similar.
17 NARROW_REQUIREMENT = b'narrowhg-experimental'
17 NARROW_REQUIREMENT = b'narrowhg-experimental'
18
18
19 # Enables sparse working directory usage
19 # Enables sparse working directory usage
20 SPARSE_REQUIREMENT = b'exp-sparse'
20 SPARSE_REQUIREMENT = b'exp-sparse'
21
21
22 # Enables the internal phase which is used to hide changesets instead
22 # Enables the internal phase which is used to hide changesets instead
23 # of stripping them
23 # of stripping them
24 INTERNAL_PHASE_REQUIREMENT = b'internal-phase'
24 INTERNAL_PHASE_REQUIREMENT = b'internal-phase'
25
25
26 # Stores manifest in Tree structure
26 # Stores manifest in Tree structure
27 TREEMANIFEST_REQUIREMENT = b'treemanifest'
27 TREEMANIFEST_REQUIREMENT = b'treemanifest'
28
28
29 REVLOGV1_REQUIREMENT = b'revlogv1'
29 REVLOGV1_REQUIREMENT = b'revlogv1'
30
30
31 # Increment the sub-version when the revlog v2 format changes to lock out old
31 # Increment the sub-version when the revlog v2 format changes to lock out old
32 # clients.
32 # clients.
33 REVLOGV2_REQUIREMENT = b'exp-revlogv2.2'
33 REVLOGV2_REQUIREMENT = b'exp-revlogv2.2'
34
34
35 # A repository with the sparserevlog feature will have delta chains that
35 # A repository with the sparserevlog feature will have delta chains that
36 # can spread over a larger span. Sparse reading cuts these large spans into
36 # can spread over a larger span. Sparse reading cuts these large spans into
37 # pieces, so that each piece isn't too big.
37 # pieces, so that each piece isn't too big.
38 # Without the sparserevlog capability, reading from the repository could use
38 # Without the sparserevlog capability, reading from the repository could use
39 # huge amounts of memory, because the whole span would be read at once,
39 # huge amounts of memory, because the whole span would be read at once,
40 # including all the intermediate revisions that aren't pertinent for the chain.
40 # including all the intermediate revisions that aren't pertinent for the chain.
41 # This is why once a repository has enabled sparse-read, it becomes required.
41 # This is why once a repository has enabled sparse-read, it becomes required.
42 SPARSEREVLOG_REQUIREMENT = b'sparserevlog'
42 SPARSEREVLOG_REQUIREMENT = b'sparserevlog'
43
43
44 # A repository with the sidedataflag requirement will allow to store extra
45 # information for revision without altering their original hashes.
46 SIDEDATA_REQUIREMENT = b'exp-sidedata-flag'
47
48 # A repository with the the copies-sidedata-changeset requirement will store
44 # A repository with the the copies-sidedata-changeset requirement will store
49 # copies related information in changeset's sidedata.
45 # copies related information in changeset's sidedata.
50 COPIESSDC_REQUIREMENT = b'exp-copies-sidedata-changeset'
46 COPIESSDC_REQUIREMENT = b'exp-copies-sidedata-changeset'
51
47
52 # The repository use persistent nodemap for the changelog and the manifest.
48 # The repository use persistent nodemap for the changelog and the manifest.
53 NODEMAP_REQUIREMENT = b'persistent-nodemap'
49 NODEMAP_REQUIREMENT = b'persistent-nodemap'
54
50
55 # Denotes that the current repository is a share
51 # Denotes that the current repository is a share
56 SHARED_REQUIREMENT = b'shared'
52 SHARED_REQUIREMENT = b'shared'
57
53
58 # Denotes that current repository is a share and the shared source path is
54 # Denotes that current repository is a share and the shared source path is
59 # relative to the current repository root path
55 # relative to the current repository root path
60 RELATIVE_SHARED_REQUIREMENT = b'relshared'
56 RELATIVE_SHARED_REQUIREMENT = b'relshared'
61
57
62 # A repository with share implemented safely. The repository has different
58 # A repository with share implemented safely. The repository has different
63 # store and working copy requirements i.e. both `.hg/requires` and
59 # store and working copy requirements i.e. both `.hg/requires` and
64 # `.hg/store/requires` are present.
60 # `.hg/store/requires` are present.
65 SHARESAFE_REQUIREMENT = b'share-safe'
61 SHARESAFE_REQUIREMENT = b'share-safe'
66
62
67 # List of requirements which are working directory specific
63 # List of requirements which are working directory specific
68 # These requirements cannot be shared between repositories if they
64 # These requirements cannot be shared between repositories if they
69 # share the same store
65 # share the same store
70 # * sparse is a working directory specific functionality and hence working
66 # * sparse is a working directory specific functionality and hence working
71 # directory specific requirement
67 # directory specific requirement
72 # * SHARED_REQUIREMENT and RELATIVE_SHARED_REQUIREMENT are requirements which
68 # * SHARED_REQUIREMENT and RELATIVE_SHARED_REQUIREMENT are requirements which
73 # represents that the current working copy/repository shares store of another
69 # represents that the current working copy/repository shares store of another
74 # repo. Hence both of them should be stored in working copy
70 # repo. Hence both of them should be stored in working copy
75 # * SHARESAFE_REQUIREMENT needs to be stored in working dir to mark that rest of
71 # * SHARESAFE_REQUIREMENT needs to be stored in working dir to mark that rest of
76 # the requirements are stored in store's requires
72 # the requirements are stored in store's requires
77 WORKING_DIR_REQUIREMENTS = {
73 WORKING_DIR_REQUIREMENTS = {
78 SPARSE_REQUIREMENT,
74 SPARSE_REQUIREMENT,
79 SHARED_REQUIREMENT,
75 SHARED_REQUIREMENT,
80 RELATIVE_SHARED_REQUIREMENT,
76 RELATIVE_SHARED_REQUIREMENT,
81 SHARESAFE_REQUIREMENT,
77 SHARESAFE_REQUIREMENT,
82 }
78 }
@@ -1,1040 +1,1037 b''
1 # upgrade.py - functions for in place upgrade of Mercurial repository
1 # upgrade.py - functions for in place upgrade of Mercurial repository
2 #
2 #
3 # Copyright (c) 2016-present, Gregory Szorc
3 # Copyright (c) 2016-present, Gregory Szorc
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 from ..i18n import _
10 from ..i18n import _
11 from .. import (
11 from .. import (
12 error,
12 error,
13 localrepo,
13 localrepo,
14 pycompat,
14 pycompat,
15 requirements,
15 requirements,
16 revlog,
16 revlog,
17 util,
17 util,
18 )
18 )
19
19
20 from ..utils import compression
20 from ..utils import compression
21
21
22 if pycompat.TYPE_CHECKING:
22 if pycompat.TYPE_CHECKING:
23 from typing import (
23 from typing import (
24 List,
24 List,
25 Type,
25 Type,
26 )
26 )
27
27
28
28
29 # list of requirements that request a clone of all revlog if added/removed
29 # list of requirements that request a clone of all revlog if added/removed
30 RECLONES_REQUIREMENTS = {
30 RECLONES_REQUIREMENTS = {
31 requirements.GENERALDELTA_REQUIREMENT,
31 requirements.GENERALDELTA_REQUIREMENT,
32 requirements.SPARSEREVLOG_REQUIREMENT,
32 requirements.SPARSEREVLOG_REQUIREMENT,
33 }
33 }
34
34
35
35
36 def preservedrequirements(repo):
36 def preservedrequirements(repo):
37 return set()
37 return set()
38
38
39
39
40 FORMAT_VARIANT = b'deficiency'
40 FORMAT_VARIANT = b'deficiency'
41 OPTIMISATION = b'optimization'
41 OPTIMISATION = b'optimization'
42
42
43
43
44 class improvement(object):
44 class improvement(object):
45 """Represents an improvement that can be made as part of an upgrade.
45 """Represents an improvement that can be made as part of an upgrade.
46
46
47 The following attributes are defined on each instance:
47 The following attributes are defined on each instance:
48
48
49 name
49 name
50 Machine-readable string uniquely identifying this improvement. It
50 Machine-readable string uniquely identifying this improvement. It
51 will be mapped to an action later in the upgrade process.
51 will be mapped to an action later in the upgrade process.
52
52
53 type
53 type
54 Either ``FORMAT_VARIANT`` or ``OPTIMISATION``.
54 Either ``FORMAT_VARIANT`` or ``OPTIMISATION``.
55 A format variant is where we change the storage format. Not all format
55 A format variant is where we change the storage format. Not all format
56 variant changes are an obvious problem.
56 variant changes are an obvious problem.
57 An optimization is an action (sometimes optional) that
57 An optimization is an action (sometimes optional) that
58 can be taken to further improve the state of the repository.
58 can be taken to further improve the state of the repository.
59
59
60 description
60 description
61 Message intended for humans explaining the improvement in more detail,
61 Message intended for humans explaining the improvement in more detail,
62 including the implications of it. For ``FORMAT_VARIANT`` types, should be
62 including the implications of it. For ``FORMAT_VARIANT`` types, should be
63 worded in the present tense. For ``OPTIMISATION`` types, should be
63 worded in the present tense. For ``OPTIMISATION`` types, should be
64 worded in the future tense.
64 worded in the future tense.
65
65
66 upgrademessage
66 upgrademessage
67 Message intended for humans explaining what an upgrade addressing this
67 Message intended for humans explaining what an upgrade addressing this
68 issue will do. Should be worded in the future tense.
68 issue will do. Should be worded in the future tense.
69
69
70 postupgrademessage
70 postupgrademessage
71 Message intended for humans which will be shown post an upgrade
71 Message intended for humans which will be shown post an upgrade
72 operation when the improvement will be added
72 operation when the improvement will be added
73
73
74 postdowngrademessage
74 postdowngrademessage
75 Message intended for humans which will be shown post an upgrade
75 Message intended for humans which will be shown post an upgrade
76 operation in which this improvement was removed
76 operation in which this improvement was removed
77
77
78 touches_filelogs (bool)
78 touches_filelogs (bool)
79 Whether this improvement touches filelogs
79 Whether this improvement touches filelogs
80
80
81 touches_manifests (bool)
81 touches_manifests (bool)
82 Whether this improvement touches manifests
82 Whether this improvement touches manifests
83
83
84 touches_changelog (bool)
84 touches_changelog (bool)
85 Whether this improvement touches changelog
85 Whether this improvement touches changelog
86
86
87 touches_requirements (bool)
87 touches_requirements (bool)
88 Whether this improvement changes repository requirements
88 Whether this improvement changes repository requirements
89 """
89 """
90
90
91 def __init__(self, name, type, description, upgrademessage):
91 def __init__(self, name, type, description, upgrademessage):
92 self.name = name
92 self.name = name
93 self.type = type
93 self.type = type
94 self.description = description
94 self.description = description
95 self.upgrademessage = upgrademessage
95 self.upgrademessage = upgrademessage
96 self.postupgrademessage = None
96 self.postupgrademessage = None
97 self.postdowngrademessage = None
97 self.postdowngrademessage = None
98 # By default for now, we assume every improvement touches
98 # By default for now, we assume every improvement touches
99 # all the things
99 # all the things
100 self.touches_filelogs = True
100 self.touches_filelogs = True
101 self.touches_manifests = True
101 self.touches_manifests = True
102 self.touches_changelog = True
102 self.touches_changelog = True
103 self.touches_requirements = True
103 self.touches_requirements = True
104
104
105 def __eq__(self, other):
105 def __eq__(self, other):
106 if not isinstance(other, improvement):
106 if not isinstance(other, improvement):
107 # This is what python tell use to do
107 # This is what python tell use to do
108 return NotImplemented
108 return NotImplemented
109 return self.name == other.name
109 return self.name == other.name
110
110
111 def __ne__(self, other):
111 def __ne__(self, other):
112 return not (self == other)
112 return not (self == other)
113
113
114 def __hash__(self):
114 def __hash__(self):
115 return hash(self.name)
115 return hash(self.name)
116
116
117
117
118 allformatvariant = [] # type: List[Type['formatvariant']]
118 allformatvariant = [] # type: List[Type['formatvariant']]
119
119
120
120
121 def registerformatvariant(cls):
121 def registerformatvariant(cls):
122 allformatvariant.append(cls)
122 allformatvariant.append(cls)
123 return cls
123 return cls
124
124
125
125
126 class formatvariant(improvement):
126 class formatvariant(improvement):
127 """an improvement subclass dedicated to repository format"""
127 """an improvement subclass dedicated to repository format"""
128
128
129 type = FORMAT_VARIANT
129 type = FORMAT_VARIANT
130 ### The following attributes should be defined for each class:
130 ### The following attributes should be defined for each class:
131
131
132 # machine-readable string uniquely identifying this improvement. it will be
132 # machine-readable string uniquely identifying this improvement. it will be
133 # mapped to an action later in the upgrade process.
133 # mapped to an action later in the upgrade process.
134 name = None
134 name = None
135
135
136 # message intended for humans explaining the improvement in more detail,
136 # message intended for humans explaining the improvement in more detail,
137 # including the implications of it ``FORMAT_VARIANT`` types, should be
137 # including the implications of it ``FORMAT_VARIANT`` types, should be
138 # worded
138 # worded
139 # in the present tense.
139 # in the present tense.
140 description = None
140 description = None
141
141
142 # message intended for humans explaining what an upgrade addressing this
142 # message intended for humans explaining what an upgrade addressing this
143 # issue will do. should be worded in the future tense.
143 # issue will do. should be worded in the future tense.
144 upgrademessage = None
144 upgrademessage = None
145
145
146 # value of current Mercurial default for new repository
146 # value of current Mercurial default for new repository
147 default = None
147 default = None
148
148
149 # Message intended for humans which will be shown post an upgrade
149 # Message intended for humans which will be shown post an upgrade
150 # operation when the improvement will be added
150 # operation when the improvement will be added
151 postupgrademessage = None
151 postupgrademessage = None
152
152
153 # Message intended for humans which will be shown post an upgrade
153 # Message intended for humans which will be shown post an upgrade
154 # operation in which this improvement was removed
154 # operation in which this improvement was removed
155 postdowngrademessage = None
155 postdowngrademessage = None
156
156
157 # By default for now, we assume every improvement touches all the things
157 # By default for now, we assume every improvement touches all the things
158 touches_filelogs = True
158 touches_filelogs = True
159 touches_manifests = True
159 touches_manifests = True
160 touches_changelog = True
160 touches_changelog = True
161 touches_requirements = True
161 touches_requirements = True
162
162
163 def __init__(self):
163 def __init__(self):
164 raise NotImplementedError()
164 raise NotImplementedError()
165
165
166 @staticmethod
166 @staticmethod
167 def fromrepo(repo):
167 def fromrepo(repo):
168 """current value of the variant in the repository"""
168 """current value of the variant in the repository"""
169 raise NotImplementedError()
169 raise NotImplementedError()
170
170
171 @staticmethod
171 @staticmethod
172 def fromconfig(repo):
172 def fromconfig(repo):
173 """current value of the variant in the configuration"""
173 """current value of the variant in the configuration"""
174 raise NotImplementedError()
174 raise NotImplementedError()
175
175
176
176
177 class requirementformatvariant(formatvariant):
177 class requirementformatvariant(formatvariant):
178 """formatvariant based on a 'requirement' name.
178 """formatvariant based on a 'requirement' name.
179
179
180 Many format variant are controlled by a 'requirement'. We define a small
180 Many format variant are controlled by a 'requirement'. We define a small
181 subclass to factor the code.
181 subclass to factor the code.
182 """
182 """
183
183
184 # the requirement that control this format variant
184 # the requirement that control this format variant
185 _requirement = None
185 _requirement = None
186
186
187 @staticmethod
187 @staticmethod
188 def _newreporequirements(ui):
188 def _newreporequirements(ui):
189 return localrepo.newreporequirements(
189 return localrepo.newreporequirements(
190 ui, localrepo.defaultcreateopts(ui)
190 ui, localrepo.defaultcreateopts(ui)
191 )
191 )
192
192
193 @classmethod
193 @classmethod
194 def fromrepo(cls, repo):
194 def fromrepo(cls, repo):
195 assert cls._requirement is not None
195 assert cls._requirement is not None
196 return cls._requirement in repo.requirements
196 return cls._requirement in repo.requirements
197
197
198 @classmethod
198 @classmethod
199 def fromconfig(cls, repo):
199 def fromconfig(cls, repo):
200 assert cls._requirement is not None
200 assert cls._requirement is not None
201 return cls._requirement in cls._newreporequirements(repo.ui)
201 return cls._requirement in cls._newreporequirements(repo.ui)
202
202
203
203
204 @registerformatvariant
204 @registerformatvariant
205 class fncache(requirementformatvariant):
205 class fncache(requirementformatvariant):
206 name = b'fncache'
206 name = b'fncache'
207
207
208 _requirement = requirements.FNCACHE_REQUIREMENT
208 _requirement = requirements.FNCACHE_REQUIREMENT
209
209
210 default = True
210 default = True
211
211
212 description = _(
212 description = _(
213 b'long and reserved filenames may not work correctly; '
213 b'long and reserved filenames may not work correctly; '
214 b'repository performance is sub-optimal'
214 b'repository performance is sub-optimal'
215 )
215 )
216
216
217 upgrademessage = _(
217 upgrademessage = _(
218 b'repository will be more resilient to storing '
218 b'repository will be more resilient to storing '
219 b'certain paths and performance of certain '
219 b'certain paths and performance of certain '
220 b'operations should be improved'
220 b'operations should be improved'
221 )
221 )
222
222
223
223
224 @registerformatvariant
224 @registerformatvariant
225 class dotencode(requirementformatvariant):
225 class dotencode(requirementformatvariant):
226 name = b'dotencode'
226 name = b'dotencode'
227
227
228 _requirement = requirements.DOTENCODE_REQUIREMENT
228 _requirement = requirements.DOTENCODE_REQUIREMENT
229
229
230 default = True
230 default = True
231
231
232 description = _(
232 description = _(
233 b'storage of filenames beginning with a period or '
233 b'storage of filenames beginning with a period or '
234 b'space may not work correctly'
234 b'space may not work correctly'
235 )
235 )
236
236
237 upgrademessage = _(
237 upgrademessage = _(
238 b'repository will be better able to store files '
238 b'repository will be better able to store files '
239 b'beginning with a space or period'
239 b'beginning with a space or period'
240 )
240 )
241
241
242
242
243 @registerformatvariant
243 @registerformatvariant
244 class generaldelta(requirementformatvariant):
244 class generaldelta(requirementformatvariant):
245 name = b'generaldelta'
245 name = b'generaldelta'
246
246
247 _requirement = requirements.GENERALDELTA_REQUIREMENT
247 _requirement = requirements.GENERALDELTA_REQUIREMENT
248
248
249 default = True
249 default = True
250
250
251 description = _(
251 description = _(
252 b'deltas within internal storage are unable to '
252 b'deltas within internal storage are unable to '
253 b'choose optimal revisions; repository is larger and '
253 b'choose optimal revisions; repository is larger and '
254 b'slower than it could be; interaction with other '
254 b'slower than it could be; interaction with other '
255 b'repositories may require extra network and CPU '
255 b'repositories may require extra network and CPU '
256 b'resources, making "hg push" and "hg pull" slower'
256 b'resources, making "hg push" and "hg pull" slower'
257 )
257 )
258
258
259 upgrademessage = _(
259 upgrademessage = _(
260 b'repository storage will be able to create '
260 b'repository storage will be able to create '
261 b'optimal deltas; new repository data will be '
261 b'optimal deltas; new repository data will be '
262 b'smaller and read times should decrease; '
262 b'smaller and read times should decrease; '
263 b'interacting with other repositories using this '
263 b'interacting with other repositories using this '
264 b'storage model should require less network and '
264 b'storage model should require less network and '
265 b'CPU resources, making "hg push" and "hg pull" '
265 b'CPU resources, making "hg push" and "hg pull" '
266 b'faster'
266 b'faster'
267 )
267 )
268
268
269
269
270 @registerformatvariant
270 @registerformatvariant
271 class sharesafe(requirementformatvariant):
271 class sharesafe(requirementformatvariant):
272 name = b'share-safe'
272 name = b'share-safe'
273 _requirement = requirements.SHARESAFE_REQUIREMENT
273 _requirement = requirements.SHARESAFE_REQUIREMENT
274
274
275 default = False
275 default = False
276
276
277 description = _(
277 description = _(
278 b'old shared repositories do not share source repository '
278 b'old shared repositories do not share source repository '
279 b'requirements and config. This leads to various problems '
279 b'requirements and config. This leads to various problems '
280 b'when the source repository format is upgraded or some new '
280 b'when the source repository format is upgraded or some new '
281 b'extensions are enabled.'
281 b'extensions are enabled.'
282 )
282 )
283
283
284 upgrademessage = _(
284 upgrademessage = _(
285 b'Upgrades a repository to share-safe format so that future '
285 b'Upgrades a repository to share-safe format so that future '
286 b'shares of this repository share its requirements and configs.'
286 b'shares of this repository share its requirements and configs.'
287 )
287 )
288
288
289 postdowngrademessage = _(
289 postdowngrademessage = _(
290 b'repository downgraded to not use share safe mode, '
290 b'repository downgraded to not use share safe mode, '
291 b'existing shares will not work and needs to'
291 b'existing shares will not work and needs to'
292 b' be reshared.'
292 b' be reshared.'
293 )
293 )
294
294
295 postupgrademessage = _(
295 postupgrademessage = _(
296 b'repository upgraded to share safe mode, existing'
296 b'repository upgraded to share safe mode, existing'
297 b' shares will still work in old non-safe mode. '
297 b' shares will still work in old non-safe mode. '
298 b'Re-share existing shares to use them in safe mode'
298 b'Re-share existing shares to use them in safe mode'
299 b' New shares will be created in safe mode.'
299 b' New shares will be created in safe mode.'
300 )
300 )
301
301
302 # upgrade only needs to change the requirements
302 # upgrade only needs to change the requirements
303 touches_filelogs = False
303 touches_filelogs = False
304 touches_manifests = False
304 touches_manifests = False
305 touches_changelog = False
305 touches_changelog = False
306 touches_requirements = True
306 touches_requirements = True
307
307
308
308
309 @registerformatvariant
309 @registerformatvariant
310 class sparserevlog(requirementformatvariant):
310 class sparserevlog(requirementformatvariant):
311 name = b'sparserevlog'
311 name = b'sparserevlog'
312
312
313 _requirement = requirements.SPARSEREVLOG_REQUIREMENT
313 _requirement = requirements.SPARSEREVLOG_REQUIREMENT
314
314
315 default = True
315 default = True
316
316
317 description = _(
317 description = _(
318 b'in order to limit disk reading and memory usage on older '
318 b'in order to limit disk reading and memory usage on older '
319 b'version, the span of a delta chain from its root to its '
319 b'version, the span of a delta chain from its root to its '
320 b'end is limited, whatever the relevant data in this span. '
320 b'end is limited, whatever the relevant data in this span. '
321 b'This can severly limit Mercurial ability to build good '
321 b'This can severly limit Mercurial ability to build good '
322 b'chain of delta resulting is much more storage space being '
322 b'chain of delta resulting is much more storage space being '
323 b'taken and limit reusability of on disk delta during '
323 b'taken and limit reusability of on disk delta during '
324 b'exchange.'
324 b'exchange.'
325 )
325 )
326
326
327 upgrademessage = _(
327 upgrademessage = _(
328 b'Revlog supports delta chain with more unused data '
328 b'Revlog supports delta chain with more unused data '
329 b'between payload. These gaps will be skipped at read '
329 b'between payload. These gaps will be skipped at read '
330 b'time. This allows for better delta chains, making a '
330 b'time. This allows for better delta chains, making a '
331 b'better compression and faster exchange with server.'
331 b'better compression and faster exchange with server.'
332 )
332 )
333
333
334
334
335 @registerformatvariant
335 @registerformatvariant
336 class persistentnodemap(requirementformatvariant):
336 class persistentnodemap(requirementformatvariant):
337 name = b'persistent-nodemap'
337 name = b'persistent-nodemap'
338
338
339 _requirement = requirements.NODEMAP_REQUIREMENT
339 _requirement = requirements.NODEMAP_REQUIREMENT
340
340
341 default = False
341 default = False
342
342
343 description = _(
343 description = _(
344 b'persist the node -> rev mapping on disk to speedup lookup'
344 b'persist the node -> rev mapping on disk to speedup lookup'
345 )
345 )
346
346
347 upgrademessage = _(b'Speedup revision lookup by node id.')
347 upgrademessage = _(b'Speedup revision lookup by node id.')
348
348
349
349
350 @registerformatvariant
350 @registerformatvariant
351 class copiessdc(requirementformatvariant):
351 class copiessdc(requirementformatvariant):
352 name = b'copies-sdc'
352 name = b'copies-sdc'
353
353
354 _requirement = requirements.COPIESSDC_REQUIREMENT
354 _requirement = requirements.COPIESSDC_REQUIREMENT
355
355
356 default = False
356 default = False
357
357
358 description = _(b'Stores copies information alongside changesets.')
358 description = _(b'Stores copies information alongside changesets.')
359
359
360 upgrademessage = _(
360 upgrademessage = _(
361 b'Allows to use more efficient algorithm to deal with ' b'copy tracing.'
361 b'Allows to use more efficient algorithm to deal with ' b'copy tracing.'
362 )
362 )
363
363
364
364
365 @registerformatvariant
365 @registerformatvariant
366 class revlogv2(requirementformatvariant):
366 class revlogv2(requirementformatvariant):
367 name = b'revlog-v2'
367 name = b'revlog-v2'
368 _requirement = requirements.REVLOGV2_REQUIREMENT
368 _requirement = requirements.REVLOGV2_REQUIREMENT
369 default = False
369 default = False
370 description = _(b'Version 2 of the revlog.')
370 description = _(b'Version 2 of the revlog.')
371 upgrademessage = _(b'very experimental')
371 upgrademessage = _(b'very experimental')
372
372
373
373
374 @registerformatvariant
374 @registerformatvariant
375 class removecldeltachain(formatvariant):
375 class removecldeltachain(formatvariant):
376 name = b'plain-cl-delta'
376 name = b'plain-cl-delta'
377
377
378 default = True
378 default = True
379
379
380 description = _(
380 description = _(
381 b'changelog storage is using deltas instead of '
381 b'changelog storage is using deltas instead of '
382 b'raw entries; changelog reading and any '
382 b'raw entries; changelog reading and any '
383 b'operation relying on changelog data are slower '
383 b'operation relying on changelog data are slower '
384 b'than they could be'
384 b'than they could be'
385 )
385 )
386
386
387 upgrademessage = _(
387 upgrademessage = _(
388 b'changelog storage will be reformated to '
388 b'changelog storage will be reformated to '
389 b'store raw entries; changelog reading will be '
389 b'store raw entries; changelog reading will be '
390 b'faster; changelog size may be reduced'
390 b'faster; changelog size may be reduced'
391 )
391 )
392
392
393 @staticmethod
393 @staticmethod
394 def fromrepo(repo):
394 def fromrepo(repo):
395 # Mercurial 4.0 changed changelogs to not use delta chains. Search for
395 # Mercurial 4.0 changed changelogs to not use delta chains. Search for
396 # changelogs with deltas.
396 # changelogs with deltas.
397 cl = repo.changelog
397 cl = repo.changelog
398 chainbase = cl.chainbase
398 chainbase = cl.chainbase
399 return all(rev == chainbase(rev) for rev in cl)
399 return all(rev == chainbase(rev) for rev in cl)
400
400
401 @staticmethod
401 @staticmethod
402 def fromconfig(repo):
402 def fromconfig(repo):
403 return True
403 return True
404
404
405
405
406 _has_zstd = (
406 _has_zstd = (
407 b'zstd' in util.compengines
407 b'zstd' in util.compengines
408 and util.compengines[b'zstd'].available()
408 and util.compengines[b'zstd'].available()
409 and util.compengines[b'zstd'].revlogheader()
409 and util.compengines[b'zstd'].revlogheader()
410 )
410 )
411
411
412
412
413 @registerformatvariant
413 @registerformatvariant
414 class compressionengine(formatvariant):
414 class compressionengine(formatvariant):
415 name = b'compression'
415 name = b'compression'
416
416
417 if _has_zstd:
417 if _has_zstd:
418 default = b'zstd'
418 default = b'zstd'
419 else:
419 else:
420 default = b'zlib'
420 default = b'zlib'
421
421
422 description = _(
422 description = _(
423 b'Compresion algorithm used to compress data. '
423 b'Compresion algorithm used to compress data. '
424 b'Some engine are faster than other'
424 b'Some engine are faster than other'
425 )
425 )
426
426
427 upgrademessage = _(
427 upgrademessage = _(
428 b'revlog content will be recompressed with the new algorithm.'
428 b'revlog content will be recompressed with the new algorithm.'
429 )
429 )
430
430
431 @classmethod
431 @classmethod
432 def fromrepo(cls, repo):
432 def fromrepo(cls, repo):
433 # we allow multiple compression engine requirement to co-exist because
433 # we allow multiple compression engine requirement to co-exist because
434 # strickly speaking, revlog seems to support mixed compression style.
434 # strickly speaking, revlog seems to support mixed compression style.
435 #
435 #
436 # The compression used for new entries will be "the last one"
436 # The compression used for new entries will be "the last one"
437 compression = b'zlib'
437 compression = b'zlib'
438 for req in repo.requirements:
438 for req in repo.requirements:
439 prefix = req.startswith
439 prefix = req.startswith
440 if prefix(b'revlog-compression-') or prefix(b'exp-compression-'):
440 if prefix(b'revlog-compression-') or prefix(b'exp-compression-'):
441 compression = req.split(b'-', 2)[2]
441 compression = req.split(b'-', 2)[2]
442 return compression
442 return compression
443
443
444 @classmethod
444 @classmethod
445 def fromconfig(cls, repo):
445 def fromconfig(cls, repo):
446 compengines = repo.ui.configlist(b'format', b'revlog-compression')
446 compengines = repo.ui.configlist(b'format', b'revlog-compression')
447 # return the first valid value as the selection code would do
447 # return the first valid value as the selection code would do
448 for comp in compengines:
448 for comp in compengines:
449 if comp in util.compengines:
449 if comp in util.compengines:
450 e = util.compengines[comp]
450 e = util.compengines[comp]
451 if e.available() and e.revlogheader():
451 if e.available() and e.revlogheader():
452 return comp
452 return comp
453
453
454 # no valide compression found lets display it all for clarity
454 # no valide compression found lets display it all for clarity
455 return b','.join(compengines)
455 return b','.join(compengines)
456
456
457
457
458 @registerformatvariant
458 @registerformatvariant
459 class compressionlevel(formatvariant):
459 class compressionlevel(formatvariant):
460 name = b'compression-level'
460 name = b'compression-level'
461 default = b'default'
461 default = b'default'
462
462
463 description = _(b'compression level')
463 description = _(b'compression level')
464
464
465 upgrademessage = _(b'revlog content will be recompressed')
465 upgrademessage = _(b'revlog content will be recompressed')
466
466
467 @classmethod
467 @classmethod
468 def fromrepo(cls, repo):
468 def fromrepo(cls, repo):
469 comp = compressionengine.fromrepo(repo)
469 comp = compressionengine.fromrepo(repo)
470 level = None
470 level = None
471 if comp == b'zlib':
471 if comp == b'zlib':
472 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
472 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
473 elif comp == b'zstd':
473 elif comp == b'zstd':
474 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
474 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
475 if level is None:
475 if level is None:
476 return b'default'
476 return b'default'
477 return bytes(level)
477 return bytes(level)
478
478
479 @classmethod
479 @classmethod
480 def fromconfig(cls, repo):
480 def fromconfig(cls, repo):
481 comp = compressionengine.fromconfig(repo)
481 comp = compressionengine.fromconfig(repo)
482 level = None
482 level = None
483 if comp == b'zlib':
483 if comp == b'zlib':
484 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
484 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
485 elif comp == b'zstd':
485 elif comp == b'zstd':
486 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
486 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
487 if level is None:
487 if level is None:
488 return b'default'
488 return b'default'
489 return bytes(level)
489 return bytes(level)
490
490
491
491
492 def find_format_upgrades(repo):
492 def find_format_upgrades(repo):
493 """returns a list of format upgrades which can be perform on the repo"""
493 """returns a list of format upgrades which can be perform on the repo"""
494 upgrades = []
494 upgrades = []
495
495
496 # We could detect lack of revlogv1 and store here, but they were added
496 # We could detect lack of revlogv1 and store here, but they were added
497 # in 0.9.2 and we don't support upgrading repos without these
497 # in 0.9.2 and we don't support upgrading repos without these
498 # requirements, so let's not bother.
498 # requirements, so let's not bother.
499
499
500 for fv in allformatvariant:
500 for fv in allformatvariant:
501 if not fv.fromrepo(repo):
501 if not fv.fromrepo(repo):
502 upgrades.append(fv)
502 upgrades.append(fv)
503
503
504 return upgrades
504 return upgrades
505
505
506
506
507 def find_format_downgrades(repo):
507 def find_format_downgrades(repo):
508 """returns a list of format downgrades which will be performed on the repo
508 """returns a list of format downgrades which will be performed on the repo
509 because of disabled config option for them"""
509 because of disabled config option for them"""
510
510
511 downgrades = []
511 downgrades = []
512
512
513 for fv in allformatvariant:
513 for fv in allformatvariant:
514 if fv.name == b'compression':
514 if fv.name == b'compression':
515 # If there is a compression change between repository
515 # If there is a compression change between repository
516 # and config, destination repository compression will change
516 # and config, destination repository compression will change
517 # and current compression will be removed.
517 # and current compression will be removed.
518 if fv.fromrepo(repo) != fv.fromconfig(repo):
518 if fv.fromrepo(repo) != fv.fromconfig(repo):
519 downgrades.append(fv)
519 downgrades.append(fv)
520 continue
520 continue
521 # format variant exist in repo but does not exist in new repository
521 # format variant exist in repo but does not exist in new repository
522 # config
522 # config
523 if fv.fromrepo(repo) and not fv.fromconfig(repo):
523 if fv.fromrepo(repo) and not fv.fromconfig(repo):
524 downgrades.append(fv)
524 downgrades.append(fv)
525
525
526 return downgrades
526 return downgrades
527
527
528
528
529 ALL_OPTIMISATIONS = []
529 ALL_OPTIMISATIONS = []
530
530
531
531
532 def register_optimization(obj):
532 def register_optimization(obj):
533 ALL_OPTIMISATIONS.append(obj)
533 ALL_OPTIMISATIONS.append(obj)
534 return obj
534 return obj
535
535
536
536
537 register_optimization(
537 register_optimization(
538 improvement(
538 improvement(
539 name=b're-delta-parent',
539 name=b're-delta-parent',
540 type=OPTIMISATION,
540 type=OPTIMISATION,
541 description=_(
541 description=_(
542 b'deltas within internal storage will be recalculated to '
542 b'deltas within internal storage will be recalculated to '
543 b'choose an optimal base revision where this was not '
543 b'choose an optimal base revision where this was not '
544 b'already done; the size of the repository may shrink and '
544 b'already done; the size of the repository may shrink and '
545 b'various operations may become faster; the first time '
545 b'various operations may become faster; the first time '
546 b'this optimization is performed could slow down upgrade '
546 b'this optimization is performed could slow down upgrade '
547 b'execution considerably; subsequent invocations should '
547 b'execution considerably; subsequent invocations should '
548 b'not run noticeably slower'
548 b'not run noticeably slower'
549 ),
549 ),
550 upgrademessage=_(
550 upgrademessage=_(
551 b'deltas within internal storage will choose a new '
551 b'deltas within internal storage will choose a new '
552 b'base revision if needed'
552 b'base revision if needed'
553 ),
553 ),
554 )
554 )
555 )
555 )
556
556
557 register_optimization(
557 register_optimization(
558 improvement(
558 improvement(
559 name=b're-delta-multibase',
559 name=b're-delta-multibase',
560 type=OPTIMISATION,
560 type=OPTIMISATION,
561 description=_(
561 description=_(
562 b'deltas within internal storage will be recalculated '
562 b'deltas within internal storage will be recalculated '
563 b'against multiple base revision and the smallest '
563 b'against multiple base revision and the smallest '
564 b'difference will be used; the size of the repository may '
564 b'difference will be used; the size of the repository may '
565 b'shrink significantly when there are many merges; this '
565 b'shrink significantly when there are many merges; this '
566 b'optimization will slow down execution in proportion to '
566 b'optimization will slow down execution in proportion to '
567 b'the number of merges in the repository and the amount '
567 b'the number of merges in the repository and the amount '
568 b'of files in the repository; this slow down should not '
568 b'of files in the repository; this slow down should not '
569 b'be significant unless there are tens of thousands of '
569 b'be significant unless there are tens of thousands of '
570 b'files and thousands of merges'
570 b'files and thousands of merges'
571 ),
571 ),
572 upgrademessage=_(
572 upgrademessage=_(
573 b'deltas within internal storage will choose an '
573 b'deltas within internal storage will choose an '
574 b'optimal delta by computing deltas against multiple '
574 b'optimal delta by computing deltas against multiple '
575 b'parents; may slow down execution time '
575 b'parents; may slow down execution time '
576 b'significantly'
576 b'significantly'
577 ),
577 ),
578 )
578 )
579 )
579 )
580
580
581 register_optimization(
581 register_optimization(
582 improvement(
582 improvement(
583 name=b're-delta-all',
583 name=b're-delta-all',
584 type=OPTIMISATION,
584 type=OPTIMISATION,
585 description=_(
585 description=_(
586 b'deltas within internal storage will always be '
586 b'deltas within internal storage will always be '
587 b'recalculated without reusing prior deltas; this will '
587 b'recalculated without reusing prior deltas; this will '
588 b'likely make execution run several times slower; this '
588 b'likely make execution run several times slower; this '
589 b'optimization is typically not needed'
589 b'optimization is typically not needed'
590 ),
590 ),
591 upgrademessage=_(
591 upgrademessage=_(
592 b'deltas within internal storage will be fully '
592 b'deltas within internal storage will be fully '
593 b'recomputed; this will likely drastically slow down '
593 b'recomputed; this will likely drastically slow down '
594 b'execution time'
594 b'execution time'
595 ),
595 ),
596 )
596 )
597 )
597 )
598
598
599 register_optimization(
599 register_optimization(
600 improvement(
600 improvement(
601 name=b're-delta-fulladd',
601 name=b're-delta-fulladd',
602 type=OPTIMISATION,
602 type=OPTIMISATION,
603 description=_(
603 description=_(
604 b'every revision will be re-added as if it was new '
604 b'every revision will be re-added as if it was new '
605 b'content. It will go through the full storage '
605 b'content. It will go through the full storage '
606 b'mechanism giving extensions a chance to process it '
606 b'mechanism giving extensions a chance to process it '
607 b'(eg. lfs). This is similar to "re-delta-all" but even '
607 b'(eg. lfs). This is similar to "re-delta-all" but even '
608 b'slower since more logic is involved.'
608 b'slower since more logic is involved.'
609 ),
609 ),
610 upgrademessage=_(
610 upgrademessage=_(
611 b'each revision will be added as new content to the '
611 b'each revision will be added as new content to the '
612 b'internal storage; this will likely drastically slow '
612 b'internal storage; this will likely drastically slow '
613 b'down execution time, but some extensions might need '
613 b'down execution time, but some extensions might need '
614 b'it'
614 b'it'
615 ),
615 ),
616 )
616 )
617 )
617 )
618
618
619
619
620 def findoptimizations(repo):
620 def findoptimizations(repo):
621 """Determine optimisation that could be used during upgrade"""
621 """Determine optimisation that could be used during upgrade"""
622 # These are unconditionally added. There is logic later that figures out
622 # These are unconditionally added. There is logic later that figures out
623 # which ones to apply.
623 # which ones to apply.
624 return list(ALL_OPTIMISATIONS)
624 return list(ALL_OPTIMISATIONS)
625
625
626
626
627 def determine_upgrade_actions(
627 def determine_upgrade_actions(
628 repo, format_upgrades, optimizations, sourcereqs, destreqs
628 repo, format_upgrades, optimizations, sourcereqs, destreqs
629 ):
629 ):
630 """Determine upgrade actions that will be performed.
630 """Determine upgrade actions that will be performed.
631
631
632 Given a list of improvements as returned by ``find_format_upgrades`` and
632 Given a list of improvements as returned by ``find_format_upgrades`` and
633 ``findoptimizations``, determine the list of upgrade actions that
633 ``findoptimizations``, determine the list of upgrade actions that
634 will be performed.
634 will be performed.
635
635
636 The role of this function is to filter improvements if needed, apply
636 The role of this function is to filter improvements if needed, apply
637 recommended optimizations from the improvements list that make sense,
637 recommended optimizations from the improvements list that make sense,
638 etc.
638 etc.
639
639
640 Returns a list of action names.
640 Returns a list of action names.
641 """
641 """
642 newactions = []
642 newactions = []
643
643
644 for d in format_upgrades:
644 for d in format_upgrades:
645 name = d._requirement
645 name = d._requirement
646
646
647 # If the action is a requirement that doesn't show up in the
647 # If the action is a requirement that doesn't show up in the
648 # destination requirements, prune the action.
648 # destination requirements, prune the action.
649 if name is not None and name not in destreqs:
649 if name is not None and name not in destreqs:
650 continue
650 continue
651
651
652 newactions.append(d)
652 newactions.append(d)
653
653
654 newactions.extend(o for o in sorted(optimizations) if o not in newactions)
654 newactions.extend(o for o in sorted(optimizations) if o not in newactions)
655
655
656 # FUTURE consider adding some optimizations here for certain transitions.
656 # FUTURE consider adding some optimizations here for certain transitions.
657 # e.g. adding generaldelta could schedule parent redeltas.
657 # e.g. adding generaldelta could schedule parent redeltas.
658
658
659 return newactions
659 return newactions
660
660
661
661
662 class UpgradeOperation(object):
662 class UpgradeOperation(object):
663 """represent the work to be done during an upgrade"""
663 """represent the work to be done during an upgrade"""
664
664
665 def __init__(
665 def __init__(
666 self,
666 self,
667 ui,
667 ui,
668 new_requirements,
668 new_requirements,
669 current_requirements,
669 current_requirements,
670 upgrade_actions,
670 upgrade_actions,
671 removed_actions,
671 removed_actions,
672 revlogs_to_process,
672 revlogs_to_process,
673 backup_store,
673 backup_store,
674 ):
674 ):
675 self.ui = ui
675 self.ui = ui
676 self.new_requirements = new_requirements
676 self.new_requirements = new_requirements
677 self.current_requirements = current_requirements
677 self.current_requirements = current_requirements
678 # list of upgrade actions the operation will perform
678 # list of upgrade actions the operation will perform
679 self.upgrade_actions = upgrade_actions
679 self.upgrade_actions = upgrade_actions
680 self._upgrade_actions_names = set([a.name for a in upgrade_actions])
680 self._upgrade_actions_names = set([a.name for a in upgrade_actions])
681 self.removed_actions = removed_actions
681 self.removed_actions = removed_actions
682 self.revlogs_to_process = revlogs_to_process
682 self.revlogs_to_process = revlogs_to_process
683 # requirements which will be added by the operation
683 # requirements which will be added by the operation
684 self._added_requirements = (
684 self._added_requirements = (
685 self.new_requirements - self.current_requirements
685 self.new_requirements - self.current_requirements
686 )
686 )
687 # requirements which will be removed by the operation
687 # requirements which will be removed by the operation
688 self._removed_requirements = (
688 self._removed_requirements = (
689 self.current_requirements - self.new_requirements
689 self.current_requirements - self.new_requirements
690 )
690 )
691 # requirements which will be preserved by the operation
691 # requirements which will be preserved by the operation
692 self._preserved_requirements = (
692 self._preserved_requirements = (
693 self.current_requirements & self.new_requirements
693 self.current_requirements & self.new_requirements
694 )
694 )
695 # optimizations which are not used and it's recommended that they
695 # optimizations which are not used and it's recommended that they
696 # should use them
696 # should use them
697 all_optimizations = findoptimizations(None)
697 all_optimizations = findoptimizations(None)
698 self.unused_optimizations = [
698 self.unused_optimizations = [
699 i for i in all_optimizations if i not in self.upgrade_actions
699 i for i in all_optimizations if i not in self.upgrade_actions
700 ]
700 ]
701
701
702 # delta reuse mode of this upgrade operation
702 # delta reuse mode of this upgrade operation
703 self.delta_reuse_mode = revlog.revlog.DELTAREUSEALWAYS
703 self.delta_reuse_mode = revlog.revlog.DELTAREUSEALWAYS
704 if b're-delta-all' in self._upgrade_actions_names:
704 if b're-delta-all' in self._upgrade_actions_names:
705 self.delta_reuse_mode = revlog.revlog.DELTAREUSENEVER
705 self.delta_reuse_mode = revlog.revlog.DELTAREUSENEVER
706 elif b're-delta-parent' in self._upgrade_actions_names:
706 elif b're-delta-parent' in self._upgrade_actions_names:
707 self.delta_reuse_mode = revlog.revlog.DELTAREUSESAMEREVS
707 self.delta_reuse_mode = revlog.revlog.DELTAREUSESAMEREVS
708 elif b're-delta-multibase' in self._upgrade_actions_names:
708 elif b're-delta-multibase' in self._upgrade_actions_names:
709 self.delta_reuse_mode = revlog.revlog.DELTAREUSESAMEREVS
709 self.delta_reuse_mode = revlog.revlog.DELTAREUSESAMEREVS
710 elif b're-delta-fulladd' in self._upgrade_actions_names:
710 elif b're-delta-fulladd' in self._upgrade_actions_names:
711 self.delta_reuse_mode = revlog.revlog.DELTAREUSEFULLADD
711 self.delta_reuse_mode = revlog.revlog.DELTAREUSEFULLADD
712
712
713 # should this operation force re-delta of both parents
713 # should this operation force re-delta of both parents
714 self.force_re_delta_both_parents = (
714 self.force_re_delta_both_parents = (
715 b're-delta-multibase' in self._upgrade_actions_names
715 b're-delta-multibase' in self._upgrade_actions_names
716 )
716 )
717
717
718 # should this operation create a backup of the store
718 # should this operation create a backup of the store
719 self.backup_store = backup_store
719 self.backup_store = backup_store
720
720
721 # whether the operation touches different revlogs at all or not
721 # whether the operation touches different revlogs at all or not
722 self.touches_filelogs = self._touches_filelogs()
722 self.touches_filelogs = self._touches_filelogs()
723 self.touches_manifests = self._touches_manifests()
723 self.touches_manifests = self._touches_manifests()
724 self.touches_changelog = self._touches_changelog()
724 self.touches_changelog = self._touches_changelog()
725 # whether the operation touches requirements file or not
725 # whether the operation touches requirements file or not
726 self.touches_requirements = self._touches_requirements()
726 self.touches_requirements = self._touches_requirements()
727 self.touches_store = (
727 self.touches_store = (
728 self.touches_filelogs
728 self.touches_filelogs
729 or self.touches_manifests
729 or self.touches_manifests
730 or self.touches_changelog
730 or self.touches_changelog
731 )
731 )
732 # does the operation only touches repository requirement
732 # does the operation only touches repository requirement
733 self.requirements_only = (
733 self.requirements_only = (
734 self.touches_requirements and not self.touches_store
734 self.touches_requirements and not self.touches_store
735 )
735 )
736
736
737 def _touches_filelogs(self):
737 def _touches_filelogs(self):
738 for a in self.upgrade_actions:
738 for a in self.upgrade_actions:
739 # in optimisations, we re-process the revlogs again
739 # in optimisations, we re-process the revlogs again
740 if a.type == OPTIMISATION:
740 if a.type == OPTIMISATION:
741 return True
741 return True
742 elif a.touches_filelogs:
742 elif a.touches_filelogs:
743 return True
743 return True
744 for a in self.removed_actions:
744 for a in self.removed_actions:
745 if a.touches_filelogs:
745 if a.touches_filelogs:
746 return True
746 return True
747 return False
747 return False
748
748
749 def _touches_manifests(self):
749 def _touches_manifests(self):
750 for a in self.upgrade_actions:
750 for a in self.upgrade_actions:
751 # in optimisations, we re-process the revlogs again
751 # in optimisations, we re-process the revlogs again
752 if a.type == OPTIMISATION:
752 if a.type == OPTIMISATION:
753 return True
753 return True
754 elif a.touches_manifests:
754 elif a.touches_manifests:
755 return True
755 return True
756 for a in self.removed_actions:
756 for a in self.removed_actions:
757 if a.touches_manifests:
757 if a.touches_manifests:
758 return True
758 return True
759 return False
759 return False
760
760
761 def _touches_changelog(self):
761 def _touches_changelog(self):
762 for a in self.upgrade_actions:
762 for a in self.upgrade_actions:
763 # in optimisations, we re-process the revlogs again
763 # in optimisations, we re-process the revlogs again
764 if a.type == OPTIMISATION:
764 if a.type == OPTIMISATION:
765 return True
765 return True
766 elif a.touches_changelog:
766 elif a.touches_changelog:
767 return True
767 return True
768 for a in self.removed_actions:
768 for a in self.removed_actions:
769 if a.touches_changelog:
769 if a.touches_changelog:
770 return True
770 return True
771 return False
771 return False
772
772
773 def _touches_requirements(self):
773 def _touches_requirements(self):
774 for a in self.upgrade_actions:
774 for a in self.upgrade_actions:
775 # optimisations are used to re-process revlogs and does not result
775 # optimisations are used to re-process revlogs and does not result
776 # in a requirement being added or removed
776 # in a requirement being added or removed
777 if a.type == OPTIMISATION:
777 if a.type == OPTIMISATION:
778 pass
778 pass
779 elif a.touches_requirements:
779 elif a.touches_requirements:
780 return True
780 return True
781 for a in self.removed_actions:
781 for a in self.removed_actions:
782 if a.touches_requirements:
782 if a.touches_requirements:
783 return True
783 return True
784
784
785 return False
785 return False
786
786
787 def _write_labeled(self, l, label):
787 def _write_labeled(self, l, label):
788 """
788 """
789 Utility function to aid writing of a list under one label
789 Utility function to aid writing of a list under one label
790 """
790 """
791 first = True
791 first = True
792 for r in sorted(l):
792 for r in sorted(l):
793 if not first:
793 if not first:
794 self.ui.write(b', ')
794 self.ui.write(b', ')
795 self.ui.write(r, label=label)
795 self.ui.write(r, label=label)
796 first = False
796 first = False
797
797
798 def print_requirements(self):
798 def print_requirements(self):
799 self.ui.write(_(b'requirements\n'))
799 self.ui.write(_(b'requirements\n'))
800 self.ui.write(_(b' preserved: '))
800 self.ui.write(_(b' preserved: '))
801 self._write_labeled(
801 self._write_labeled(
802 self._preserved_requirements, "upgrade-repo.requirement.preserved"
802 self._preserved_requirements, "upgrade-repo.requirement.preserved"
803 )
803 )
804 self.ui.write((b'\n'))
804 self.ui.write((b'\n'))
805 if self._removed_requirements:
805 if self._removed_requirements:
806 self.ui.write(_(b' removed: '))
806 self.ui.write(_(b' removed: '))
807 self._write_labeled(
807 self._write_labeled(
808 self._removed_requirements, "upgrade-repo.requirement.removed"
808 self._removed_requirements, "upgrade-repo.requirement.removed"
809 )
809 )
810 self.ui.write((b'\n'))
810 self.ui.write((b'\n'))
811 if self._added_requirements:
811 if self._added_requirements:
812 self.ui.write(_(b' added: '))
812 self.ui.write(_(b' added: '))
813 self._write_labeled(
813 self._write_labeled(
814 self._added_requirements, "upgrade-repo.requirement.added"
814 self._added_requirements, "upgrade-repo.requirement.added"
815 )
815 )
816 self.ui.write((b'\n'))
816 self.ui.write((b'\n'))
817 self.ui.write(b'\n')
817 self.ui.write(b'\n')
818
818
819 def print_optimisations(self):
819 def print_optimisations(self):
820 optimisations = [
820 optimisations = [
821 a for a in self.upgrade_actions if a.type == OPTIMISATION
821 a for a in self.upgrade_actions if a.type == OPTIMISATION
822 ]
822 ]
823 optimisations.sort(key=lambda a: a.name)
823 optimisations.sort(key=lambda a: a.name)
824 if optimisations:
824 if optimisations:
825 self.ui.write(_(b'optimisations: '))
825 self.ui.write(_(b'optimisations: '))
826 self._write_labeled(
826 self._write_labeled(
827 [a.name for a in optimisations],
827 [a.name for a in optimisations],
828 "upgrade-repo.optimisation.performed",
828 "upgrade-repo.optimisation.performed",
829 )
829 )
830 self.ui.write(b'\n\n')
830 self.ui.write(b'\n\n')
831
831
832 def print_upgrade_actions(self):
832 def print_upgrade_actions(self):
833 for a in self.upgrade_actions:
833 for a in self.upgrade_actions:
834 self.ui.status(b'%s\n %s\n\n' % (a.name, a.upgrademessage))
834 self.ui.status(b'%s\n %s\n\n' % (a.name, a.upgrademessage))
835
835
836 def print_affected_revlogs(self):
836 def print_affected_revlogs(self):
837 if not self.revlogs_to_process:
837 if not self.revlogs_to_process:
838 self.ui.write((b'no revlogs to process\n'))
838 self.ui.write((b'no revlogs to process\n'))
839 else:
839 else:
840 self.ui.write((b'processed revlogs:\n'))
840 self.ui.write((b'processed revlogs:\n'))
841 for r in sorted(self.revlogs_to_process):
841 for r in sorted(self.revlogs_to_process):
842 self.ui.write((b' - %s\n' % r))
842 self.ui.write((b' - %s\n' % r))
843 self.ui.write((b'\n'))
843 self.ui.write((b'\n'))
844
844
845 def print_unused_optimizations(self):
845 def print_unused_optimizations(self):
846 for i in self.unused_optimizations:
846 for i in self.unused_optimizations:
847 self.ui.status(_(b'%s\n %s\n\n') % (i.name, i.description))
847 self.ui.status(_(b'%s\n %s\n\n') % (i.name, i.description))
848
848
849 def has_upgrade_action(self, name):
849 def has_upgrade_action(self, name):
850 """Check whether the upgrade operation will perform this action"""
850 """Check whether the upgrade operation will perform this action"""
851 return name in self._upgrade_actions_names
851 return name in self._upgrade_actions_names
852
852
853 def print_post_op_messages(self):
853 def print_post_op_messages(self):
854 """print post upgrade operation warning messages"""
854 """print post upgrade operation warning messages"""
855 for a in self.upgrade_actions:
855 for a in self.upgrade_actions:
856 if a.postupgrademessage is not None:
856 if a.postupgrademessage is not None:
857 self.ui.warn(b'%s\n' % a.postupgrademessage)
857 self.ui.warn(b'%s\n' % a.postupgrademessage)
858 for a in self.removed_actions:
858 for a in self.removed_actions:
859 if a.postdowngrademessage is not None:
859 if a.postdowngrademessage is not None:
860 self.ui.warn(b'%s\n' % a.postdowngrademessage)
860 self.ui.warn(b'%s\n' % a.postdowngrademessage)
861
861
862
862
863 ### Code checking if a repository can got through the upgrade process at all. #
863 ### Code checking if a repository can got through the upgrade process at all. #
864
864
865
865
866 def requiredsourcerequirements(repo):
866 def requiredsourcerequirements(repo):
867 """Obtain requirements required to be present to upgrade a repo.
867 """Obtain requirements required to be present to upgrade a repo.
868
868
869 An upgrade will not be allowed if the repository doesn't have the
869 An upgrade will not be allowed if the repository doesn't have the
870 requirements returned by this function.
870 requirements returned by this function.
871 """
871 """
872 return {
872 return {
873 # Introduced in Mercurial 0.9.2.
873 # Introduced in Mercurial 0.9.2.
874 requirements.STORE_REQUIREMENT,
874 requirements.STORE_REQUIREMENT,
875 }
875 }
876
876
877
877
878 def blocksourcerequirements(repo):
878 def blocksourcerequirements(repo):
879 """Obtain requirements that will prevent an upgrade from occurring.
879 """Obtain requirements that will prevent an upgrade from occurring.
880
880
881 An upgrade cannot be performed if the source repository contains a
881 An upgrade cannot be performed if the source repository contains a
882 requirements in the returned set.
882 requirements in the returned set.
883 """
883 """
884 return {
884 return {
885 # The upgrade code does not yet support these experimental features.
885 # The upgrade code does not yet support these experimental features.
886 # This is an artificial limitation.
886 # This is an artificial limitation.
887 requirements.TREEMANIFEST_REQUIREMENT,
887 requirements.TREEMANIFEST_REQUIREMENT,
888 # This was a precursor to generaldelta and was never enabled by default.
888 # This was a precursor to generaldelta and was never enabled by default.
889 # It should (hopefully) not exist in the wild.
889 # It should (hopefully) not exist in the wild.
890 b'parentdelta',
890 b'parentdelta',
891 # Upgrade should operate on the actual store, not the shared link.
891 # Upgrade should operate on the actual store, not the shared link.
892 requirements.SHARED_REQUIREMENT,
892 requirements.SHARED_REQUIREMENT,
893 }
893 }
894
894
895
895
896 def check_revlog_version(reqs):
896 def check_revlog_version(reqs):
897 """Check that the requirements contain at least one Revlog version"""
897 """Check that the requirements contain at least one Revlog version"""
898 all_revlogs = {
898 all_revlogs = {
899 requirements.REVLOGV1_REQUIREMENT,
899 requirements.REVLOGV1_REQUIREMENT,
900 requirements.REVLOGV2_REQUIREMENT,
900 requirements.REVLOGV2_REQUIREMENT,
901 }
901 }
902 if not all_revlogs.intersection(reqs):
902 if not all_revlogs.intersection(reqs):
903 msg = _(b'cannot upgrade repository; missing a revlog version')
903 msg = _(b'cannot upgrade repository; missing a revlog version')
904 raise error.Abort(msg)
904 raise error.Abort(msg)
905
905
906
906
907 def check_source_requirements(repo):
907 def check_source_requirements(repo):
908 """Ensure that no existing requirements prevent the repository upgrade"""
908 """Ensure that no existing requirements prevent the repository upgrade"""
909
909
910 check_revlog_version(repo.requirements)
910 check_revlog_version(repo.requirements)
911 required = requiredsourcerequirements(repo)
911 required = requiredsourcerequirements(repo)
912 missingreqs = required - repo.requirements
912 missingreqs = required - repo.requirements
913 if missingreqs:
913 if missingreqs:
914 msg = _(b'cannot upgrade repository; requirement missing: %s')
914 msg = _(b'cannot upgrade repository; requirement missing: %s')
915 missingreqs = b', '.join(sorted(missingreqs))
915 missingreqs = b', '.join(sorted(missingreqs))
916 raise error.Abort(msg % missingreqs)
916 raise error.Abort(msg % missingreqs)
917
917
918 blocking = blocksourcerequirements(repo)
918 blocking = blocksourcerequirements(repo)
919 blockingreqs = blocking & repo.requirements
919 blockingreqs = blocking & repo.requirements
920 if blockingreqs:
920 if blockingreqs:
921 m = _(b'cannot upgrade repository; unsupported source requirement: %s')
921 m = _(b'cannot upgrade repository; unsupported source requirement: %s')
922 blockingreqs = b', '.join(sorted(blockingreqs))
922 blockingreqs = b', '.join(sorted(blockingreqs))
923 raise error.Abort(m % blockingreqs)
923 raise error.Abort(m % blockingreqs)
924
924
925
925
926 ### Verify the validity of the planned requirement changes ####################
926 ### Verify the validity of the planned requirement changes ####################
927
927
928
928
929 def supportremovedrequirements(repo):
929 def supportremovedrequirements(repo):
930 """Obtain requirements that can be removed during an upgrade.
930 """Obtain requirements that can be removed during an upgrade.
931
931
932 If an upgrade were to create a repository that dropped a requirement,
932 If an upgrade were to create a repository that dropped a requirement,
933 the dropped requirement must appear in the returned set for the upgrade
933 the dropped requirement must appear in the returned set for the upgrade
934 to be allowed.
934 to be allowed.
935 """
935 """
936 supported = {
936 supported = {
937 requirements.SPARSEREVLOG_REQUIREMENT,
937 requirements.SPARSEREVLOG_REQUIREMENT,
938 requirements.SIDEDATA_REQUIREMENT,
939 requirements.COPIESSDC_REQUIREMENT,
938 requirements.COPIESSDC_REQUIREMENT,
940 requirements.NODEMAP_REQUIREMENT,
939 requirements.NODEMAP_REQUIREMENT,
941 requirements.SHARESAFE_REQUIREMENT,
940 requirements.SHARESAFE_REQUIREMENT,
942 requirements.REVLOGV2_REQUIREMENT,
941 requirements.REVLOGV2_REQUIREMENT,
943 requirements.REVLOGV1_REQUIREMENT,
942 requirements.REVLOGV1_REQUIREMENT,
944 }
943 }
945 for name in compression.compengines:
944 for name in compression.compengines:
946 engine = compression.compengines[name]
945 engine = compression.compengines[name]
947 if engine.available() and engine.revlogheader():
946 if engine.available() and engine.revlogheader():
948 supported.add(b'exp-compression-%s' % name)
947 supported.add(b'exp-compression-%s' % name)
949 if engine.name() == b'zstd':
948 if engine.name() == b'zstd':
950 supported.add(b'revlog-compression-zstd')
949 supported.add(b'revlog-compression-zstd')
951 return supported
950 return supported
952
951
953
952
954 def supporteddestrequirements(repo):
953 def supporteddestrequirements(repo):
955 """Obtain requirements that upgrade supports in the destination.
954 """Obtain requirements that upgrade supports in the destination.
956
955
957 If the result of the upgrade would create requirements not in this set,
956 If the result of the upgrade would create requirements not in this set,
958 the upgrade is disallowed.
957 the upgrade is disallowed.
959
958
960 Extensions should monkeypatch this to add their custom requirements.
959 Extensions should monkeypatch this to add their custom requirements.
961 """
960 """
962 supported = {
961 supported = {
963 requirements.DOTENCODE_REQUIREMENT,
962 requirements.DOTENCODE_REQUIREMENT,
964 requirements.FNCACHE_REQUIREMENT,
963 requirements.FNCACHE_REQUIREMENT,
965 requirements.GENERALDELTA_REQUIREMENT,
964 requirements.GENERALDELTA_REQUIREMENT,
966 requirements.REVLOGV1_REQUIREMENT, # allowed in case of downgrade
965 requirements.REVLOGV1_REQUIREMENT, # allowed in case of downgrade
967 requirements.STORE_REQUIREMENT,
966 requirements.STORE_REQUIREMENT,
968 requirements.SPARSEREVLOG_REQUIREMENT,
967 requirements.SPARSEREVLOG_REQUIREMENT,
969 requirements.SIDEDATA_REQUIREMENT,
970 requirements.COPIESSDC_REQUIREMENT,
968 requirements.COPIESSDC_REQUIREMENT,
971 requirements.NODEMAP_REQUIREMENT,
969 requirements.NODEMAP_REQUIREMENT,
972 requirements.SHARESAFE_REQUIREMENT,
970 requirements.SHARESAFE_REQUIREMENT,
973 requirements.REVLOGV2_REQUIREMENT,
971 requirements.REVLOGV2_REQUIREMENT,
974 }
972 }
975 for name in compression.compengines:
973 for name in compression.compengines:
976 engine = compression.compengines[name]
974 engine = compression.compengines[name]
977 if engine.available() and engine.revlogheader():
975 if engine.available() and engine.revlogheader():
978 supported.add(b'exp-compression-%s' % name)
976 supported.add(b'exp-compression-%s' % name)
979 if engine.name() == b'zstd':
977 if engine.name() == b'zstd':
980 supported.add(b'revlog-compression-zstd')
978 supported.add(b'revlog-compression-zstd')
981 return supported
979 return supported
982
980
983
981
984 def allowednewrequirements(repo):
982 def allowednewrequirements(repo):
985 """Obtain requirements that can be added to a repository during upgrade.
983 """Obtain requirements that can be added to a repository during upgrade.
986
984
987 This is used to disallow proposed requirements from being added when
985 This is used to disallow proposed requirements from being added when
988 they weren't present before.
986 they weren't present before.
989
987
990 We use a list of allowed requirement additions instead of a list of known
988 We use a list of allowed requirement additions instead of a list of known
991 bad additions because the whitelist approach is safer and will prevent
989 bad additions because the whitelist approach is safer and will prevent
992 future, unknown requirements from accidentally being added.
990 future, unknown requirements from accidentally being added.
993 """
991 """
994 supported = {
992 supported = {
995 requirements.DOTENCODE_REQUIREMENT,
993 requirements.DOTENCODE_REQUIREMENT,
996 requirements.FNCACHE_REQUIREMENT,
994 requirements.FNCACHE_REQUIREMENT,
997 requirements.GENERALDELTA_REQUIREMENT,
995 requirements.GENERALDELTA_REQUIREMENT,
998 requirements.SPARSEREVLOG_REQUIREMENT,
996 requirements.SPARSEREVLOG_REQUIREMENT,
999 requirements.SIDEDATA_REQUIREMENT,
1000 requirements.COPIESSDC_REQUIREMENT,
997 requirements.COPIESSDC_REQUIREMENT,
1001 requirements.NODEMAP_REQUIREMENT,
998 requirements.NODEMAP_REQUIREMENT,
1002 requirements.SHARESAFE_REQUIREMENT,
999 requirements.SHARESAFE_REQUIREMENT,
1003 requirements.REVLOGV1_REQUIREMENT,
1000 requirements.REVLOGV1_REQUIREMENT,
1004 requirements.REVLOGV2_REQUIREMENT,
1001 requirements.REVLOGV2_REQUIREMENT,
1005 }
1002 }
1006 for name in compression.compengines:
1003 for name in compression.compengines:
1007 engine = compression.compengines[name]
1004 engine = compression.compengines[name]
1008 if engine.available() and engine.revlogheader():
1005 if engine.available() and engine.revlogheader():
1009 supported.add(b'exp-compression-%s' % name)
1006 supported.add(b'exp-compression-%s' % name)
1010 if engine.name() == b'zstd':
1007 if engine.name() == b'zstd':
1011 supported.add(b'revlog-compression-zstd')
1008 supported.add(b'revlog-compression-zstd')
1012 return supported
1009 return supported
1013
1010
1014
1011
1015 def check_requirements_changes(repo, new_reqs):
1012 def check_requirements_changes(repo, new_reqs):
1016 old_reqs = repo.requirements
1013 old_reqs = repo.requirements
1017 check_revlog_version(repo.requirements)
1014 check_revlog_version(repo.requirements)
1018 support_removal = supportremovedrequirements(repo)
1015 support_removal = supportremovedrequirements(repo)
1019 no_remove_reqs = old_reqs - new_reqs - support_removal
1016 no_remove_reqs = old_reqs - new_reqs - support_removal
1020 if no_remove_reqs:
1017 if no_remove_reqs:
1021 msg = _(b'cannot upgrade repository; requirement would be removed: %s')
1018 msg = _(b'cannot upgrade repository; requirement would be removed: %s')
1022 no_remove_reqs = b', '.join(sorted(no_remove_reqs))
1019 no_remove_reqs = b', '.join(sorted(no_remove_reqs))
1023 raise error.Abort(msg % no_remove_reqs)
1020 raise error.Abort(msg % no_remove_reqs)
1024
1021
1025 support_addition = allowednewrequirements(repo)
1022 support_addition = allowednewrequirements(repo)
1026 no_add_reqs = new_reqs - old_reqs - support_addition
1023 no_add_reqs = new_reqs - old_reqs - support_addition
1027 if no_add_reqs:
1024 if no_add_reqs:
1028 m = _(b'cannot upgrade repository; do not support adding requirement: ')
1025 m = _(b'cannot upgrade repository; do not support adding requirement: ')
1029 no_add_reqs = b', '.join(sorted(no_add_reqs))
1026 no_add_reqs = b', '.join(sorted(no_add_reqs))
1030 raise error.Abort(m + no_add_reqs)
1027 raise error.Abort(m + no_add_reqs)
1031
1028
1032 supported = supporteddestrequirements(repo)
1029 supported = supporteddestrequirements(repo)
1033 unsupported_reqs = new_reqs - supported
1030 unsupported_reqs = new_reqs - supported
1034 if unsupported_reqs:
1031 if unsupported_reqs:
1035 msg = _(
1032 msg = _(
1036 b'cannot upgrade repository; do not support destination '
1033 b'cannot upgrade repository; do not support destination '
1037 b'requirement: %s'
1034 b'requirement: %s'
1038 )
1035 )
1039 unsupported_reqs = b', '.join(sorted(unsupported_reqs))
1036 unsupported_reqs = b', '.join(sorted(unsupported_reqs))
1040 raise error.Abort(msg % unsupported_reqs)
1037 raise error.Abort(msg % unsupported_reqs)
@@ -1,158 +1,153 b''
1 use crate::errors::{HgError, HgResultExt};
1 use crate::errors::{HgError, HgResultExt};
2 use crate::repo::{Repo, Vfs};
2 use crate::repo::{Repo, Vfs};
3 use crate::utils::join_display;
3 use crate::utils::join_display;
4 use std::collections::HashSet;
4 use std::collections::HashSet;
5
5
6 fn parse(bytes: &[u8]) -> Result<HashSet<String>, HgError> {
6 fn parse(bytes: &[u8]) -> Result<HashSet<String>, HgError> {
7 // The Python code reading this file uses `str.splitlines`
7 // The Python code reading this file uses `str.splitlines`
8 // which looks for a number of line separators (even including a couple of
8 // which looks for a number of line separators (even including a couple of
9 // non-ASCII ones), but Python code writing it always uses `\n`.
9 // non-ASCII ones), but Python code writing it always uses `\n`.
10 let lines = bytes.split(|&byte| byte == b'\n');
10 let lines = bytes.split(|&byte| byte == b'\n');
11
11
12 lines
12 lines
13 .filter(|line| !line.is_empty())
13 .filter(|line| !line.is_empty())
14 .map(|line| {
14 .map(|line| {
15 // Python uses Unicode `str.isalnum` but feature names are all
15 // Python uses Unicode `str.isalnum` but feature names are all
16 // ASCII
16 // ASCII
17 if line[0].is_ascii_alphanumeric() && line.is_ascii() {
17 if line[0].is_ascii_alphanumeric() && line.is_ascii() {
18 Ok(String::from_utf8(line.into()).unwrap())
18 Ok(String::from_utf8(line.into()).unwrap())
19 } else {
19 } else {
20 Err(HgError::corrupted("parse error in 'requires' file"))
20 Err(HgError::corrupted("parse error in 'requires' file"))
21 }
21 }
22 })
22 })
23 .collect()
23 .collect()
24 }
24 }
25
25
26 pub(crate) fn load(hg_vfs: Vfs) -> Result<HashSet<String>, HgError> {
26 pub(crate) fn load(hg_vfs: Vfs) -> Result<HashSet<String>, HgError> {
27 parse(&hg_vfs.read("requires")?)
27 parse(&hg_vfs.read("requires")?)
28 }
28 }
29
29
30 pub(crate) fn load_if_exists(hg_vfs: Vfs) -> Result<HashSet<String>, HgError> {
30 pub(crate) fn load_if_exists(hg_vfs: Vfs) -> Result<HashSet<String>, HgError> {
31 if let Some(bytes) = hg_vfs.read("requires").io_not_found_as_none()? {
31 if let Some(bytes) = hg_vfs.read("requires").io_not_found_as_none()? {
32 parse(&bytes)
32 parse(&bytes)
33 } else {
33 } else {
34 // Treat a missing file the same as an empty file.
34 // Treat a missing file the same as an empty file.
35 // From `mercurial/localrepo.py`:
35 // From `mercurial/localrepo.py`:
36 // > requires file contains a newline-delimited list of
36 // > requires file contains a newline-delimited list of
37 // > features/capabilities the opener (us) must have in order to use
37 // > features/capabilities the opener (us) must have in order to use
38 // > the repository. This file was introduced in Mercurial 0.9.2,
38 // > the repository. This file was introduced in Mercurial 0.9.2,
39 // > which means very old repositories may not have one. We assume
39 // > which means very old repositories may not have one. We assume
40 // > a missing file translates to no requirements.
40 // > a missing file translates to no requirements.
41 Ok(HashSet::new())
41 Ok(HashSet::new())
42 }
42 }
43 }
43 }
44
44
45 pub(crate) fn check(repo: &Repo) -> Result<(), HgError> {
45 pub(crate) fn check(repo: &Repo) -> Result<(), HgError> {
46 let unknown: Vec<_> = repo
46 let unknown: Vec<_> = repo
47 .requirements()
47 .requirements()
48 .iter()
48 .iter()
49 .map(String::as_str)
49 .map(String::as_str)
50 // .filter(|feature| !ALL_SUPPORTED.contains(feature.as_str()))
50 // .filter(|feature| !ALL_SUPPORTED.contains(feature.as_str()))
51 .filter(|feature| {
51 .filter(|feature| {
52 !REQUIRED.contains(feature) && !SUPPORTED.contains(feature)
52 !REQUIRED.contains(feature) && !SUPPORTED.contains(feature)
53 })
53 })
54 .collect();
54 .collect();
55 if !unknown.is_empty() {
55 if !unknown.is_empty() {
56 return Err(HgError::unsupported(format!(
56 return Err(HgError::unsupported(format!(
57 "repository requires feature unknown to this Mercurial: {}",
57 "repository requires feature unknown to this Mercurial: {}",
58 join_display(&unknown, ", ")
58 join_display(&unknown, ", ")
59 )));
59 )));
60 }
60 }
61 let missing: Vec<_> = REQUIRED
61 let missing: Vec<_> = REQUIRED
62 .iter()
62 .iter()
63 .filter(|&&feature| !repo.requirements().contains(feature))
63 .filter(|&&feature| !repo.requirements().contains(feature))
64 .collect();
64 .collect();
65 if !missing.is_empty() {
65 if !missing.is_empty() {
66 return Err(HgError::unsupported(format!(
66 return Err(HgError::unsupported(format!(
67 "repository is missing feature required by this Mercurial: {}",
67 "repository is missing feature required by this Mercurial: {}",
68 join_display(&missing, ", ")
68 join_display(&missing, ", ")
69 )));
69 )));
70 }
70 }
71 Ok(())
71 Ok(())
72 }
72 }
73
73
74 /// rhg does not support repositories that are *missing* any of these features
74 /// rhg does not support repositories that are *missing* any of these features
75 const REQUIRED: &[&str] = &["revlogv1", "store", "fncache", "dotencode"];
75 const REQUIRED: &[&str] = &["revlogv1", "store", "fncache", "dotencode"];
76
76
77 /// rhg supports repository with or without these
77 /// rhg supports repository with or without these
78 const SUPPORTED: &[&str] = &[
78 const SUPPORTED: &[&str] = &[
79 "generaldelta",
79 "generaldelta",
80 SHARED_REQUIREMENT,
80 SHARED_REQUIREMENT,
81 SHARESAFE_REQUIREMENT,
81 SHARESAFE_REQUIREMENT,
82 SPARSEREVLOG_REQUIREMENT,
82 SPARSEREVLOG_REQUIREMENT,
83 RELATIVE_SHARED_REQUIREMENT,
83 RELATIVE_SHARED_REQUIREMENT,
84 REVLOG_COMPRESSION_ZSTD,
84 REVLOG_COMPRESSION_ZSTD,
85 // As of this writing everything rhg does is read-only.
85 // As of this writing everything rhg does is read-only.
86 // When it starts writing to the repository, it’ll need to either keep the
86 // When it starts writing to the repository, it’ll need to either keep the
87 // persistent nodemap up to date or remove this entry:
87 // persistent nodemap up to date or remove this entry:
88 NODEMAP_REQUIREMENT,
88 NODEMAP_REQUIREMENT,
89 ];
89 ];
90
90
91 // Copied from mercurial/requirements.py:
91 // Copied from mercurial/requirements.py:
92
92
93 /// When narrowing is finalized and no longer subject to format changes,
93 /// When narrowing is finalized and no longer subject to format changes,
94 /// we should move this to just "narrow" or similar.
94 /// we should move this to just "narrow" or similar.
95 #[allow(unused)]
95 #[allow(unused)]
96 pub(crate) const NARROW_REQUIREMENT: &str = "narrowhg-experimental";
96 pub(crate) const NARROW_REQUIREMENT: &str = "narrowhg-experimental";
97
97
98 /// Enables sparse working directory usage
98 /// Enables sparse working directory usage
99 #[allow(unused)]
99 #[allow(unused)]
100 pub(crate) const SPARSE_REQUIREMENT: &str = "exp-sparse";
100 pub(crate) const SPARSE_REQUIREMENT: &str = "exp-sparse";
101
101
102 /// Enables the internal phase which is used to hide changesets instead
102 /// Enables the internal phase which is used to hide changesets instead
103 /// of stripping them
103 /// of stripping them
104 #[allow(unused)]
104 #[allow(unused)]
105 pub(crate) const INTERNAL_PHASE_REQUIREMENT: &str = "internal-phase";
105 pub(crate) const INTERNAL_PHASE_REQUIREMENT: &str = "internal-phase";
106
106
107 /// Stores manifest in Tree structure
107 /// Stores manifest in Tree structure
108 #[allow(unused)]
108 #[allow(unused)]
109 pub(crate) const TREEMANIFEST_REQUIREMENT: &str = "treemanifest";
109 pub(crate) const TREEMANIFEST_REQUIREMENT: &str = "treemanifest";
110
110
111 /// Increment the sub-version when the revlog v2 format changes to lock out old
111 /// Increment the sub-version when the revlog v2 format changes to lock out old
112 /// clients.
112 /// clients.
113 #[allow(unused)]
113 #[allow(unused)]
114 pub(crate) const REVLOGV2_REQUIREMENT: &str = "exp-revlogv2.1";
114 pub(crate) const REVLOGV2_REQUIREMENT: &str = "exp-revlogv2.1";
115
115
116 /// A repository with the sparserevlog feature will have delta chains that
116 /// A repository with the sparserevlog feature will have delta chains that
117 /// can spread over a larger span. Sparse reading cuts these large spans into
117 /// can spread over a larger span. Sparse reading cuts these large spans into
118 /// pieces, so that each piece isn't too big.
118 /// pieces, so that each piece isn't too big.
119 /// Without the sparserevlog capability, reading from the repository could use
119 /// Without the sparserevlog capability, reading from the repository could use
120 /// huge amounts of memory, because the whole span would be read at once,
120 /// huge amounts of memory, because the whole span would be read at once,
121 /// including all the intermediate revisions that aren't pertinent for the
121 /// including all the intermediate revisions that aren't pertinent for the
122 /// chain. This is why once a repository has enabled sparse-read, it becomes
122 /// chain. This is why once a repository has enabled sparse-read, it becomes
123 /// required.
123 /// required.
124 #[allow(unused)]
124 #[allow(unused)]
125 pub(crate) const SPARSEREVLOG_REQUIREMENT: &str = "sparserevlog";
125 pub(crate) const SPARSEREVLOG_REQUIREMENT: &str = "sparserevlog";
126
126
127 /// A repository with the sidedataflag requirement will allow to store extra
128 /// information for revision without altering their original hashes.
129 #[allow(unused)]
130 pub(crate) const SIDEDATA_REQUIREMENT: &str = "exp-sidedata-flag";
131
132 /// A repository with the the copies-sidedata-changeset requirement will store
127 /// A repository with the the copies-sidedata-changeset requirement will store
133 /// copies related information in changeset's sidedata.
128 /// copies related information in changeset's sidedata.
134 #[allow(unused)]
129 #[allow(unused)]
135 pub(crate) const COPIESSDC_REQUIREMENT: &str = "exp-copies-sidedata-changeset";
130 pub(crate) const COPIESSDC_REQUIREMENT: &str = "exp-copies-sidedata-changeset";
136
131
137 /// The repository use persistent nodemap for the changelog and the manifest.
132 /// The repository use persistent nodemap for the changelog and the manifest.
138 #[allow(unused)]
133 #[allow(unused)]
139 pub(crate) const NODEMAP_REQUIREMENT: &str = "persistent-nodemap";
134 pub(crate) const NODEMAP_REQUIREMENT: &str = "persistent-nodemap";
140
135
141 /// Denotes that the current repository is a share
136 /// Denotes that the current repository is a share
142 #[allow(unused)]
137 #[allow(unused)]
143 pub(crate) const SHARED_REQUIREMENT: &str = "shared";
138 pub(crate) const SHARED_REQUIREMENT: &str = "shared";
144
139
145 /// Denotes that current repository is a share and the shared source path is
140 /// Denotes that current repository is a share and the shared source path is
146 /// relative to the current repository root path
141 /// relative to the current repository root path
147 #[allow(unused)]
142 #[allow(unused)]
148 pub(crate) const RELATIVE_SHARED_REQUIREMENT: &str = "relshared";
143 pub(crate) const RELATIVE_SHARED_REQUIREMENT: &str = "relshared";
149
144
150 /// A repository with share implemented safely. The repository has different
145 /// A repository with share implemented safely. The repository has different
151 /// store and working copy requirements i.e. both `.hg/requires` and
146 /// store and working copy requirements i.e. both `.hg/requires` and
152 /// `.hg/store/requires` are present.
147 /// `.hg/store/requires` are present.
153 #[allow(unused)]
148 #[allow(unused)]
154 pub(crate) const SHARESAFE_REQUIREMENT: &str = "share-safe";
149 pub(crate) const SHARESAFE_REQUIREMENT: &str = "share-safe";
155
150
156 /// A repository that use zstd compression inside its revlog
151 /// A repository that use zstd compression inside its revlog
157 #[allow(unused)]
152 #[allow(unused)]
158 pub(crate) const REVLOG_COMPRESSION_ZSTD: &str = "revlog-compression-zstd";
153 pub(crate) const REVLOG_COMPRESSION_ZSTD: &str = "revlog-compression-zstd";
@@ -1,3872 +1,3872 b''
1 #testcases filelog compatibility changeset sidedata upgraded upgraded-parallel pull push pull-upgrade push-upgrade
1 #testcases filelog compatibility changeset sidedata upgraded upgraded-parallel pull push pull-upgrade push-upgrade
2
2
3 =====================================================
3 =====================================================
4 Test Copy tracing for chain of copies involving merge
4 Test Copy tracing for chain of copies involving merge
5 =====================================================
5 =====================================================
6
6
7 This test files covers copies/rename case for a chains of commit where merges
7 This test files covers copies/rename case for a chains of commit where merges
8 are involved. It cheks we do not have unwanted update of behavior and that the
8 are involved. It cheks we do not have unwanted update of behavior and that the
9 different options to retrieve copies behave correctly.
9 different options to retrieve copies behave correctly.
10
10
11
11
12 Setup
12 Setup
13 =====
13 =====
14
14
15 use git diff to see rename
15 use git diff to see rename
16
16
17 $ cat << EOF >> ./no-linkrev
17 $ cat << EOF >> ./no-linkrev
18 > #!$PYTHON
18 > #!$PYTHON
19 > # filter out linkrev part of the debugindex command
19 > # filter out linkrev part of the debugindex command
20 > import sys
20 > import sys
21 > for line in sys.stdin:
21 > for line in sys.stdin:
22 > if " linkrev " in line:
22 > if " linkrev " in line:
23 > print(line.rstrip())
23 > print(line.rstrip())
24 > else:
24 > else:
25 > l = "%s *%s" % (line[:6], line[14:].rstrip())
25 > l = "%s *%s" % (line[:6], line[14:].rstrip())
26 > print(l)
26 > print(l)
27 > EOF
27 > EOF
28
28
29 $ cat << EOF >> $HGRCPATH
29 $ cat << EOF >> $HGRCPATH
30 > [diff]
30 > [diff]
31 > git=yes
31 > git=yes
32 > [command-templates]
32 > [command-templates]
33 > log={desc}\n
33 > log={desc}\n
34 > EOF
34 > EOF
35
35
36 #if compatibility
36 #if compatibility
37 $ cat >> $HGRCPATH << EOF
37 $ cat >> $HGRCPATH << EOF
38 > [experimental]
38 > [experimental]
39 > copies.read-from = compatibility
39 > copies.read-from = compatibility
40 > EOF
40 > EOF
41 #endif
41 #endif
42
42
43 #if changeset
43 #if changeset
44 $ cat >> $HGRCPATH << EOF
44 $ cat >> $HGRCPATH << EOF
45 > [experimental]
45 > [experimental]
46 > copies.read-from = changeset-only
46 > copies.read-from = changeset-only
47 > copies.write-to = changeset-only
47 > copies.write-to = changeset-only
48 > EOF
48 > EOF
49 #endif
49 #endif
50
50
51 #if sidedata
51 #if sidedata
52 $ cat >> $HGRCPATH << EOF
52 $ cat >> $HGRCPATH << EOF
53 > [format]
53 > [format]
54 > exp-use-copies-side-data-changeset = yes
54 > exp-use-copies-side-data-changeset = yes
55 > EOF
55 > EOF
56 #endif
56 #endif
57
57
58 #if pull
58 #if pull
59 $ cat >> $HGRCPATH << EOF
59 $ cat >> $HGRCPATH << EOF
60 > [format]
60 > [format]
61 > exp-use-copies-side-data-changeset = yes
61 > exp-use-copies-side-data-changeset = yes
62 > EOF
62 > EOF
63 #endif
63 #endif
64
64
65 #if push
65 #if push
66 $ cat >> $HGRCPATH << EOF
66 $ cat >> $HGRCPATH << EOF
67 > [format]
67 > [format]
68 > exp-use-copies-side-data-changeset = yes
68 > exp-use-copies-side-data-changeset = yes
69 > EOF
69 > EOF
70 #endif
70 #endif
71
71
72 #if pull-upgrade
72 #if pull-upgrade
73 $ cat >> $HGRCPATH << EOF
73 $ cat >> $HGRCPATH << EOF
74 > [format]
74 > [format]
75 > exp-use-copies-side-data-changeset = no
75 > exp-use-copies-side-data-changeset = no
76 > [experimental]
76 > [experimental]
77 > changegroup4 = yes
77 > changegroup4 = yes
78 > EOF
78 > EOF
79 #endif
79 #endif
80
80
81 #if push-upgrade
81 #if push-upgrade
82 $ cat >> $HGRCPATH << EOF
82 $ cat >> $HGRCPATH << EOF
83 > [format]
83 > [format]
84 > exp-use-copies-side-data-changeset = no
84 > exp-use-copies-side-data-changeset = no
85 > [experimental]
85 > [experimental]
86 > changegroup4 = yes
86 > changegroup4 = yes
87 > EOF
87 > EOF
88 #endif
88 #endif
89
89
90 $ cat > same-content.txt << EOF
90 $ cat > same-content.txt << EOF
91 > Here is some content that will be the same accros multiple file.
91 > Here is some content that will be the same accros multiple file.
92 >
92 >
93 > This is done on purpose so that we end up in some merge situation, were the
93 > This is done on purpose so that we end up in some merge situation, were the
94 > resulting content is the same as in the parent(s), but a new filenodes still
94 > resulting content is the same as in the parent(s), but a new filenodes still
95 > need to be created to record some file history information (especially
95 > need to be created to record some file history information (especially
96 > about copies).
96 > about copies).
97 > EOF
97 > EOF
98
98
99 $ hg init repo-chain
99 $ hg init repo-chain
100 $ cd repo-chain
100 $ cd repo-chain
101
101
102 Add some linear rename initialy
102 Add some linear rename initialy
103
103
104 $ cp ../same-content.txt a
104 $ cp ../same-content.txt a
105 $ cp ../same-content.txt b
105 $ cp ../same-content.txt b
106 $ cp ../same-content.txt h
106 $ cp ../same-content.txt h
107 $ echo "original content for P" > p
107 $ echo "original content for P" > p
108 $ echo "original content for Q" > q
108 $ echo "original content for Q" > q
109 $ echo "original content for R" > r
109 $ echo "original content for R" > r
110 $ hg ci -Am 'i-0 initial commit: a b h p q r'
110 $ hg ci -Am 'i-0 initial commit: a b h p q r'
111 adding a
111 adding a
112 adding b
112 adding b
113 adding h
113 adding h
114 adding p
114 adding p
115 adding q
115 adding q
116 adding r
116 adding r
117 $ hg mv a c
117 $ hg mv a c
118 $ hg mv p s
118 $ hg mv p s
119 $ hg ci -Am 'i-1: a -move-> c, p -move-> s'
119 $ hg ci -Am 'i-1: a -move-> c, p -move-> s'
120 $ hg mv c d
120 $ hg mv c d
121 $ hg mv s t
121 $ hg mv s t
122 $ hg ci -Am 'i-2: c -move-> d, s -move-> t'
122 $ hg ci -Am 'i-2: c -move-> d, s -move-> t'
123 $ hg log -G
123 $ hg log -G
124 @ i-2: c -move-> d, s -move-> t
124 @ i-2: c -move-> d, s -move-> t
125 |
125 |
126 o i-1: a -move-> c, p -move-> s
126 o i-1: a -move-> c, p -move-> s
127 |
127 |
128 o i-0 initial commit: a b h p q r
128 o i-0 initial commit: a b h p q r
129
129
130
130
131 And having another branch with renames on the other side
131 And having another branch with renames on the other side
132
132
133 $ hg mv d e
133 $ hg mv d e
134 $ hg ci -Am 'a-1: d -move-> e'
134 $ hg ci -Am 'a-1: d -move-> e'
135 $ hg mv e f
135 $ hg mv e f
136 $ hg ci -Am 'a-2: e -move-> f'
136 $ hg ci -Am 'a-2: e -move-> f'
137 $ hg log -G --rev '::.'
137 $ hg log -G --rev '::.'
138 @ a-2: e -move-> f
138 @ a-2: e -move-> f
139 |
139 |
140 o a-1: d -move-> e
140 o a-1: d -move-> e
141 |
141 |
142 o i-2: c -move-> d, s -move-> t
142 o i-2: c -move-> d, s -move-> t
143 |
143 |
144 o i-1: a -move-> c, p -move-> s
144 o i-1: a -move-> c, p -move-> s
145 |
145 |
146 o i-0 initial commit: a b h p q r
146 o i-0 initial commit: a b h p q r
147
147
148
148
149 Have a branching with nothing on one side
149 Have a branching with nothing on one side
150
150
151 $ hg up 'desc("i-2")'
151 $ hg up 'desc("i-2")'
152 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
152 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
153 $ echo foo > b
153 $ echo foo > b
154 $ hg ci -m 'b-1: b update'
154 $ hg ci -m 'b-1: b update'
155 created new head
155 created new head
156 $ hg log -G --rev '::.'
156 $ hg log -G --rev '::.'
157 @ b-1: b update
157 @ b-1: b update
158 |
158 |
159 o i-2: c -move-> d, s -move-> t
159 o i-2: c -move-> d, s -move-> t
160 |
160 |
161 o i-1: a -move-> c, p -move-> s
161 o i-1: a -move-> c, p -move-> s
162 |
162 |
163 o i-0 initial commit: a b h p q r
163 o i-0 initial commit: a b h p q r
164
164
165
165
166 Create a branch that delete a file previous renamed
166 Create a branch that delete a file previous renamed
167
167
168 $ hg up 'desc("i-2")'
168 $ hg up 'desc("i-2")'
169 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
169 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
170 $ hg rm d
170 $ hg rm d
171 $ hg ci -m 'c-1 delete d'
171 $ hg ci -m 'c-1 delete d'
172 created new head
172 created new head
173 $ hg log -G --rev '::.'
173 $ hg log -G --rev '::.'
174 @ c-1 delete d
174 @ c-1 delete d
175 |
175 |
176 o i-2: c -move-> d, s -move-> t
176 o i-2: c -move-> d, s -move-> t
177 |
177 |
178 o i-1: a -move-> c, p -move-> s
178 o i-1: a -move-> c, p -move-> s
179 |
179 |
180 o i-0 initial commit: a b h p q r
180 o i-0 initial commit: a b h p q r
181
181
182
182
183 Create a branch that delete a file previous renamed and recreate it
183 Create a branch that delete a file previous renamed and recreate it
184
184
185 $ hg up 'desc("i-2")'
185 $ hg up 'desc("i-2")'
186 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
186 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
187 $ hg rm d
187 $ hg rm d
188 $ hg ci -m 'd-1 delete d'
188 $ hg ci -m 'd-1 delete d'
189 created new head
189 created new head
190 $ echo bar > d
190 $ echo bar > d
191 $ hg add d
191 $ hg add d
192 $ hg ci -m 'd-2 re-add d'
192 $ hg ci -m 'd-2 re-add d'
193 $ hg log -G --rev '::.'
193 $ hg log -G --rev '::.'
194 @ d-2 re-add d
194 @ d-2 re-add d
195 |
195 |
196 o d-1 delete d
196 o d-1 delete d
197 |
197 |
198 o i-2: c -move-> d, s -move-> t
198 o i-2: c -move-> d, s -move-> t
199 |
199 |
200 o i-1: a -move-> c, p -move-> s
200 o i-1: a -move-> c, p -move-> s
201 |
201 |
202 o i-0 initial commit: a b h p q r
202 o i-0 initial commit: a b h p q r
203
203
204
204
205 Having another branch renaming a different file to the same filename as another
205 Having another branch renaming a different file to the same filename as another
206
206
207 $ hg up 'desc("i-2")'
207 $ hg up 'desc("i-2")'
208 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
208 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
209 $ hg mv b g
209 $ hg mv b g
210 $ hg ci -m 'e-1 b -move-> g'
210 $ hg ci -m 'e-1 b -move-> g'
211 created new head
211 created new head
212 $ hg mv g f
212 $ hg mv g f
213 $ hg ci -m 'e-2 g -move-> f'
213 $ hg ci -m 'e-2 g -move-> f'
214 $ hg log -G --rev '::.'
214 $ hg log -G --rev '::.'
215 @ e-2 g -move-> f
215 @ e-2 g -move-> f
216 |
216 |
217 o e-1 b -move-> g
217 o e-1 b -move-> g
218 |
218 |
219 o i-2: c -move-> d, s -move-> t
219 o i-2: c -move-> d, s -move-> t
220 |
220 |
221 o i-1: a -move-> c, p -move-> s
221 o i-1: a -move-> c, p -move-> s
222 |
222 |
223 o i-0 initial commit: a b h p q r
223 o i-0 initial commit: a b h p q r
224
224
225 $ hg up -q null
225 $ hg up -q null
226
226
227 Having a branch similar to the 'a' one, but moving the 'p' file around.
227 Having a branch similar to the 'a' one, but moving the 'p' file around.
228
228
229 $ hg up 'desc("i-2")'
229 $ hg up 'desc("i-2")'
230 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
230 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
231 $ hg mv t u
231 $ hg mv t u
232 $ hg ci -Am 'p-1: t -move-> u'
232 $ hg ci -Am 'p-1: t -move-> u'
233 created new head
233 created new head
234 $ hg mv u v
234 $ hg mv u v
235 $ hg ci -Am 'p-2: u -move-> v'
235 $ hg ci -Am 'p-2: u -move-> v'
236 $ hg log -G --rev '::.'
236 $ hg log -G --rev '::.'
237 @ p-2: u -move-> v
237 @ p-2: u -move-> v
238 |
238 |
239 o p-1: t -move-> u
239 o p-1: t -move-> u
240 |
240 |
241 o i-2: c -move-> d, s -move-> t
241 o i-2: c -move-> d, s -move-> t
242 |
242 |
243 o i-1: a -move-> c, p -move-> s
243 o i-1: a -move-> c, p -move-> s
244 |
244 |
245 o i-0 initial commit: a b h p q r
245 o i-0 initial commit: a b h p q r
246
246
247 $ hg up -q null
247 $ hg up -q null
248
248
249 Having another branch renaming a different file to the same filename as another
249 Having another branch renaming a different file to the same filename as another
250
250
251 $ hg up 'desc("i-2")'
251 $ hg up 'desc("i-2")'
252 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
252 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
253 $ hg mv r w
253 $ hg mv r w
254 $ hg ci -m 'q-1 r -move-> w'
254 $ hg ci -m 'q-1 r -move-> w'
255 created new head
255 created new head
256 $ hg mv w v
256 $ hg mv w v
257 $ hg ci -m 'q-2 w -move-> v'
257 $ hg ci -m 'q-2 w -move-> v'
258 $ hg log -G --rev '::.'
258 $ hg log -G --rev '::.'
259 @ q-2 w -move-> v
259 @ q-2 w -move-> v
260 |
260 |
261 o q-1 r -move-> w
261 o q-1 r -move-> w
262 |
262 |
263 o i-2: c -move-> d, s -move-> t
263 o i-2: c -move-> d, s -move-> t
264 |
264 |
265 o i-1: a -move-> c, p -move-> s
265 o i-1: a -move-> c, p -move-> s
266 |
266 |
267 o i-0 initial commit: a b h p q r
267 o i-0 initial commit: a b h p q r
268
268
269 $ hg up -q null
269 $ hg up -q null
270
270
271 Setup all merge
271 Setup all merge
272 ===============
272 ===============
273
273
274 This is done beforehand to validate that the upgrade process creates valid copy
274 This is done beforehand to validate that the upgrade process creates valid copy
275 information.
275 information.
276
276
277 merging with unrelated change does not interfere with the renames
277 merging with unrelated change does not interfere with the renames
278 ---------------------------------------------------------------
278 ---------------------------------------------------------------
279
279
280 - rename on one side
280 - rename on one side
281 - unrelated change on the other side
281 - unrelated change on the other side
282
282
283 $ case_desc="simple merge - A side: multiple renames, B side: unrelated update"
283 $ case_desc="simple merge - A side: multiple renames, B side: unrelated update"
284
284
285 $ hg up 'desc("b-1")'
285 $ hg up 'desc("b-1")'
286 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
286 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
287 $ hg merge 'desc("a-2")'
287 $ hg merge 'desc("a-2")'
288 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
288 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
289 (branch merge, don't forget to commit)
289 (branch merge, don't forget to commit)
290 $ hg ci -m "mBAm-0 $case_desc - one way"
290 $ hg ci -m "mBAm-0 $case_desc - one way"
291 $ hg up 'desc("a-2")'
291 $ hg up 'desc("a-2")'
292 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
292 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
293 $ hg merge 'desc("b-1")'
293 $ hg merge 'desc("b-1")'
294 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
294 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
295 (branch merge, don't forget to commit)
295 (branch merge, don't forget to commit)
296 $ hg ci -m "mABm-0 $case_desc - the other way"
296 $ hg ci -m "mABm-0 $case_desc - the other way"
297 created new head
297 created new head
298 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
298 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
299 @ mABm-0 simple merge - A side: multiple renames, B side: unrelated update - the other way
299 @ mABm-0 simple merge - A side: multiple renames, B side: unrelated update - the other way
300 |\
300 |\
301 +---o mBAm-0 simple merge - A side: multiple renames, B side: unrelated update - one way
301 +---o mBAm-0 simple merge - A side: multiple renames, B side: unrelated update - one way
302 | |/
302 | |/
303 | o b-1: b update
303 | o b-1: b update
304 | |
304 | |
305 o | a-2: e -move-> f
305 o | a-2: e -move-> f
306 | |
306 | |
307 o | a-1: d -move-> e
307 o | a-1: d -move-> e
308 |/
308 |/
309 o i-2: c -move-> d, s -move-> t
309 o i-2: c -move-> d, s -move-> t
310 |
310 |
311 o i-1: a -move-> c, p -move-> s
311 o i-1: a -move-> c, p -move-> s
312 |
312 |
313 o i-0 initial commit: a b h p q r
313 o i-0 initial commit: a b h p q r
314
314
315
315
316
316
317 merging with the side having a delete
317 merging with the side having a delete
318 -------------------------------------
318 -------------------------------------
319
319
320 case summary:
320 case summary:
321 - one with change to an unrelated file
321 - one with change to an unrelated file
322 - one deleting the change
322 - one deleting the change
323 and recreate an unrelated file after the merge
323 and recreate an unrelated file after the merge
324
324
325 $ case_desc="simple merge - C side: delete a file with copies history , B side: unrelated update"
325 $ case_desc="simple merge - C side: delete a file with copies history , B side: unrelated update"
326
326
327 $ hg up 'desc("b-1")'
327 $ hg up 'desc("b-1")'
328 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
328 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
329 $ hg merge 'desc("c-1")'
329 $ hg merge 'desc("c-1")'
330 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
330 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
331 (branch merge, don't forget to commit)
331 (branch merge, don't forget to commit)
332 $ hg ci -m "mBCm-0 $case_desc - one way"
332 $ hg ci -m "mBCm-0 $case_desc - one way"
333 $ echo bar > d
333 $ echo bar > d
334 $ hg add d
334 $ hg add d
335 $ hg ci -m 'mBCm-1 re-add d'
335 $ hg ci -m 'mBCm-1 re-add d'
336 $ hg up 'desc("c-1")'
336 $ hg up 'desc("c-1")'
337 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
337 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
338 $ hg merge 'desc("b-1")'
338 $ hg merge 'desc("b-1")'
339 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
339 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
340 (branch merge, don't forget to commit)
340 (branch merge, don't forget to commit)
341 $ hg ci -m "mCBm-0 $case_desc - the other way"
341 $ hg ci -m "mCBm-0 $case_desc - the other way"
342 created new head
342 created new head
343 $ echo bar > d
343 $ echo bar > d
344 $ hg add d
344 $ hg add d
345 $ hg ci -m 'mCBm-1 re-add d'
345 $ hg ci -m 'mCBm-1 re-add d'
346 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
346 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
347 @ mCBm-1 re-add d
347 @ mCBm-1 re-add d
348 |
348 |
349 o mCBm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - the other way
349 o mCBm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - the other way
350 |\
350 |\
351 | | o mBCm-1 re-add d
351 | | o mBCm-1 re-add d
352 | | |
352 | | |
353 +---o mBCm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - one way
353 +---o mBCm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - one way
354 | |/
354 | |/
355 | o c-1 delete d
355 | o c-1 delete d
356 | |
356 | |
357 o | b-1: b update
357 o | b-1: b update
358 |/
358 |/
359 o i-2: c -move-> d, s -move-> t
359 o i-2: c -move-> d, s -move-> t
360 |
360 |
361 o i-1: a -move-> c, p -move-> s
361 o i-1: a -move-> c, p -move-> s
362 |
362 |
363 o i-0 initial commit: a b h p q r
363 o i-0 initial commit: a b h p q r
364
364
365
365
366 Comparing with a merge re-adding the file afterward
366 Comparing with a merge re-adding the file afterward
367 ---------------------------------------------------
367 ---------------------------------------------------
368
368
369 Merge:
369 Merge:
370 - one with change to an unrelated file
370 - one with change to an unrelated file
371 - one deleting and recreating the change
371 - one deleting and recreating the change
372
372
373 $ case_desc="simple merge - B side: unrelated update, D side: delete and recreate a file (with different content)"
373 $ case_desc="simple merge - B side: unrelated update, D side: delete and recreate a file (with different content)"
374
374
375 $ hg up 'desc("b-1")'
375 $ hg up 'desc("b-1")'
376 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
376 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
377 $ hg merge 'desc("d-2")'
377 $ hg merge 'desc("d-2")'
378 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
378 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
379 (branch merge, don't forget to commit)
379 (branch merge, don't forget to commit)
380 $ hg ci -m "mBDm-0 $case_desc - one way"
380 $ hg ci -m "mBDm-0 $case_desc - one way"
381 $ hg up 'desc("d-2")'
381 $ hg up 'desc("d-2")'
382 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
382 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
383 $ hg merge 'desc("b-1")'
383 $ hg merge 'desc("b-1")'
384 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
384 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
385 (branch merge, don't forget to commit)
385 (branch merge, don't forget to commit)
386 $ hg ci -m "mDBm-0 $case_desc - the other way"
386 $ hg ci -m "mDBm-0 $case_desc - the other way"
387 created new head
387 created new head
388 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
388 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
389 @ mDBm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - the other way
389 @ mDBm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - the other way
390 |\
390 |\
391 +---o mBDm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - one way
391 +---o mBDm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - one way
392 | |/
392 | |/
393 | o d-2 re-add d
393 | o d-2 re-add d
394 | |
394 | |
395 | o d-1 delete d
395 | o d-1 delete d
396 | |
396 | |
397 o | b-1: b update
397 o | b-1: b update
398 |/
398 |/
399 o i-2: c -move-> d, s -move-> t
399 o i-2: c -move-> d, s -move-> t
400 |
400 |
401 o i-1: a -move-> c, p -move-> s
401 o i-1: a -move-> c, p -move-> s
402 |
402 |
403 o i-0 initial commit: a b h p q r
403 o i-0 initial commit: a b h p q r
404
404
405
405
406
406
407 Comparing with a merge with colliding rename
407 Comparing with a merge with colliding rename
408 --------------------------------------------
408 --------------------------------------------
409
409
410 Subcase: new copy information on both side
410 Subcase: new copy information on both side
411 ``````````````````````````````````````````
411 ``````````````````````````````````````````
412
412
413 - the "e-" branch renaming b to f (through 'g')
413 - the "e-" branch renaming b to f (through 'g')
414 - the "a-" branch renaming d to f (through e)
414 - the "a-" branch renaming d to f (through e)
415
415
416 $ case_desc="merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f)"
416 $ case_desc="merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f)"
417
417
418 $ hg up 'desc("a-2")'
418 $ hg up 'desc("a-2")'
419 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
419 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
420 $ hg merge 'desc("e-2")'
420 $ hg merge 'desc("e-2")'
421 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
421 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
422 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
422 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
423 (branch merge, don't forget to commit)
423 (branch merge, don't forget to commit)
424 $ hg ci -m "mAEm-0 $case_desc - one way"
424 $ hg ci -m "mAEm-0 $case_desc - one way"
425 $ hg up 'desc("e-2")'
425 $ hg up 'desc("e-2")'
426 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
426 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
427 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
427 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
428 $ hg merge 'desc("a-2")'
428 $ hg merge 'desc("a-2")'
429 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
429 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
430 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
430 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
431 (branch merge, don't forget to commit)
431 (branch merge, don't forget to commit)
432 $ hg ci -m "mEAm-0 $case_desc - the other way"
432 $ hg ci -m "mEAm-0 $case_desc - the other way"
433 created new head
433 created new head
434 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
434 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
435 @ mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
435 @ mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
436 |\
436 |\
437 +---o mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
437 +---o mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
438 | |/
438 | |/
439 | o e-2 g -move-> f
439 | o e-2 g -move-> f
440 | |
440 | |
441 | o e-1 b -move-> g
441 | o e-1 b -move-> g
442 | |
442 | |
443 o | a-2: e -move-> f
443 o | a-2: e -move-> f
444 | |
444 | |
445 o | a-1: d -move-> e
445 o | a-1: d -move-> e
446 |/
446 |/
447 o i-2: c -move-> d, s -move-> t
447 o i-2: c -move-> d, s -move-> t
448 |
448 |
449 o i-1: a -move-> c, p -move-> s
449 o i-1: a -move-> c, p -move-> s
450 |
450 |
451 o i-0 initial commit: a b h p q r
451 o i-0 initial commit: a b h p q r
452
452
453
453
454 Subcase: new copy information on both side with an actual merge happening
454 Subcase: new copy information on both side with an actual merge happening
455 `````````````````````````````````````````````````````````````````````````
455 `````````````````````````````````````````````````````````````````````````
456
456
457 - the "p-" branch renaming 't' to 'v' (through 'u')
457 - the "p-" branch renaming 't' to 'v' (through 'u')
458 - the "q-" branch renaming 'r' to 'v' (through 'w')
458 - the "q-" branch renaming 'r' to 'v' (through 'w')
459
459
460 $ case_desc="merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content)"
460 $ case_desc="merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content)"
461
461
462 $ hg up 'desc("p-2")'
462 $ hg up 'desc("p-2")'
463 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
463 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
464 $ hg merge 'desc("q-2")' --tool ':union'
464 $ hg merge 'desc("q-2")' --tool ':union'
465 merging v
465 merging v
466 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
466 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
467 (branch merge, don't forget to commit)
467 (branch merge, don't forget to commit)
468 $ hg ci -m "mPQm-0 $case_desc - one way"
468 $ hg ci -m "mPQm-0 $case_desc - one way"
469 $ hg up 'desc("q-2")'
469 $ hg up 'desc("q-2")'
470 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
470 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
471 $ hg merge 'desc("p-2")' --tool ':union'
471 $ hg merge 'desc("p-2")' --tool ':union'
472 merging v
472 merging v
473 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
473 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
474 (branch merge, don't forget to commit)
474 (branch merge, don't forget to commit)
475 $ hg ci -m "mQPm-0 $case_desc - the other way"
475 $ hg ci -m "mQPm-0 $case_desc - the other way"
476 created new head
476 created new head
477 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
477 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
478 o mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
478 o mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
479 |\
479 |\
480 +---o mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
480 +---o mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
481 | |/
481 | |/
482 | o e-2 g -move-> f
482 | o e-2 g -move-> f
483 | |
483 | |
484 | o e-1 b -move-> g
484 | o e-1 b -move-> g
485 | |
485 | |
486 o | a-2: e -move-> f
486 o | a-2: e -move-> f
487 | |
487 | |
488 o | a-1: d -move-> e
488 o | a-1: d -move-> e
489 |/
489 |/
490 o i-2: c -move-> d, s -move-> t
490 o i-2: c -move-> d, s -move-> t
491 |
491 |
492 o i-1: a -move-> c, p -move-> s
492 o i-1: a -move-> c, p -move-> s
493 |
493 |
494 o i-0 initial commit: a b h p q r
494 o i-0 initial commit: a b h p q r
495
495
496
496
497 Subcase: existing copy information overwritten on one branch
497 Subcase: existing copy information overwritten on one branch
498 ````````````````````````````````````````````````````````````
498 ````````````````````````````````````````````````````````````
499
499
500 Merge:
500 Merge:
501 - one with change to an unrelated file (b)
501 - one with change to an unrelated file (b)
502 - one overwriting a file (d) with a rename (from h to i to d)
502 - one overwriting a file (d) with a rename (from h to i to d)
503
503
504 $ case_desc="simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d)"
504 $ case_desc="simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d)"
505
505
506 $ hg up 'desc("i-2")'
506 $ hg up 'desc("i-2")'
507 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
507 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
508 $ hg mv h i
508 $ hg mv h i
509 $ hg commit -m "f-1: rename h -> i"
509 $ hg commit -m "f-1: rename h -> i"
510 created new head
510 created new head
511 $ hg mv --force i d
511 $ hg mv --force i d
512 $ hg commit -m "f-2: rename i -> d"
512 $ hg commit -m "f-2: rename i -> d"
513 $ hg debugindex d | "$PYTHON" ../no-linkrev
513 $ hg debugindex d | "$PYTHON" ../no-linkrev
514 rev linkrev nodeid p1 p2
514 rev linkrev nodeid p1 p2
515 0 * d8252ab2e760 000000000000 000000000000 (no-changeset !)
515 0 * d8252ab2e760 000000000000 000000000000 (no-changeset !)
516 0 * ae258f702dfe 000000000000 000000000000 (changeset !)
516 0 * ae258f702dfe 000000000000 000000000000 (changeset !)
517 1 * b004912a8510 000000000000 000000000000
517 1 * b004912a8510 000000000000 000000000000
518 2 * 7b79e2fe0c89 000000000000 000000000000 (no-changeset !)
518 2 * 7b79e2fe0c89 000000000000 000000000000 (no-changeset !)
519 $ hg up 'desc("b-1")'
519 $ hg up 'desc("b-1")'
520 3 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
520 3 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
521 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
521 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
522 $ hg merge 'desc("f-2")'
522 $ hg merge 'desc("f-2")'
523 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
523 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
524 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
524 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
525 (branch merge, don't forget to commit)
525 (branch merge, don't forget to commit)
526 $ hg ci -m "mBFm-0 $case_desc - one way"
526 $ hg ci -m "mBFm-0 $case_desc - one way"
527 $ hg up 'desc("f-2")'
527 $ hg up 'desc("f-2")'
528 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
528 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
529 $ hg merge 'desc("b-1")'
529 $ hg merge 'desc("b-1")'
530 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
530 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
531 (branch merge, don't forget to commit)
531 (branch merge, don't forget to commit)
532 $ hg ci -m "mFBm-0 $case_desc - the other way"
532 $ hg ci -m "mFBm-0 $case_desc - the other way"
533 created new head
533 created new head
534 $ hg up null --quiet
534 $ hg up null --quiet
535 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
535 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
536 o mFBm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
536 o mFBm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
537 |\
537 |\
538 +---o mBFm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
538 +---o mBFm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
539 | |/
539 | |/
540 | o f-2: rename i -> d
540 | o f-2: rename i -> d
541 | |
541 | |
542 | o f-1: rename h -> i
542 | o f-1: rename h -> i
543 | |
543 | |
544 o | b-1: b update
544 o | b-1: b update
545 |/
545 |/
546 o i-2: c -move-> d, s -move-> t
546 o i-2: c -move-> d, s -move-> t
547 |
547 |
548 o i-1: a -move-> c, p -move-> s
548 o i-1: a -move-> c, p -move-> s
549 |
549 |
550 o i-0 initial commit: a b h p q r
550 o i-0 initial commit: a b h p q r
551
551
552
552
553 Subcase: existing copy information overwritten on one branch, with different content)
553 Subcase: existing copy information overwritten on one branch, with different content)
554 `````````````````````````````````````````````````````````````````````````````````````
554 `````````````````````````````````````````````````````````````````````````````````````
555
555
556 Merge:
556 Merge:
557 - one with change to an unrelated file (b)
557 - one with change to an unrelated file (b)
558 - one overwriting a file (t) with a rename (from r to x to t), v content is not the same as on the other branch
558 - one overwriting a file (t) with a rename (from r to x to t), v content is not the same as on the other branch
559
559
560 $ case_desc="simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content"
560 $ case_desc="simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content"
561
561
562 $ hg up 'desc("i-2")'
562 $ hg up 'desc("i-2")'
563 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
563 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
564 $ hg mv r x
564 $ hg mv r x
565 $ hg commit -m "r-1: rename r -> x"
565 $ hg commit -m "r-1: rename r -> x"
566 created new head
566 created new head
567 $ hg mv --force x t
567 $ hg mv --force x t
568 $ hg commit -m "r-2: rename t -> x"
568 $ hg commit -m "r-2: rename t -> x"
569 $ hg debugindex t | "$PYTHON" ../no-linkrev
569 $ hg debugindex t | "$PYTHON" ../no-linkrev
570 rev linkrev nodeid p1 p2
570 rev linkrev nodeid p1 p2
571 0 * d74efbf65309 000000000000 000000000000 (no-changeset !)
571 0 * d74efbf65309 000000000000 000000000000 (no-changeset !)
572 1 * 02a930b9d7ad 000000000000 000000000000 (no-changeset !)
572 1 * 02a930b9d7ad 000000000000 000000000000 (no-changeset !)
573 0 * 5aed6a8dbff0 000000000000 000000000000 (changeset !)
573 0 * 5aed6a8dbff0 000000000000 000000000000 (changeset !)
574 1 * a38b2fa17021 000000000000 000000000000 (changeset !)
574 1 * a38b2fa17021 000000000000 000000000000 (changeset !)
575 $ hg up 'desc("b-1")'
575 $ hg up 'desc("b-1")'
576 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
576 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
577 $ hg merge 'desc("r-2")'
577 $ hg merge 'desc("r-2")'
578 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
578 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
579 (branch merge, don't forget to commit)
579 (branch merge, don't forget to commit)
580 $ hg ci -m "mBRm-0 $case_desc - one way"
580 $ hg ci -m "mBRm-0 $case_desc - one way"
581 $ hg up 'desc("r-2")'
581 $ hg up 'desc("r-2")'
582 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
582 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
583 $ hg merge 'desc("b-1")'
583 $ hg merge 'desc("b-1")'
584 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
584 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
585 (branch merge, don't forget to commit)
585 (branch merge, don't forget to commit)
586 $ hg ci -m "mRBm-0 $case_desc - the other way"
586 $ hg ci -m "mRBm-0 $case_desc - the other way"
587 created new head
587 created new head
588 $ hg up null --quiet
588 $ hg up null --quiet
589 $ hg log -G --rev '::(desc("mBRm")+desc("mRBm"))'
589 $ hg log -G --rev '::(desc("mBRm")+desc("mRBm"))'
590 o mRBm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - the other way
590 o mRBm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - the other way
591 |\
591 |\
592 +---o mBRm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - one way
592 +---o mBRm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - one way
593 | |/
593 | |/
594 | o r-2: rename t -> x
594 | o r-2: rename t -> x
595 | |
595 | |
596 | o r-1: rename r -> x
596 | o r-1: rename r -> x
597 | |
597 | |
598 o | b-1: b update
598 o | b-1: b update
599 |/
599 |/
600 o i-2: c -move-> d, s -move-> t
600 o i-2: c -move-> d, s -move-> t
601 |
601 |
602 o i-1: a -move-> c, p -move-> s
602 o i-1: a -move-> c, p -move-> s
603 |
603 |
604 o i-0 initial commit: a b h p q r
604 o i-0 initial commit: a b h p q r
605
605
606
606
607
607
608 Subcase: reset of the copy history on one side
608 Subcase: reset of the copy history on one side
609 ``````````````````````````````````````````````
609 ``````````````````````````````````````````````
610
610
611 Merge:
611 Merge:
612 - one with change to a file
612 - one with change to a file
613 - one deleting and recreating the file
613 - one deleting and recreating the file
614
614
615 Unlike in the 'BD/DB' cases, an actual merge happened here. So we should
615 Unlike in the 'BD/DB' cases, an actual merge happened here. So we should
616 consider history and rename on both branch of the merge.
616 consider history and rename on both branch of the merge.
617
617
618 $ case_desc="actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content"
618 $ case_desc="actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content"
619
619
620 $ hg up 'desc("i-2")'
620 $ hg up 'desc("i-2")'
621 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
621 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
622 $ echo "some update" >> d
622 $ echo "some update" >> d
623 $ hg commit -m "g-1: update d"
623 $ hg commit -m "g-1: update d"
624 created new head
624 created new head
625 $ hg up 'desc("d-2")'
625 $ hg up 'desc("d-2")'
626 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
626 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
627 $ hg merge 'desc("g-1")' --tool :union
627 $ hg merge 'desc("g-1")' --tool :union
628 merging d
628 merging d
629 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
629 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
630 (branch merge, don't forget to commit)
630 (branch merge, don't forget to commit)
631 $ hg ci -m "mDGm-0 $case_desc - one way"
631 $ hg ci -m "mDGm-0 $case_desc - one way"
632 $ hg up 'desc("g-1")'
632 $ hg up 'desc("g-1")'
633 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
633 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
634 $ hg merge 'desc("d-2")' --tool :union
634 $ hg merge 'desc("d-2")' --tool :union
635 merging d
635 merging d
636 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
636 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
637 (branch merge, don't forget to commit)
637 (branch merge, don't forget to commit)
638 $ hg ci -m "mGDm-0 $case_desc - the other way"
638 $ hg ci -m "mGDm-0 $case_desc - the other way"
639 created new head
639 created new head
640 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
640 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
641 @ mGDm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - the other way
641 @ mGDm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - the other way
642 |\
642 |\
643 +---o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
643 +---o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
644 | |/
644 | |/
645 | o g-1: update d
645 | o g-1: update d
646 | |
646 | |
647 o | d-2 re-add d
647 o | d-2 re-add d
648 | |
648 | |
649 o | d-1 delete d
649 o | d-1 delete d
650 |/
650 |/
651 o i-2: c -move-> d, s -move-> t
651 o i-2: c -move-> d, s -move-> t
652 |
652 |
653 o i-1: a -move-> c, p -move-> s
653 o i-1: a -move-> c, p -move-> s
654 |
654 |
655 o i-0 initial commit: a b h p q r
655 o i-0 initial commit: a b h p q r
656
656
657
657
658 Subcase: merging a change to a file with a "copy overwrite" to that file from another branch
658 Subcase: merging a change to a file with a "copy overwrite" to that file from another branch
659 ````````````````````````````````````````````````````````````````````````````````````````````
659 ````````````````````````````````````````````````````````````````````````````````````````````
660
660
661 Merge:
661 Merge:
662 - one with change to a file (d)
662 - one with change to a file (d)
663 - one overwriting that file with a rename (from h to i, to d)
663 - one overwriting that file with a rename (from h to i, to d)
664
664
665 This case is similar to BF/FB, but an actual merge happens, so both side of the
665 This case is similar to BF/FB, but an actual merge happens, so both side of the
666 history are relevant.
666 history are relevant.
667
667
668 Note:
668 Note:
669 | In this case, the merge get conflicting information since on one side we have
669 | In this case, the merge get conflicting information since on one side we have
670 | "a -> c -> d". and one the other one we have "h -> i -> d".
670 | "a -> c -> d". and one the other one we have "h -> i -> d".
671 |
671 |
672 | The current code arbitrarily pick one side
672 | The current code arbitrarily pick one side
673
673
674 $ case_desc="merge - G side: content change, F side: copy overwrite, no content change"
674 $ case_desc="merge - G side: content change, F side: copy overwrite, no content change"
675
675
676 $ hg up 'desc("f-2")'
676 $ hg up 'desc("f-2")'
677 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
677 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
678 $ hg merge 'desc("g-1")' --tool :union
678 $ hg merge 'desc("g-1")' --tool :union
679 merging d (no-changeset !)
679 merging d (no-changeset !)
680 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (no-changeset !)
680 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (no-changeset !)
681 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
681 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
682 (branch merge, don't forget to commit)
682 (branch merge, don't forget to commit)
683 $ hg ci -m "mFGm-0 $case_desc - one way"
683 $ hg ci -m "mFGm-0 $case_desc - one way"
684 created new head
684 created new head
685 $ hg up 'desc("g-1")'
685 $ hg up 'desc("g-1")'
686 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
686 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
687 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
687 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
688 $ hg merge 'desc("f-2")' --tool :union
688 $ hg merge 'desc("f-2")' --tool :union
689 merging d (no-changeset !)
689 merging d (no-changeset !)
690 0 files updated, 1 files merged, 1 files removed, 0 files unresolved (no-changeset !)
690 0 files updated, 1 files merged, 1 files removed, 0 files unresolved (no-changeset !)
691 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
691 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
692 (branch merge, don't forget to commit)
692 (branch merge, don't forget to commit)
693 $ hg ci -m "mGFm-0 $case_desc - the other way"
693 $ hg ci -m "mGFm-0 $case_desc - the other way"
694 created new head
694 created new head
695 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
695 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
696 @ mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
696 @ mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
697 |\
697 |\
698 +---o mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
698 +---o mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
699 | |/
699 | |/
700 | o g-1: update d
700 | o g-1: update d
701 | |
701 | |
702 o | f-2: rename i -> d
702 o | f-2: rename i -> d
703 | |
703 | |
704 o | f-1: rename h -> i
704 o | f-1: rename h -> i
705 |/
705 |/
706 o i-2: c -move-> d, s -move-> t
706 o i-2: c -move-> d, s -move-> t
707 |
707 |
708 o i-1: a -move-> c, p -move-> s
708 o i-1: a -move-> c, p -move-> s
709 |
709 |
710 o i-0 initial commit: a b h p q r
710 o i-0 initial commit: a b h p q r
711
711
712
712
713
713
714 Comparing with merging with a deletion (and keeping the file)
714 Comparing with merging with a deletion (and keeping the file)
715 -------------------------------------------------------------
715 -------------------------------------------------------------
716
716
717 Merge:
717 Merge:
718 - one removing a file (d)
718 - one removing a file (d)
719 - one updating that file
719 - one updating that file
720 - the merge keep the modified version of the file (canceling the delete)
720 - the merge keep the modified version of the file (canceling the delete)
721
721
722 In this case, the file keep on living after the merge. So we should not drop its
722 In this case, the file keep on living after the merge. So we should not drop its
723 copy tracing chain.
723 copy tracing chain.
724
724
725 $ case_desc="merge updated/deleted - revive the file (updated content)"
725 $ case_desc="merge updated/deleted - revive the file (updated content)"
726
726
727 $ hg up 'desc("c-1")'
727 $ hg up 'desc("c-1")'
728 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
728 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
729 $ hg merge 'desc("g-1")'
729 $ hg merge 'desc("g-1")'
730 file 'd' was deleted in local [working copy] but was modified in other [merge rev].
730 file 'd' was deleted in local [working copy] but was modified in other [merge rev].
731 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
731 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
732 What do you want to do? u
732 What do you want to do? u
733 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
733 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
734 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
734 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
735 [1]
735 [1]
736 $ hg resolve -t :other d
736 $ hg resolve -t :other d
737 (no more unresolved files)
737 (no more unresolved files)
738 $ hg ci -m "mCGm-0 $case_desc - one way"
738 $ hg ci -m "mCGm-0 $case_desc - one way"
739 created new head
739 created new head
740
740
741 $ hg up 'desc("g-1")'
741 $ hg up 'desc("g-1")'
742 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
742 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
743 $ hg merge 'desc("c-1")'
743 $ hg merge 'desc("c-1")'
744 file 'd' was deleted in other [merge rev] but was modified in local [working copy].
744 file 'd' was deleted in other [merge rev] but was modified in local [working copy].
745 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
745 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
746 What do you want to do? u
746 What do you want to do? u
747 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
747 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
748 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
748 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
749 [1]
749 [1]
750 $ hg resolve -t :local d
750 $ hg resolve -t :local d
751 (no more unresolved files)
751 (no more unresolved files)
752 $ hg ci -m "mGCm-0 $case_desc - the other way"
752 $ hg ci -m "mGCm-0 $case_desc - the other way"
753 created new head
753 created new head
754
754
755 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
755 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
756 @ mGCm-0 merge updated/deleted - revive the file (updated content) - the other way
756 @ mGCm-0 merge updated/deleted - revive the file (updated content) - the other way
757 |\
757 |\
758 +---o mCGm-0 merge updated/deleted - revive the file (updated content) - one way
758 +---o mCGm-0 merge updated/deleted - revive the file (updated content) - one way
759 | |/
759 | |/
760 | o g-1: update d
760 | o g-1: update d
761 | |
761 | |
762 o | c-1 delete d
762 o | c-1 delete d
763 |/
763 |/
764 o i-2: c -move-> d, s -move-> t
764 o i-2: c -move-> d, s -move-> t
765 |
765 |
766 o i-1: a -move-> c, p -move-> s
766 o i-1: a -move-> c, p -move-> s
767 |
767 |
768 o i-0 initial commit: a b h p q r
768 o i-0 initial commit: a b h p q r
769
769
770
770
771
771
772
772
773 Comparing with merge restoring an untouched deleted file
773 Comparing with merge restoring an untouched deleted file
774 --------------------------------------------------------
774 --------------------------------------------------------
775
775
776 Merge:
776 Merge:
777 - one removing a file (d)
777 - one removing a file (d)
778 - one leaving the file untouched
778 - one leaving the file untouched
779 - the merge actively restore the file to the same content.
779 - the merge actively restore the file to the same content.
780
780
781 In this case, the file keep on living after the merge. So we should not drop its
781 In this case, the file keep on living after the merge. So we should not drop its
782 copy tracing chain.
782 copy tracing chain.
783
783
784 $ case_desc="merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge)"
784 $ case_desc="merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge)"
785
785
786 $ hg up 'desc("c-1")'
786 $ hg up 'desc("c-1")'
787 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
787 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
788 $ hg merge 'desc("b-1")'
788 $ hg merge 'desc("b-1")'
789 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
789 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
790 (branch merge, don't forget to commit)
790 (branch merge, don't forget to commit)
791 $ hg revert --rev 'desc("b-1")' d
791 $ hg revert --rev 'desc("b-1")' d
792 $ hg ci -m "mCB-revert-m-0 $case_desc - one way"
792 $ hg ci -m "mCB-revert-m-0 $case_desc - one way"
793 created new head
793 created new head
794
794
795 $ hg up 'desc("b-1")'
795 $ hg up 'desc("b-1")'
796 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
796 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
797 $ hg merge 'desc("c-1")'
797 $ hg merge 'desc("c-1")'
798 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
798 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
799 (branch merge, don't forget to commit)
799 (branch merge, don't forget to commit)
800 $ hg revert --rev 'desc("b-1")' d
800 $ hg revert --rev 'desc("b-1")' d
801 $ hg ci -m "mBC-revert-m-0 $case_desc - the other way"
801 $ hg ci -m "mBC-revert-m-0 $case_desc - the other way"
802 created new head
802 created new head
803
803
804 $ hg log -G --rev '::(desc("mCB-revert-m")+desc("mBC-revert-m"))'
804 $ hg log -G --rev '::(desc("mCB-revert-m")+desc("mBC-revert-m"))'
805 @ mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
805 @ mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
806 |\
806 |\
807 +---o mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
807 +---o mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
808 | |/
808 | |/
809 | o c-1 delete d
809 | o c-1 delete d
810 | |
810 | |
811 o | b-1: b update
811 o | b-1: b update
812 |/
812 |/
813 o i-2: c -move-> d, s -move-> t
813 o i-2: c -move-> d, s -move-> t
814 |
814 |
815 o i-1: a -move-> c, p -move-> s
815 o i-1: a -move-> c, p -move-> s
816 |
816 |
817 o i-0 initial commit: a b h p q r
817 o i-0 initial commit: a b h p q r
818
818
819
819
820
820
821 $ hg up null --quiet
821 $ hg up null --quiet
822
822
823 Merging a branch where a rename was deleted with a branch where the same file was renamed
823 Merging a branch where a rename was deleted with a branch where the same file was renamed
824 ------------------------------------------------------------------------------------------
824 ------------------------------------------------------------------------------------------
825
825
826 Create a "conflicting" merge where `d` get removed on one branch before its
826 Create a "conflicting" merge where `d` get removed on one branch before its
827 rename information actually conflict with the other branch.
827 rename information actually conflict with the other branch.
828
828
829 (the copy information from the branch that was not deleted should win).
829 (the copy information from the branch that was not deleted should win).
830
830
831 $ case_desc="simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch)"
831 $ case_desc="simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch)"
832
832
833 $ hg up 'desc("i-0")'
833 $ hg up 'desc("i-0")'
834 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
834 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
835 $ hg mv b d
835 $ hg mv b d
836 $ hg ci -m "h-1: b -(move)-> d"
836 $ hg ci -m "h-1: b -(move)-> d"
837 created new head
837 created new head
838
838
839 $ hg up 'desc("c-1")'
839 $ hg up 'desc("c-1")'
840 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
840 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
841 $ hg merge 'desc("h-1")'
841 $ hg merge 'desc("h-1")'
842 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
842 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
843 (branch merge, don't forget to commit)
843 (branch merge, don't forget to commit)
844 $ hg ci -m "mCH-delete-before-conflict-m-0 $case_desc - one way"
844 $ hg ci -m "mCH-delete-before-conflict-m-0 $case_desc - one way"
845
845
846 $ hg up 'desc("h-1")'
846 $ hg up 'desc("h-1")'
847 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
847 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
848 $ hg merge 'desc("c-1")'
848 $ hg merge 'desc("c-1")'
849 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
849 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
850 (branch merge, don't forget to commit)
850 (branch merge, don't forget to commit)
851 $ hg ci -m "mHC-delete-before-conflict-m-0 $case_desc - the other way"
851 $ hg ci -m "mHC-delete-before-conflict-m-0 $case_desc - the other way"
852 created new head
852 created new head
853 $ hg log -G --rev '::(desc("mCH-delete-before-conflict-m")+desc("mHC-delete-before-conflict-m"))'
853 $ hg log -G --rev '::(desc("mCH-delete-before-conflict-m")+desc("mHC-delete-before-conflict-m"))'
854 @ mHC-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - the other way
854 @ mHC-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - the other way
855 |\
855 |\
856 +---o mCH-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - one way
856 +---o mCH-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - one way
857 | |/
857 | |/
858 | o h-1: b -(move)-> d
858 | o h-1: b -(move)-> d
859 | |
859 | |
860 o | c-1 delete d
860 o | c-1 delete d
861 | |
861 | |
862 o | i-2: c -move-> d, s -move-> t
862 o | i-2: c -move-> d, s -move-> t
863 | |
863 | |
864 o | i-1: a -move-> c, p -move-> s
864 o | i-1: a -move-> c, p -move-> s
865 |/
865 |/
866 o i-0 initial commit: a b h p q r
866 o i-0 initial commit: a b h p q r
867
867
868
868
869 Variant of previous with extra changes introduced by the merge
869 Variant of previous with extra changes introduced by the merge
870 --------------------------------------------------------------
870 --------------------------------------------------------------
871
871
872 Multiple cases above explicitely test cases where content are the same on both side during merge. In this section we will introduce variants for theses cases where new change are introduced to these file content during the merges.
872 Multiple cases above explicitely test cases where content are the same on both side during merge. In this section we will introduce variants for theses cases where new change are introduced to these file content during the merges.
873
873
874
874
875 Subcase: merge has same initial content on both side, but merge introduced a change
875 Subcase: merge has same initial content on both side, but merge introduced a change
876 ```````````````````````````````````````````````````````````````````````````````````
876 ```````````````````````````````````````````````````````````````````````````````````
877
877
878 Same as `mAEm` and `mEAm` but with extra change to the file before commiting
878 Same as `mAEm` and `mEAm` but with extra change to the file before commiting
879
879
880 - the "e-" branch renaming b to f (through 'g')
880 - the "e-" branch renaming b to f (through 'g')
881 - the "a-" branch renaming d to f (through e)
881 - the "a-" branch renaming d to f (through e)
882
882
883 $ case_desc="merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent)"
883 $ case_desc="merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent)"
884
884
885 $ hg up 'desc("a-2")'
885 $ hg up 'desc("a-2")'
886 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
886 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
887 $ hg merge 'desc("e-2")'
887 $ hg merge 'desc("e-2")'
888 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
888 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
889 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
889 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
890 (branch merge, don't forget to commit)
890 (branch merge, don't forget to commit)
891 $ echo "content change for mAE-change-m" > f
891 $ echo "content change for mAE-change-m" > f
892 $ hg ci -m "mAE-change-m-0 $case_desc - one way"
892 $ hg ci -m "mAE-change-m-0 $case_desc - one way"
893 created new head
893 created new head
894 $ hg up 'desc("e-2")'
894 $ hg up 'desc("e-2")'
895 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
895 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
896 $ hg merge 'desc("a-2")'
896 $ hg merge 'desc("a-2")'
897 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
897 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
898 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
898 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
899 (branch merge, don't forget to commit)
899 (branch merge, don't forget to commit)
900 $ echo "content change for mEA-change-m" > f
900 $ echo "content change for mEA-change-m" > f
901 $ hg ci -m "mEA-change-m-0 $case_desc - the other way"
901 $ hg ci -m "mEA-change-m-0 $case_desc - the other way"
902 created new head
902 created new head
903 $ hg log -G --rev '::(desc("mAE-change-m")+desc("mEA-change-m"))'
903 $ hg log -G --rev '::(desc("mAE-change-m")+desc("mEA-change-m"))'
904 @ mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
904 @ mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
905 |\
905 |\
906 +---o mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
906 +---o mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
907 | |/
907 | |/
908 | o e-2 g -move-> f
908 | o e-2 g -move-> f
909 | |
909 | |
910 | o e-1 b -move-> g
910 | o e-1 b -move-> g
911 | |
911 | |
912 o | a-2: e -move-> f
912 o | a-2: e -move-> f
913 | |
913 | |
914 o | a-1: d -move-> e
914 o | a-1: d -move-> e
915 |/
915 |/
916 o i-2: c -move-> d, s -move-> t
916 o i-2: c -move-> d, s -move-> t
917 |
917 |
918 o i-1: a -move-> c, p -move-> s
918 o i-1: a -move-> c, p -move-> s
919 |
919 |
920 o i-0 initial commit: a b h p q r
920 o i-0 initial commit: a b h p q r
921
921
922
922
923 Subcase: merge overwrite common copy information, but with extra change during the merge
923 Subcase: merge overwrite common copy information, but with extra change during the merge
924 ````````````````````````````````````````````````````````````````````````````````````````
924 ````````````````````````````````````````````````````````````````````````````````````````
925
925
926 Merge:
926 Merge:
927 - one with change to an unrelated file (b)
927 - one with change to an unrelated file (b)
928 - one overwriting a file (d) with a rename (from h to i to d)
928 - one overwriting a file (d) with a rename (from h to i to d)
929 - the merge update f content
929 - the merge update f content
930
930
931 $ case_desc="merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d)"
931 $ case_desc="merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d)"
932
932
933 $ hg up 'desc("f-2")'
933 $ hg up 'desc("f-2")'
934 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
934 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
935 #if no-changeset
935 #if no-changeset
936 $ hg debugindex d | "$PYTHON" ../no-linkrev
936 $ hg debugindex d | "$PYTHON" ../no-linkrev
937 rev linkrev nodeid p1 p2
937 rev linkrev nodeid p1 p2
938 0 * d8252ab2e760 000000000000 000000000000
938 0 * d8252ab2e760 000000000000 000000000000
939 1 * b004912a8510 000000000000 000000000000
939 1 * b004912a8510 000000000000 000000000000
940 2 * 7b79e2fe0c89 000000000000 000000000000
940 2 * 7b79e2fe0c89 000000000000 000000000000
941 3 * 17ec97e60577 d8252ab2e760 000000000000
941 3 * 17ec97e60577 d8252ab2e760 000000000000
942 4 * 06dabf50734c b004912a8510 17ec97e60577
942 4 * 06dabf50734c b004912a8510 17ec97e60577
943 5 * 19c0e3924691 17ec97e60577 b004912a8510
943 5 * 19c0e3924691 17ec97e60577 b004912a8510
944 6 * 89c873a01d97 7b79e2fe0c89 17ec97e60577
944 6 * 89c873a01d97 7b79e2fe0c89 17ec97e60577
945 7 * d55cb4e9ef57 000000000000 000000000000
945 7 * d55cb4e9ef57 000000000000 000000000000
946 #else
946 #else
947 $ hg debugindex d | "$PYTHON" ../no-linkrev
947 $ hg debugindex d | "$PYTHON" ../no-linkrev
948 rev linkrev nodeid p1 p2
948 rev linkrev nodeid p1 p2
949 0 * ae258f702dfe 000000000000 000000000000
949 0 * ae258f702dfe 000000000000 000000000000
950 1 * b004912a8510 000000000000 000000000000
950 1 * b004912a8510 000000000000 000000000000
951 2 * 5cce88bf349f ae258f702dfe 000000000000
951 2 * 5cce88bf349f ae258f702dfe 000000000000
952 3 * cc269dd788c8 b004912a8510 5cce88bf349f
952 3 * cc269dd788c8 b004912a8510 5cce88bf349f
953 4 * 51c91a115080 5cce88bf349f b004912a8510
953 4 * 51c91a115080 5cce88bf349f b004912a8510
954 #endif
954 #endif
955 $ hg up 'desc("b-1")'
955 $ hg up 'desc("b-1")'
956 3 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
956 3 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
957 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
957 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
958 $ hg merge 'desc("f-2")'
958 $ hg merge 'desc("f-2")'
959 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
959 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
960 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
960 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
961 (branch merge, don't forget to commit)
961 (branch merge, don't forget to commit)
962 $ echo "extra-change to (formelly h) during the merge" > d
962 $ echo "extra-change to (formelly h) during the merge" > d
963 $ hg ci -m "mBF-change-m-0 $case_desc - one way"
963 $ hg ci -m "mBF-change-m-0 $case_desc - one way"
964 created new head
964 created new head
965 $ hg manifest --rev . --debug | grep " d"
965 $ hg manifest --rev . --debug | grep " d"
966 1c334238bd42ec85c6a0d83fd1b2a898a6a3215d 644 d (no-changeset !)
966 1c334238bd42ec85c6a0d83fd1b2a898a6a3215d 644 d (no-changeset !)
967 cea2d99c0fde64672ef61953786fdff34f16e230 644 d (changeset !)
967 cea2d99c0fde64672ef61953786fdff34f16e230 644 d (changeset !)
968
968
969 $ hg up 'desc("f-2")'
969 $ hg up 'desc("f-2")'
970 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
970 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
971 $ hg merge 'desc("b-1")'
971 $ hg merge 'desc("b-1")'
972 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
972 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
973 (branch merge, don't forget to commit)
973 (branch merge, don't forget to commit)
974 $ echo "extra-change to (formelly h) during the merge" > d
974 $ echo "extra-change to (formelly h) during the merge" > d
975 $ hg ci -m "mFB-change-m-0 $case_desc - the other way"
975 $ hg ci -m "mFB-change-m-0 $case_desc - the other way"
976 created new head
976 created new head
977 $ hg manifest --rev . --debug | grep " d"
977 $ hg manifest --rev . --debug | grep " d"
978 1c334238bd42ec85c6a0d83fd1b2a898a6a3215d 644 d (no-changeset !)
978 1c334238bd42ec85c6a0d83fd1b2a898a6a3215d 644 d (no-changeset !)
979 cea2d99c0fde64672ef61953786fdff34f16e230 644 d (changeset !)
979 cea2d99c0fde64672ef61953786fdff34f16e230 644 d (changeset !)
980 #if no-changeset
980 #if no-changeset
981 $ hg debugindex d | "$PYTHON" ../no-linkrev
981 $ hg debugindex d | "$PYTHON" ../no-linkrev
982 rev linkrev nodeid p1 p2
982 rev linkrev nodeid p1 p2
983 0 * d8252ab2e760 000000000000 000000000000
983 0 * d8252ab2e760 000000000000 000000000000
984 1 * b004912a8510 000000000000 000000000000
984 1 * b004912a8510 000000000000 000000000000
985 2 * 7b79e2fe0c89 000000000000 000000000000
985 2 * 7b79e2fe0c89 000000000000 000000000000
986 3 * 17ec97e60577 d8252ab2e760 000000000000
986 3 * 17ec97e60577 d8252ab2e760 000000000000
987 4 * 06dabf50734c b004912a8510 17ec97e60577
987 4 * 06dabf50734c b004912a8510 17ec97e60577
988 5 * 19c0e3924691 17ec97e60577 b004912a8510
988 5 * 19c0e3924691 17ec97e60577 b004912a8510
989 6 * 89c873a01d97 7b79e2fe0c89 17ec97e60577
989 6 * 89c873a01d97 7b79e2fe0c89 17ec97e60577
990 7 * d55cb4e9ef57 000000000000 000000000000
990 7 * d55cb4e9ef57 000000000000 000000000000
991 8 * 1c334238bd42 7b79e2fe0c89 000000000000
991 8 * 1c334238bd42 7b79e2fe0c89 000000000000
992 #else
992 #else
993 $ hg debugindex d | "$PYTHON" ../no-linkrev
993 $ hg debugindex d | "$PYTHON" ../no-linkrev
994 rev linkrev nodeid p1 p2
994 rev linkrev nodeid p1 p2
995 0 * ae258f702dfe 000000000000 000000000000
995 0 * ae258f702dfe 000000000000 000000000000
996 1 * b004912a8510 000000000000 000000000000
996 1 * b004912a8510 000000000000 000000000000
997 2 * 5cce88bf349f ae258f702dfe 000000000000
997 2 * 5cce88bf349f ae258f702dfe 000000000000
998 3 * cc269dd788c8 b004912a8510 5cce88bf349f
998 3 * cc269dd788c8 b004912a8510 5cce88bf349f
999 4 * 51c91a115080 5cce88bf349f b004912a8510
999 4 * 51c91a115080 5cce88bf349f b004912a8510
1000 5 * cea2d99c0fde ae258f702dfe 000000000000
1000 5 * cea2d99c0fde ae258f702dfe 000000000000
1001 #endif
1001 #endif
1002 $ hg log -G --rev '::(desc("mBF-change-m")+desc("mFB-change-m"))'
1002 $ hg log -G --rev '::(desc("mBF-change-m")+desc("mFB-change-m"))'
1003 @ mFB-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
1003 @ mFB-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
1004 |\
1004 |\
1005 +---o mBF-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
1005 +---o mBF-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
1006 | |/
1006 | |/
1007 | o f-2: rename i -> d
1007 | o f-2: rename i -> d
1008 | |
1008 | |
1009 | o f-1: rename h -> i
1009 | o f-1: rename h -> i
1010 | |
1010 | |
1011 o | b-1: b update
1011 o | b-1: b update
1012 |/
1012 |/
1013 o i-2: c -move-> d, s -move-> t
1013 o i-2: c -move-> d, s -move-> t
1014 |
1014 |
1015 o i-1: a -move-> c, p -move-> s
1015 o i-1: a -move-> c, p -move-> s
1016 |
1016 |
1017 o i-0 initial commit: a b h p q r
1017 o i-0 initial commit: a b h p q r
1018
1018
1019
1019
1020 Subcase: restoring and untouched deleted file, while touching it
1020 Subcase: restoring and untouched deleted file, while touching it
1021 ````````````````````````````````````````````````````````````````
1021 ````````````````````````````````````````````````````````````````
1022
1022
1023 Merge:
1023 Merge:
1024 - one removing a file (d)
1024 - one removing a file (d)
1025 - one leaving the file untouched
1025 - one leaving the file untouched
1026 - the merge actively restore the file to the same content.
1026 - the merge actively restore the file to the same content.
1027
1027
1028 In this case, the file keep on living after the merge. So we should not drop its
1028 In this case, the file keep on living after the merge. So we should not drop its
1029 copy tracing chain.
1029 copy tracing chain.
1030
1030
1031 $ case_desc="merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge)"
1031 $ case_desc="merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge)"
1032
1032
1033 $ hg up 'desc("c-1")'
1033 $ hg up 'desc("c-1")'
1034 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1034 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1035 $ hg merge 'desc("b-1")'
1035 $ hg merge 'desc("b-1")'
1036 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1036 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1037 (branch merge, don't forget to commit)
1037 (branch merge, don't forget to commit)
1038 $ hg revert --rev 'desc("b-1")' d
1038 $ hg revert --rev 'desc("b-1")' d
1039 $ echo "new content for d after the revert" > d
1039 $ echo "new content for d after the revert" > d
1040 $ hg ci -m "mCB-change-m-0 $case_desc - one way"
1040 $ hg ci -m "mCB-change-m-0 $case_desc - one way"
1041 created new head
1041 created new head
1042 $ hg manifest --rev . --debug | grep " d"
1042 $ hg manifest --rev . --debug | grep " d"
1043 e333780c17752a3b0dd15e3ad48aa4e5c745f621 644 d (no-changeset !)
1043 e333780c17752a3b0dd15e3ad48aa4e5c745f621 644 d (no-changeset !)
1044 4b540a18ad699234b2b2aa18cb69555ac9c4b1df 644 d (changeset !)
1044 4b540a18ad699234b2b2aa18cb69555ac9c4b1df 644 d (changeset !)
1045
1045
1046 $ hg up 'desc("b-1")'
1046 $ hg up 'desc("b-1")'
1047 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1047 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1048 $ hg merge 'desc("c-1")'
1048 $ hg merge 'desc("c-1")'
1049 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1049 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1050 (branch merge, don't forget to commit)
1050 (branch merge, don't forget to commit)
1051 $ hg revert --rev 'desc("b-1")' d
1051 $ hg revert --rev 'desc("b-1")' d
1052 $ echo "new content for d after the revert" > d
1052 $ echo "new content for d after the revert" > d
1053 $ hg ci -m "mBC-change-m-0 $case_desc - the other way"
1053 $ hg ci -m "mBC-change-m-0 $case_desc - the other way"
1054 created new head
1054 created new head
1055 $ hg manifest --rev . --debug | grep " d"
1055 $ hg manifest --rev . --debug | grep " d"
1056 e333780c17752a3b0dd15e3ad48aa4e5c745f621 644 d (no-changeset !)
1056 e333780c17752a3b0dd15e3ad48aa4e5c745f621 644 d (no-changeset !)
1057 4b540a18ad699234b2b2aa18cb69555ac9c4b1df 644 d (changeset !)
1057 4b540a18ad699234b2b2aa18cb69555ac9c4b1df 644 d (changeset !)
1058
1058
1059
1059
1060 $ hg up null --quiet
1060 $ hg up null --quiet
1061 $ hg log -G --rev '::(desc("mCB-change-m")+desc("mBC-change-m"))'
1061 $ hg log -G --rev '::(desc("mCB-change-m")+desc("mBC-change-m"))'
1062 o mBC-change-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
1062 o mBC-change-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
1063 |\
1063 |\
1064 +---o mCB-change-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
1064 +---o mCB-change-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
1065 | |/
1065 | |/
1066 | o c-1 delete d
1066 | o c-1 delete d
1067 | |
1067 | |
1068 o | b-1: b update
1068 o | b-1: b update
1069 |/
1069 |/
1070 o i-2: c -move-> d, s -move-> t
1070 o i-2: c -move-> d, s -move-> t
1071 |
1071 |
1072 o i-1: a -move-> c, p -move-> s
1072 o i-1: a -move-> c, p -move-> s
1073 |
1073 |
1074 o i-0 initial commit: a b h p q r
1074 o i-0 initial commit: a b h p q r
1075
1075
1076
1076
1077 Decision from previous merge are properly chained with later merge
1077 Decision from previous merge are properly chained with later merge
1078 ------------------------------------------------------------------
1078 ------------------------------------------------------------------
1079
1079
1080 Subcase: chaining conflicting rename resolution
1080 Subcase: chaining conflicting rename resolution
1081 ```````````````````````````````````````````````
1081 ```````````````````````````````````````````````
1082
1082
1083 The "mAEm" and "mEAm" case create a rename tracking conflict on file 'f'. We
1083 The "mAEm" and "mEAm" case create a rename tracking conflict on file 'f'. We
1084 add more change on the respective branch and merge again. These second merge
1084 add more change on the respective branch and merge again. These second merge
1085 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
1085 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
1086 about that file should stay unchanged.
1086 about that file should stay unchanged.
1087
1087
1088 We also touch J during some of the merge to check for unrelated change to new file during merge.
1088 We also touch J during some of the merge to check for unrelated change to new file during merge.
1089
1089
1090 $ case_desc="chained merges (conflict -> simple) - same content everywhere"
1090 $ case_desc="chained merges (conflict -> simple) - same content everywhere"
1091
1091
1092 (extra unrelated changes)
1092 (extra unrelated changes)
1093
1093
1094 $ hg up 'desc("a-2")'
1094 $ hg up 'desc("a-2")'
1095 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
1095 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
1096 $ echo j > unrelated-j
1096 $ echo j > unrelated-j
1097 $ hg add unrelated-j
1097 $ hg add unrelated-j
1098 $ hg ci -m 'j-1: unrelated changes (based on the "a" series of changes)'
1098 $ hg ci -m 'j-1: unrelated changes (based on the "a" series of changes)'
1099 created new head
1099 created new head
1100
1100
1101 $ hg up 'desc("e-2")'
1101 $ hg up 'desc("e-2")'
1102 2 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
1102 2 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
1103 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
1103 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
1104 $ echo k > unrelated-k
1104 $ echo k > unrelated-k
1105 $ hg add unrelated-k
1105 $ hg add unrelated-k
1106 $ hg ci -m 'k-1: unrelated changes (based on "e" changes)'
1106 $ hg ci -m 'k-1: unrelated changes (based on "e" changes)'
1107 created new head
1107 created new head
1108
1108
1109 (merge variant 1)
1109 (merge variant 1)
1110
1110
1111 $ hg up 'desc("mAEm")'
1111 $ hg up 'desc("mAEm")'
1112 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
1112 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
1113 0 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
1113 0 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
1114 $ hg merge 'desc("k-1")'
1114 $ hg merge 'desc("k-1")'
1115 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1115 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1116 (branch merge, don't forget to commit)
1116 (branch merge, don't forget to commit)
1117 $ hg ci -m "mAE,Km: $case_desc"
1117 $ hg ci -m "mAE,Km: $case_desc"
1118
1118
1119 (merge variant 2)
1119 (merge variant 2)
1120
1120
1121 $ hg up 'desc("k-1")'
1121 $ hg up 'desc("k-1")'
1122 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
1122 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
1123 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
1123 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
1124
1124
1125 $ hg merge 'desc("mAEm")'
1125 $ hg merge 'desc("mAEm")'
1126 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
1126 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
1127 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
1127 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
1128 (branch merge, don't forget to commit)
1128 (branch merge, don't forget to commit)
1129 $ hg ci -m "mK,AEm: $case_desc"
1129 $ hg ci -m "mK,AEm: $case_desc"
1130 created new head
1130 created new head
1131
1131
1132 (merge variant 3)
1132 (merge variant 3)
1133
1133
1134 $ hg up 'desc("mEAm")'
1134 $ hg up 'desc("mEAm")'
1135 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1135 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1136 $ hg merge 'desc("j-1")'
1136 $ hg merge 'desc("j-1")'
1137 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1137 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1138 (branch merge, don't forget to commit)
1138 (branch merge, don't forget to commit)
1139 $ echo jj > unrelated-j
1139 $ echo jj > unrelated-j
1140 $ hg ci -m "mEA,Jm: $case_desc"
1140 $ hg ci -m "mEA,Jm: $case_desc"
1141
1141
1142 (merge variant 4)
1142 (merge variant 4)
1143
1143
1144 $ hg up 'desc("j-1")'
1144 $ hg up 'desc("j-1")'
1145 3 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
1145 3 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
1146 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
1146 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
1147 $ hg merge 'desc("mEAm")'
1147 $ hg merge 'desc("mEAm")'
1148 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
1148 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
1149 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
1149 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
1150 (branch merge, don't forget to commit)
1150 (branch merge, don't forget to commit)
1151 $ echo jj > unrelated-j
1151 $ echo jj > unrelated-j
1152 $ hg ci -m "mJ,EAm: $case_desc"
1152 $ hg ci -m "mJ,EAm: $case_desc"
1153 created new head
1153 created new head
1154
1154
1155
1155
1156 $ hg log -G --rev '::(desc("mAE,Km") + desc("mK,AEm") + desc("mEA,Jm") + desc("mJ,EAm"))'
1156 $ hg log -G --rev '::(desc("mAE,Km") + desc("mK,AEm") + desc("mEA,Jm") + desc("mJ,EAm"))'
1157 @ mJ,EAm: chained merges (conflict -> simple) - same content everywhere
1157 @ mJ,EAm: chained merges (conflict -> simple) - same content everywhere
1158 |\
1158 |\
1159 +---o mEA,Jm: chained merges (conflict -> simple) - same content everywhere
1159 +---o mEA,Jm: chained merges (conflict -> simple) - same content everywhere
1160 | |/
1160 | |/
1161 | | o mK,AEm: chained merges (conflict -> simple) - same content everywhere
1161 | | o mK,AEm: chained merges (conflict -> simple) - same content everywhere
1162 | | |\
1162 | | |\
1163 | | +---o mAE,Km: chained merges (conflict -> simple) - same content everywhere
1163 | | +---o mAE,Km: chained merges (conflict -> simple) - same content everywhere
1164 | | | |/
1164 | | | |/
1165 | | | o k-1: unrelated changes (based on "e" changes)
1165 | | | o k-1: unrelated changes (based on "e" changes)
1166 | | | |
1166 | | | |
1167 | o | | j-1: unrelated changes (based on the "a" series of changes)
1167 | o | | j-1: unrelated changes (based on the "a" series of changes)
1168 | | | |
1168 | | | |
1169 o-----+ mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
1169 o-----+ mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
1170 |/ / /
1170 |/ / /
1171 | o / mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
1171 | o / mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
1172 |/|/
1172 |/|/
1173 | o e-2 g -move-> f
1173 | o e-2 g -move-> f
1174 | |
1174 | |
1175 | o e-1 b -move-> g
1175 | o e-1 b -move-> g
1176 | |
1176 | |
1177 o | a-2: e -move-> f
1177 o | a-2: e -move-> f
1178 | |
1178 | |
1179 o | a-1: d -move-> e
1179 o | a-1: d -move-> e
1180 |/
1180 |/
1181 o i-2: c -move-> d, s -move-> t
1181 o i-2: c -move-> d, s -move-> t
1182 |
1182 |
1183 o i-1: a -move-> c, p -move-> s
1183 o i-1: a -move-> c, p -move-> s
1184 |
1184 |
1185 o i-0 initial commit: a b h p q r
1185 o i-0 initial commit: a b h p q r
1186
1186
1187
1187
1188 Subcase: chaining conflicting rename resolution, with actual merging happening
1188 Subcase: chaining conflicting rename resolution, with actual merging happening
1189 ``````````````````````````````````````````````````````````````````````````````
1189 ``````````````````````````````````````````````````````````````````````````````
1190
1190
1191 The "mPQm" and "mQPm" case create a rename tracking conflict on file 't'. We
1191 The "mPQm" and "mQPm" case create a rename tracking conflict on file 't'. We
1192 add more change on the respective branch and merge again. These second merge
1192 add more change on the respective branch and merge again. These second merge
1193 does not involve the file 't' and the arbitration done within "mPQm" and "mQP"
1193 does not involve the file 't' and the arbitration done within "mPQm" and "mQP"
1194 about that file should stay unchanged.
1194 about that file should stay unchanged.
1195
1195
1196 $ case_desc="chained merges (conflict -> simple) - different content"
1196 $ case_desc="chained merges (conflict -> simple) - different content"
1197
1197
1198 (extra unrelated changes)
1198 (extra unrelated changes)
1199
1199
1200 $ hg up 'desc("p-2")'
1200 $ hg up 'desc("p-2")'
1201 3 files updated, 0 files merged, 3 files removed, 0 files unresolved
1201 3 files updated, 0 files merged, 3 files removed, 0 files unresolved
1202 $ echo s > unrelated-s
1202 $ echo s > unrelated-s
1203 $ hg add unrelated-s
1203 $ hg add unrelated-s
1204 $ hg ci -m 's-1: unrelated changes (based on the "p" series of changes)'
1204 $ hg ci -m 's-1: unrelated changes (based on the "p" series of changes)'
1205 created new head
1205 created new head
1206
1206
1207 $ hg up 'desc("q-2")'
1207 $ hg up 'desc("q-2")'
1208 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
1208 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
1209 $ echo t > unrelated-t
1209 $ echo t > unrelated-t
1210 $ hg add unrelated-t
1210 $ hg add unrelated-t
1211 $ hg ci -m 't-1: unrelated changes (based on "q" changes)'
1211 $ hg ci -m 't-1: unrelated changes (based on "q" changes)'
1212 created new head
1212 created new head
1213
1213
1214 (merge variant 1)
1214 (merge variant 1)
1215
1215
1216 $ hg up 'desc("mPQm")'
1216 $ hg up 'desc("mPQm")'
1217 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
1217 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
1218 $ hg merge 'desc("t-1")'
1218 $ hg merge 'desc("t-1")'
1219 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1219 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1220 (branch merge, don't forget to commit)
1220 (branch merge, don't forget to commit)
1221 $ hg ci -m "mPQ,Tm: $case_desc"
1221 $ hg ci -m "mPQ,Tm: $case_desc"
1222
1222
1223 (merge variant 2)
1223 (merge variant 2)
1224
1224
1225 $ hg up 'desc("t-1")'
1225 $ hg up 'desc("t-1")'
1226 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1226 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1227
1227
1228 $ hg merge 'desc("mPQm")'
1228 $ hg merge 'desc("mPQm")'
1229 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1229 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1230 (branch merge, don't forget to commit)
1230 (branch merge, don't forget to commit)
1231 $ hg ci -m "mT,PQm: $case_desc"
1231 $ hg ci -m "mT,PQm: $case_desc"
1232 created new head
1232 created new head
1233
1233
1234 (merge variant 3)
1234 (merge variant 3)
1235
1235
1236 $ hg up 'desc("mQPm")'
1236 $ hg up 'desc("mQPm")'
1237 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1237 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1238 $ hg merge 'desc("s-1")'
1238 $ hg merge 'desc("s-1")'
1239 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1239 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1240 (branch merge, don't forget to commit)
1240 (branch merge, don't forget to commit)
1241 $ hg ci -m "mQP,Sm: $case_desc"
1241 $ hg ci -m "mQP,Sm: $case_desc"
1242
1242
1243 (merge variant 4)
1243 (merge variant 4)
1244
1244
1245 $ hg up 'desc("s-1")'
1245 $ hg up 'desc("s-1")'
1246 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1246 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1247 $ hg merge 'desc("mQPm")'
1247 $ hg merge 'desc("mQPm")'
1248 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1248 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1249 (branch merge, don't forget to commit)
1249 (branch merge, don't forget to commit)
1250 $ hg ci -m "mS,QPm: $case_desc"
1250 $ hg ci -m "mS,QPm: $case_desc"
1251 created new head
1251 created new head
1252 $ hg up null --quiet
1252 $ hg up null --quiet
1253
1253
1254
1254
1255 $ hg log -G --rev '::(desc("mPQ,Tm") + desc("mT,PQm") + desc("mQP,Sm") + desc("mS,QPm"))'
1255 $ hg log -G --rev '::(desc("mPQ,Tm") + desc("mT,PQm") + desc("mQP,Sm") + desc("mS,QPm"))'
1256 o mS,QPm: chained merges (conflict -> simple) - different content
1256 o mS,QPm: chained merges (conflict -> simple) - different content
1257 |\
1257 |\
1258 +---o mQP,Sm: chained merges (conflict -> simple) - different content
1258 +---o mQP,Sm: chained merges (conflict -> simple) - different content
1259 | |/
1259 | |/
1260 | | o mT,PQm: chained merges (conflict -> simple) - different content
1260 | | o mT,PQm: chained merges (conflict -> simple) - different content
1261 | | |\
1261 | | |\
1262 | | +---o mPQ,Tm: chained merges (conflict -> simple) - different content
1262 | | +---o mPQ,Tm: chained merges (conflict -> simple) - different content
1263 | | | |/
1263 | | | |/
1264 | | | o t-1: unrelated changes (based on "q" changes)
1264 | | | o t-1: unrelated changes (based on "q" changes)
1265 | | | |
1265 | | | |
1266 | o | | s-1: unrelated changes (based on the "p" series of changes)
1266 | o | | s-1: unrelated changes (based on the "p" series of changes)
1267 | | | |
1267 | | | |
1268 o-----+ mQPm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - the other way
1268 o-----+ mQPm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - the other way
1269 |/ / /
1269 |/ / /
1270 | o / mPQm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - one way
1270 | o / mPQm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - one way
1271 |/|/
1271 |/|/
1272 | o q-2 w -move-> v
1272 | o q-2 w -move-> v
1273 | |
1273 | |
1274 | o q-1 r -move-> w
1274 | o q-1 r -move-> w
1275 | |
1275 | |
1276 o | p-2: u -move-> v
1276 o | p-2: u -move-> v
1277 | |
1277 | |
1278 o | p-1: t -move-> u
1278 o | p-1: t -move-> u
1279 |/
1279 |/
1280 o i-2: c -move-> d, s -move-> t
1280 o i-2: c -move-> d, s -move-> t
1281 |
1281 |
1282 o i-1: a -move-> c, p -move-> s
1282 o i-1: a -move-> c, p -move-> s
1283 |
1283 |
1284 o i-0 initial commit: a b h p q r
1284 o i-0 initial commit: a b h p q r
1285
1285
1286
1286
1287 Subcase: chaining salvage information during a merge
1287 Subcase: chaining salvage information during a merge
1288 ````````````````````````````````````````````````````
1288 ````````````````````````````````````````````````````
1289
1289
1290 We add more change on the branch were the file was deleted. merging again
1290 We add more change on the branch were the file was deleted. merging again
1291 should preserve the fact eh file was salvaged.
1291 should preserve the fact eh file was salvaged.
1292
1292
1293 $ case_desc="chained merges (salvaged -> simple) - same content (when the file exists)"
1293 $ case_desc="chained merges (salvaged -> simple) - same content (when the file exists)"
1294
1294
1295 (creating the change)
1295 (creating the change)
1296
1296
1297 $ hg up 'desc("c-1")'
1297 $ hg up 'desc("c-1")'
1298 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1298 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1299 $ echo l > unrelated-l
1299 $ echo l > unrelated-l
1300 $ hg add unrelated-l
1300 $ hg add unrelated-l
1301 $ hg ci -m 'l-1: unrelated changes (based on "c" changes)'
1301 $ hg ci -m 'l-1: unrelated changes (based on "c" changes)'
1302 created new head
1302 created new head
1303
1303
1304 (Merge variant 1)
1304 (Merge variant 1)
1305
1305
1306 $ hg up 'desc("mBC-revert-m")'
1306 $ hg up 'desc("mBC-revert-m")'
1307 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1307 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1308 $ hg merge 'desc("l-1")'
1308 $ hg merge 'desc("l-1")'
1309 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1309 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1310 (branch merge, don't forget to commit)
1310 (branch merge, don't forget to commit)
1311 $ hg ci -m "mBC+revert,Lm: $case_desc"
1311 $ hg ci -m "mBC+revert,Lm: $case_desc"
1312
1312
1313 (Merge variant 2)
1313 (Merge variant 2)
1314
1314
1315 $ hg up 'desc("mCB-revert-m")'
1315 $ hg up 'desc("mCB-revert-m")'
1316 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1316 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1317 $ hg merge 'desc("l-1")'
1317 $ hg merge 'desc("l-1")'
1318 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1318 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1319 (branch merge, don't forget to commit)
1319 (branch merge, don't forget to commit)
1320 $ hg ci -m "mCB+revert,Lm: $case_desc"
1320 $ hg ci -m "mCB+revert,Lm: $case_desc"
1321
1321
1322 (Merge variant 3)
1322 (Merge variant 3)
1323
1323
1324 $ hg up 'desc("l-1")'
1324 $ hg up 'desc("l-1")'
1325 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1325 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1326
1326
1327 $ hg merge 'desc("mBC-revert-m")'
1327 $ hg merge 'desc("mBC-revert-m")'
1328 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1328 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1329 (branch merge, don't forget to commit)
1329 (branch merge, don't forget to commit)
1330 $ hg ci -m "mL,BC+revertm: $case_desc"
1330 $ hg ci -m "mL,BC+revertm: $case_desc"
1331 created new head
1331 created new head
1332
1332
1333 (Merge variant 4)
1333 (Merge variant 4)
1334
1334
1335 $ hg up 'desc("l-1")'
1335 $ hg up 'desc("l-1")'
1336 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1336 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1337
1337
1338 $ hg merge 'desc("mCB-revert-m")'
1338 $ hg merge 'desc("mCB-revert-m")'
1339 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1339 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1340 (branch merge, don't forget to commit)
1340 (branch merge, don't forget to commit)
1341 $ hg ci -m "mL,CB+revertm: $case_desc"
1341 $ hg ci -m "mL,CB+revertm: $case_desc"
1342 created new head
1342 created new head
1343
1343
1344 $ hg log -G --rev '::(desc("mBC+revert,Lm") + desc("mCB+revert,Lm") + desc("mL,BC+revertm") + desc("mL,CB+revertm"))'
1344 $ hg log -G --rev '::(desc("mBC+revert,Lm") + desc("mCB+revert,Lm") + desc("mL,BC+revertm") + desc("mL,CB+revertm"))'
1345 @ mL,CB+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1345 @ mL,CB+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1346 |\
1346 |\
1347 | | o mL,BC+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1347 | | o mL,BC+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1348 | |/|
1348 | |/|
1349 +-+---o mCB+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1349 +-+---o mCB+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1350 | | |
1350 | | |
1351 | +---o mBC+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1351 | +---o mBC+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1352 | | |/
1352 | | |/
1353 | o | l-1: unrelated changes (based on "c" changes)
1353 | o | l-1: unrelated changes (based on "c" changes)
1354 | | |
1354 | | |
1355 | | o mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
1355 | | o mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
1356 | |/|
1356 | |/|
1357 o---+ mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
1357 o---+ mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
1358 |/ /
1358 |/ /
1359 o | c-1 delete d
1359 o | c-1 delete d
1360 | |
1360 | |
1361 | o b-1: b update
1361 | o b-1: b update
1362 |/
1362 |/
1363 o i-2: c -move-> d, s -move-> t
1363 o i-2: c -move-> d, s -move-> t
1364 |
1364 |
1365 o i-1: a -move-> c, p -move-> s
1365 o i-1: a -move-> c, p -move-> s
1366 |
1366 |
1367 o i-0 initial commit: a b h p q r
1367 o i-0 initial commit: a b h p q r
1368
1368
1369
1369
1370
1370
1371 Subcase: chaining "merged" information during a merge
1371 Subcase: chaining "merged" information during a merge
1372 ``````````````````````````````````````````````````````
1372 ``````````````````````````````````````````````````````
1373
1373
1374 When a non-rename change are merged with a copy overwrite, the merge pick the copy source from (p1) as the reference. We should preserve this information in subsequent merges.
1374 When a non-rename change are merged with a copy overwrite, the merge pick the copy source from (p1) as the reference. We should preserve this information in subsequent merges.
1375
1375
1376 $ case_desc="chained merges (copy-overwrite -> simple) - same content"
1376 $ case_desc="chained merges (copy-overwrite -> simple) - same content"
1377
1377
1378 (extra unrelated changes)
1378 (extra unrelated changes)
1379
1379
1380 $ hg up 'desc("f-2")'
1380 $ hg up 'desc("f-2")'
1381 2 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
1381 2 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
1382 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
1382 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
1383 $ echo n > unrelated-n
1383 $ echo n > unrelated-n
1384 $ hg add unrelated-n
1384 $ hg add unrelated-n
1385 $ hg ci -m 'n-1: unrelated changes (based on the "f" series of changes)'
1385 $ hg ci -m 'n-1: unrelated changes (based on the "f" series of changes)'
1386 created new head
1386 created new head
1387
1387
1388 $ hg up 'desc("g-1")'
1388 $ hg up 'desc("g-1")'
1389 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1389 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1390 $ echo o > unrelated-o
1390 $ echo o > unrelated-o
1391 $ hg add unrelated-o
1391 $ hg add unrelated-o
1392 $ hg ci -m 'o-1: unrelated changes (based on "g" changes)'
1392 $ hg ci -m 'o-1: unrelated changes (based on "g" changes)'
1393 created new head
1393 created new head
1394
1394
1395 (merge variant 1)
1395 (merge variant 1)
1396
1396
1397 $ hg up 'desc("mFGm")'
1397 $ hg up 'desc("mFGm")'
1398 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
1398 1 files updated, 0 files merged, 2 files removed, 0 files unresolved (no-changeset !)
1399 0 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
1399 0 files updated, 0 files merged, 2 files removed, 0 files unresolved (changeset !)
1400 $ hg merge 'desc("o-1")'
1400 $ hg merge 'desc("o-1")'
1401 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1401 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1402 (branch merge, don't forget to commit)
1402 (branch merge, don't forget to commit)
1403 $ hg ci -m "mFG,Om: $case_desc"
1403 $ hg ci -m "mFG,Om: $case_desc"
1404
1404
1405 (merge variant 2)
1405 (merge variant 2)
1406
1406
1407 $ hg up 'desc("o-1")'
1407 $ hg up 'desc("o-1")'
1408 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
1408 2 files updated, 0 files merged, 0 files removed, 0 files unresolved (no-changeset !)
1409 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
1409 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (changeset !)
1410 $ hg merge 'desc("FGm")'
1410 $ hg merge 'desc("FGm")'
1411 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
1411 1 files updated, 0 files merged, 1 files removed, 0 files unresolved (no-changeset !)
1412 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
1412 0 files updated, 0 files merged, 1 files removed, 0 files unresolved (changeset !)
1413 (branch merge, don't forget to commit)
1413 (branch merge, don't forget to commit)
1414 $ hg ci -m "mO,FGm: $case_desc"
1414 $ hg ci -m "mO,FGm: $case_desc"
1415 created new head
1415 created new head
1416
1416
1417 (merge variant 3)
1417 (merge variant 3)
1418
1418
1419 $ hg up 'desc("mGFm")'
1419 $ hg up 'desc("mGFm")'
1420 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1420 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1421 $ hg merge 'desc("n-1")'
1421 $ hg merge 'desc("n-1")'
1422 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1422 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1423 (branch merge, don't forget to commit)
1423 (branch merge, don't forget to commit)
1424 $ hg ci -m "mGF,Nm: $case_desc"
1424 $ hg ci -m "mGF,Nm: $case_desc"
1425
1425
1426 (merge variant 4)
1426 (merge variant 4)
1427
1427
1428 $ hg up 'desc("n-1")'
1428 $ hg up 'desc("n-1")'
1429 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1429 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1430 $ hg merge 'desc("mGFm")'
1430 $ hg merge 'desc("mGFm")'
1431 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1431 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1432 (branch merge, don't forget to commit)
1432 (branch merge, don't forget to commit)
1433 $ hg ci -m "mN,GFm: $case_desc"
1433 $ hg ci -m "mN,GFm: $case_desc"
1434 created new head
1434 created new head
1435
1435
1436 $ hg log -G --rev '::(desc("mFG,Om") + desc("mO,FGm") + desc("mGF,Nm") + desc("mN,GFm"))'
1436 $ hg log -G --rev '::(desc("mFG,Om") + desc("mO,FGm") + desc("mGF,Nm") + desc("mN,GFm"))'
1437 @ mN,GFm: chained merges (copy-overwrite -> simple) - same content
1437 @ mN,GFm: chained merges (copy-overwrite -> simple) - same content
1438 |\
1438 |\
1439 +---o mGF,Nm: chained merges (copy-overwrite -> simple) - same content
1439 +---o mGF,Nm: chained merges (copy-overwrite -> simple) - same content
1440 | |/
1440 | |/
1441 | | o mO,FGm: chained merges (copy-overwrite -> simple) - same content
1441 | | o mO,FGm: chained merges (copy-overwrite -> simple) - same content
1442 | | |\
1442 | | |\
1443 | | +---o mFG,Om: chained merges (copy-overwrite -> simple) - same content
1443 | | +---o mFG,Om: chained merges (copy-overwrite -> simple) - same content
1444 | | | |/
1444 | | | |/
1445 | | | o o-1: unrelated changes (based on "g" changes)
1445 | | | o o-1: unrelated changes (based on "g" changes)
1446 | | | |
1446 | | | |
1447 | o | | n-1: unrelated changes (based on the "f" series of changes)
1447 | o | | n-1: unrelated changes (based on the "f" series of changes)
1448 | | | |
1448 | | | |
1449 o-----+ mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
1449 o-----+ mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
1450 |/ / /
1450 |/ / /
1451 | o / mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
1451 | o / mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
1452 |/|/
1452 |/|/
1453 | o g-1: update d
1453 | o g-1: update d
1454 | |
1454 | |
1455 o | f-2: rename i -> d
1455 o | f-2: rename i -> d
1456 | |
1456 | |
1457 o | f-1: rename h -> i
1457 o | f-1: rename h -> i
1458 |/
1458 |/
1459 o i-2: c -move-> d, s -move-> t
1459 o i-2: c -move-> d, s -move-> t
1460 |
1460 |
1461 o i-1: a -move-> c, p -move-> s
1461 o i-1: a -move-> c, p -move-> s
1462 |
1462 |
1463 o i-0 initial commit: a b h p q r
1463 o i-0 initial commit: a b h p q r
1464
1464
1465
1465
1466 Subcase: chaining conflicting rename resolution, with extra change during the merge
1466 Subcase: chaining conflicting rename resolution, with extra change during the merge
1467 ```````````````````````````````````````````````````````````````````````````````````
1467 ```````````````````````````````````````````````````````````````````````````````````
1468
1468
1469 The "mEA-change-m-0" and "mAE-change-m-0" case create a rename tracking conflict on file 'f'. We
1469 The "mEA-change-m-0" and "mAE-change-m-0" case create a rename tracking conflict on file 'f'. We
1470 add more change on the respective branch and merge again. These second merge
1470 add more change on the respective branch and merge again. These second merge
1471 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
1471 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
1472 about that file should stay unchanged.
1472 about that file should stay unchanged.
1473
1473
1474 $ case_desc="chained merges (conflict+change -> simple) - same content on both branch in the initial merge"
1474 $ case_desc="chained merges (conflict+change -> simple) - same content on both branch in the initial merge"
1475
1475
1476
1476
1477 (merge variant 1)
1477 (merge variant 1)
1478
1478
1479 $ hg up 'desc("mAE-change-m")'
1479 $ hg up 'desc("mAE-change-m")'
1480 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
1480 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
1481 $ hg merge 'desc("k-1")'
1481 $ hg merge 'desc("k-1")'
1482 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1482 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1483 (branch merge, don't forget to commit)
1483 (branch merge, don't forget to commit)
1484 $ hg ci -m "mAE-change,Km: $case_desc"
1484 $ hg ci -m "mAE-change,Km: $case_desc"
1485
1485
1486 (merge variant 2)
1486 (merge variant 2)
1487
1487
1488 $ hg up 'desc("k-1")'
1488 $ hg up 'desc("k-1")'
1489 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1489 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1490
1490
1491 $ hg merge 'desc("mAE-change-m")'
1491 $ hg merge 'desc("mAE-change-m")'
1492 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1492 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1493 (branch merge, don't forget to commit)
1493 (branch merge, don't forget to commit)
1494 $ hg ci -m "mK,AE-change-m: $case_desc"
1494 $ hg ci -m "mK,AE-change-m: $case_desc"
1495 created new head
1495 created new head
1496
1496
1497 (merge variant 3)
1497 (merge variant 3)
1498
1498
1499 $ hg up 'desc("mEA-change-m")'
1499 $ hg up 'desc("mEA-change-m")'
1500 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1500 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1501 $ hg merge 'desc("j-1")'
1501 $ hg merge 'desc("j-1")'
1502 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1502 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1503 (branch merge, don't forget to commit)
1503 (branch merge, don't forget to commit)
1504 $ hg ci -m "mEA-change,Jm: $case_desc"
1504 $ hg ci -m "mEA-change,Jm: $case_desc"
1505
1505
1506 (merge variant 4)
1506 (merge variant 4)
1507
1507
1508 $ hg up 'desc("j-1")'
1508 $ hg up 'desc("j-1")'
1509 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1509 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1510 $ hg merge 'desc("mEA-change-m")'
1510 $ hg merge 'desc("mEA-change-m")'
1511 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1511 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1512 (branch merge, don't forget to commit)
1512 (branch merge, don't forget to commit)
1513 $ hg ci -m "mJ,EA-change-m: $case_desc"
1513 $ hg ci -m "mJ,EA-change-m: $case_desc"
1514 created new head
1514 created new head
1515
1515
1516
1516
1517 $ hg log -G --rev '::(desc("mAE-change,Km") + desc("mK,AE-change-m") + desc("mEA-change,Jm") + desc("mJ,EA-change-m"))'
1517 $ hg log -G --rev '::(desc("mAE-change,Km") + desc("mK,AE-change-m") + desc("mEA-change,Jm") + desc("mJ,EA-change-m"))'
1518 @ mJ,EA-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1518 @ mJ,EA-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1519 |\
1519 |\
1520 +---o mEA-change,Jm: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1520 +---o mEA-change,Jm: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1521 | |/
1521 | |/
1522 | | o mK,AE-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1522 | | o mK,AE-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1523 | | |\
1523 | | |\
1524 | | +---o mAE-change,Km: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1524 | | +---o mAE-change,Km: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1525 | | | |/
1525 | | | |/
1526 | | | o k-1: unrelated changes (based on "e" changes)
1526 | | | o k-1: unrelated changes (based on "e" changes)
1527 | | | |
1527 | | | |
1528 | o | | j-1: unrelated changes (based on the "a" series of changes)
1528 | o | | j-1: unrelated changes (based on the "a" series of changes)
1529 | | | |
1529 | | | |
1530 o-----+ mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
1530 o-----+ mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
1531 |/ / /
1531 |/ / /
1532 | o / mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
1532 | o / mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
1533 |/|/
1533 |/|/
1534 | o e-2 g -move-> f
1534 | o e-2 g -move-> f
1535 | |
1535 | |
1536 | o e-1 b -move-> g
1536 | o e-1 b -move-> g
1537 | |
1537 | |
1538 o | a-2: e -move-> f
1538 o | a-2: e -move-> f
1539 | |
1539 | |
1540 o | a-1: d -move-> e
1540 o | a-1: d -move-> e
1541 |/
1541 |/
1542 o i-2: c -move-> d, s -move-> t
1542 o i-2: c -move-> d, s -move-> t
1543 |
1543 |
1544 o i-1: a -move-> c, p -move-> s
1544 o i-1: a -move-> c, p -move-> s
1545 |
1545 |
1546 o i-0 initial commit: a b h p q r
1546 o i-0 initial commit: a b h p q r
1547
1547
1548
1548
1549 Summary of all created cases
1549 Summary of all created cases
1550 ----------------------------
1550 ----------------------------
1551
1551
1552 $ hg up --quiet null
1552 $ hg up --quiet null
1553
1553
1554 (This exists to help keeping a compact list of the various cases we have built)
1554 (This exists to help keeping a compact list of the various cases we have built)
1555
1555
1556 $ hg log -T '{desc|firstline}\n'| sort
1556 $ hg log -T '{desc|firstline}\n'| sort
1557 a-1: d -move-> e
1557 a-1: d -move-> e
1558 a-2: e -move-> f
1558 a-2: e -move-> f
1559 b-1: b update
1559 b-1: b update
1560 c-1 delete d
1560 c-1 delete d
1561 d-1 delete d
1561 d-1 delete d
1562 d-2 re-add d
1562 d-2 re-add d
1563 e-1 b -move-> g
1563 e-1 b -move-> g
1564 e-2 g -move-> f
1564 e-2 g -move-> f
1565 f-1: rename h -> i
1565 f-1: rename h -> i
1566 f-2: rename i -> d
1566 f-2: rename i -> d
1567 g-1: update d
1567 g-1: update d
1568 h-1: b -(move)-> d
1568 h-1: b -(move)-> d
1569 i-0 initial commit: a b h p q r
1569 i-0 initial commit: a b h p q r
1570 i-1: a -move-> c, p -move-> s
1570 i-1: a -move-> c, p -move-> s
1571 i-2: c -move-> d, s -move-> t
1571 i-2: c -move-> d, s -move-> t
1572 j-1: unrelated changes (based on the "a" series of changes)
1572 j-1: unrelated changes (based on the "a" series of changes)
1573 k-1: unrelated changes (based on "e" changes)
1573 k-1: unrelated changes (based on "e" changes)
1574 l-1: unrelated changes (based on "c" changes)
1574 l-1: unrelated changes (based on "c" changes)
1575 mABm-0 simple merge - A side: multiple renames, B side: unrelated update - the other way
1575 mABm-0 simple merge - A side: multiple renames, B side: unrelated update - the other way
1576 mAE,Km: chained merges (conflict -> simple) - same content everywhere
1576 mAE,Km: chained merges (conflict -> simple) - same content everywhere
1577 mAE-change,Km: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1577 mAE-change,Km: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1578 mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
1578 mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
1579 mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
1579 mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
1580 mBAm-0 simple merge - A side: multiple renames, B side: unrelated update - one way
1580 mBAm-0 simple merge - A side: multiple renames, B side: unrelated update - one way
1581 mBC+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1581 mBC+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1582 mBC-change-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
1582 mBC-change-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
1583 mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
1583 mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
1584 mBCm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - one way
1584 mBCm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - one way
1585 mBCm-1 re-add d
1585 mBCm-1 re-add d
1586 mBDm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - one way
1586 mBDm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - one way
1587 mBF-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
1587 mBF-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
1588 mBFm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
1588 mBFm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
1589 mBRm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - one way
1589 mBRm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - one way
1590 mCB+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1590 mCB+revert,Lm: chained merges (salvaged -> simple) - same content (when the file exists)
1591 mCB-change-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
1591 mCB-change-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
1592 mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
1592 mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
1593 mCBm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - the other way
1593 mCBm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - the other way
1594 mCBm-1 re-add d
1594 mCBm-1 re-add d
1595 mCGm-0 merge updated/deleted - revive the file (updated content) - one way
1595 mCGm-0 merge updated/deleted - revive the file (updated content) - one way
1596 mCH-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - one way
1596 mCH-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - one way
1597 mDBm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - the other way
1597 mDBm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - the other way
1598 mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
1598 mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
1599 mEA,Jm: chained merges (conflict -> simple) - same content everywhere
1599 mEA,Jm: chained merges (conflict -> simple) - same content everywhere
1600 mEA-change,Jm: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1600 mEA-change,Jm: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1601 mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
1601 mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
1602 mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
1602 mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
1603 mFB-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
1603 mFB-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
1604 mFBm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
1604 mFBm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
1605 mFG,Om: chained merges (copy-overwrite -> simple) - same content
1605 mFG,Om: chained merges (copy-overwrite -> simple) - same content
1606 mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
1606 mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
1607 mGCm-0 merge updated/deleted - revive the file (updated content) - the other way
1607 mGCm-0 merge updated/deleted - revive the file (updated content) - the other way
1608 mGDm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - the other way
1608 mGDm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - the other way
1609 mGF,Nm: chained merges (copy-overwrite -> simple) - same content
1609 mGF,Nm: chained merges (copy-overwrite -> simple) - same content
1610 mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
1610 mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
1611 mHC-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - the other way
1611 mHC-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - the other way
1612 mJ,EA-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1612 mJ,EA-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1613 mJ,EAm: chained merges (conflict -> simple) - same content everywhere
1613 mJ,EAm: chained merges (conflict -> simple) - same content everywhere
1614 mK,AE-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1614 mK,AE-change-m: chained merges (conflict+change -> simple) - same content on both branch in the initial merge
1615 mK,AEm: chained merges (conflict -> simple) - same content everywhere
1615 mK,AEm: chained merges (conflict -> simple) - same content everywhere
1616 mL,BC+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1616 mL,BC+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1617 mL,CB+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1617 mL,CB+revertm: chained merges (salvaged -> simple) - same content (when the file exists)
1618 mN,GFm: chained merges (copy-overwrite -> simple) - same content
1618 mN,GFm: chained merges (copy-overwrite -> simple) - same content
1619 mO,FGm: chained merges (copy-overwrite -> simple) - same content
1619 mO,FGm: chained merges (copy-overwrite -> simple) - same content
1620 mPQ,Tm: chained merges (conflict -> simple) - different content
1620 mPQ,Tm: chained merges (conflict -> simple) - different content
1621 mPQm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - one way
1621 mPQm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - one way
1622 mQP,Sm: chained merges (conflict -> simple) - different content
1622 mQP,Sm: chained merges (conflict -> simple) - different content
1623 mQPm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - the other way
1623 mQPm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - the other way
1624 mRBm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - the other way
1624 mRBm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - the other way
1625 mS,QPm: chained merges (conflict -> simple) - different content
1625 mS,QPm: chained merges (conflict -> simple) - different content
1626 mT,PQm: chained merges (conflict -> simple) - different content
1626 mT,PQm: chained merges (conflict -> simple) - different content
1627 n-1: unrelated changes (based on the "f" series of changes)
1627 n-1: unrelated changes (based on the "f" series of changes)
1628 o-1: unrelated changes (based on "g" changes)
1628 o-1: unrelated changes (based on "g" changes)
1629 p-1: t -move-> u
1629 p-1: t -move-> u
1630 p-2: u -move-> v
1630 p-2: u -move-> v
1631 q-1 r -move-> w
1631 q-1 r -move-> w
1632 q-2 w -move-> v
1632 q-2 w -move-> v
1633 r-1: rename r -> x
1633 r-1: rename r -> x
1634 r-2: rename t -> x
1634 r-2: rename t -> x
1635 s-1: unrelated changes (based on the "p" series of changes)
1635 s-1: unrelated changes (based on the "p" series of changes)
1636 t-1: unrelated changes (based on "q" changes)
1636 t-1: unrelated changes (based on "q" changes)
1637
1637
1638
1638
1639 Test that sidedata computations during upgrades are correct
1639 Test that sidedata computations during upgrades are correct
1640 ===========================================================
1640 ===========================================================
1641
1641
1642 We upgrade a repository that is not using sidedata (the filelog case) and
1642 We upgrade a repository that is not using sidedata (the filelog case) and
1643 check that the same side data have been generated as if they were computed at
1643 check that the same side data have been generated as if they were computed at
1644 commit time.
1644 commit time.
1645
1645
1646
1646
1647 #if upgraded
1647 #if upgraded
1648 $ cat >> $HGRCPATH << EOF
1648 $ cat >> $HGRCPATH << EOF
1649 > [format]
1649 > [format]
1650 > exp-use-copies-side-data-changeset = yes
1650 > exp-use-copies-side-data-changeset = yes
1651 > EOF
1651 > EOF
1652 $ hg debugformat -v
1652 $ hg debugformat -v
1653 format-variant repo config default
1653 format-variant repo config default
1654 fncache: yes yes yes
1654 fncache: yes yes yes
1655 dotencode: yes yes yes
1655 dotencode: yes yes yes
1656 generaldelta: yes yes yes
1656 generaldelta: yes yes yes
1657 share-safe: no no no
1657 share-safe: no no no
1658 sparserevlog: yes yes yes
1658 sparserevlog: yes yes yes
1659 persistent-nodemap: no no no (no-rust !)
1659 persistent-nodemap: no no no (no-rust !)
1660 persistent-nodemap: yes yes no (rust !)
1660 persistent-nodemap: yes yes no (rust !)
1661 copies-sdc: no yes no
1661 copies-sdc: no yes no
1662 revlog-v2: no yes no
1662 revlog-v2: no yes no
1663 plain-cl-delta: yes yes yes
1663 plain-cl-delta: yes yes yes
1664 compression: * (glob)
1664 compression: * (glob)
1665 compression-level: default default default
1665 compression-level: default default default
1666 $ hg debugupgraderepo --run --quiet
1666 $ hg debugupgraderepo --run --quiet
1667 upgrade will perform the following actions:
1667 upgrade will perform the following actions:
1668
1668
1669 requirements
1669 requirements
1670 preserved: * (glob)
1670 preserved: * (glob)
1671 removed: revlogv1
1671 removed: revlogv1
1672 added: exp-copies-sidedata-changeset, exp-revlogv2.2, exp-sidedata-flag
1672 added: exp-copies-sidedata-changeset, exp-revlogv2.2
1673
1673
1674 processed revlogs:
1674 processed revlogs:
1675 - all-filelogs
1675 - all-filelogs
1676 - changelog
1676 - changelog
1677 - manifest
1677 - manifest
1678
1678
1679 #endif
1679 #endif
1680
1680
1681 #if upgraded-parallel
1681 #if upgraded-parallel
1682 $ cat >> $HGRCPATH << EOF
1682 $ cat >> $HGRCPATH << EOF
1683 > [format]
1683 > [format]
1684 > exp-use-copies-side-data-changeset = yes
1684 > exp-use-copies-side-data-changeset = yes
1685 > [experimental]
1685 > [experimental]
1686 > worker.repository-upgrade=yes
1686 > worker.repository-upgrade=yes
1687 > [worker]
1687 > [worker]
1688 > enabled=yes
1688 > enabled=yes
1689 > numcpus=8
1689 > numcpus=8
1690 > EOF
1690 > EOF
1691 $ hg debugformat -v
1691 $ hg debugformat -v
1692 format-variant repo config default
1692 format-variant repo config default
1693 fncache: yes yes yes
1693 fncache: yes yes yes
1694 dotencode: yes yes yes
1694 dotencode: yes yes yes
1695 generaldelta: yes yes yes
1695 generaldelta: yes yes yes
1696 share-safe: no no no
1696 share-safe: no no no
1697 sparserevlog: yes yes yes
1697 sparserevlog: yes yes yes
1698 persistent-nodemap: no no no (no-rust !)
1698 persistent-nodemap: no no no (no-rust !)
1699 persistent-nodemap: yes yes no (rust !)
1699 persistent-nodemap: yes yes no (rust !)
1700 copies-sdc: no yes no
1700 copies-sdc: no yes no
1701 revlog-v2: no yes no
1701 revlog-v2: no yes no
1702 plain-cl-delta: yes yes yes
1702 plain-cl-delta: yes yes yes
1703 compression: * (glob)
1703 compression: * (glob)
1704 compression-level: default default default
1704 compression-level: default default default
1705 $ hg debugupgraderepo --run --quiet
1705 $ hg debugupgraderepo --run --quiet
1706 upgrade will perform the following actions:
1706 upgrade will perform the following actions:
1707
1707
1708 requirements
1708 requirements
1709 preserved: * (glob)
1709 preserved: * (glob)
1710 removed: revlogv1
1710 removed: revlogv1
1711 added: exp-copies-sidedata-changeset, exp-revlogv2.2, exp-sidedata-flag
1711 added: exp-copies-sidedata-changeset, exp-revlogv2.2
1712
1712
1713 processed revlogs:
1713 processed revlogs:
1714 - all-filelogs
1714 - all-filelogs
1715 - changelog
1715 - changelog
1716 - manifest
1716 - manifest
1717
1717
1718 #endif
1718 #endif
1719
1719
1720 #if pull
1720 #if pull
1721 $ cd ..
1721 $ cd ..
1722 $ mv repo-chain repo-source
1722 $ mv repo-chain repo-source
1723 $ hg init repo-chain
1723 $ hg init repo-chain
1724 $ cd repo-chain
1724 $ cd repo-chain
1725 $ hg pull ../repo-source
1725 $ hg pull ../repo-source
1726 pulling from ../repo-source
1726 pulling from ../repo-source
1727 requesting all changes
1727 requesting all changes
1728 adding changesets
1728 adding changesets
1729 adding manifests
1729 adding manifests
1730 adding file changes
1730 adding file changes
1731 added 80 changesets with 44 changes to 25 files (+39 heads)
1731 added 80 changesets with 44 changes to 25 files (+39 heads)
1732 new changesets a3a31bbefea6:908ce9259ffa
1732 new changesets a3a31bbefea6:908ce9259ffa
1733 (run 'hg heads' to see heads, 'hg merge' to merge)
1733 (run 'hg heads' to see heads, 'hg merge' to merge)
1734 #endif
1734 #endif
1735
1735
1736 #if pull-upgrade
1736 #if pull-upgrade
1737 $ cat >> $HGRCPATH << EOF
1737 $ cat >> $HGRCPATH << EOF
1738 > [format]
1738 > [format]
1739 > exp-use-copies-side-data-changeset = yes
1739 > exp-use-copies-side-data-changeset = yes
1740 > [experimental]
1740 > [experimental]
1741 > changegroup4 = yes
1741 > changegroup4 = yes
1742 > EOF
1742 > EOF
1743 $ cd ..
1743 $ cd ..
1744 $ mv repo-chain repo-source
1744 $ mv repo-chain repo-source
1745 $ hg init repo-chain
1745 $ hg init repo-chain
1746 $ cd repo-chain
1746 $ cd repo-chain
1747 $ hg pull ../repo-source
1747 $ hg pull ../repo-source
1748 pulling from ../repo-source
1748 pulling from ../repo-source
1749 requesting all changes
1749 requesting all changes
1750 adding changesets
1750 adding changesets
1751 adding manifests
1751 adding manifests
1752 adding file changes
1752 adding file changes
1753 added 80 changesets with 44 changes to 25 files (+39 heads)
1753 added 80 changesets with 44 changes to 25 files (+39 heads)
1754 new changesets a3a31bbefea6:908ce9259ffa
1754 new changesets a3a31bbefea6:908ce9259ffa
1755 (run 'hg heads' to see heads, 'hg merge' to merge)
1755 (run 'hg heads' to see heads, 'hg merge' to merge)
1756 #endif
1756 #endif
1757
1757
1758 #if push
1758 #if push
1759 $ cd ..
1759 $ cd ..
1760 $ mv repo-chain repo-source
1760 $ mv repo-chain repo-source
1761 $ hg init repo-chain
1761 $ hg init repo-chain
1762 $ cd repo-source
1762 $ cd repo-source
1763 $ hg push ../repo-chain
1763 $ hg push ../repo-chain
1764 pushing to ../repo-chain
1764 pushing to ../repo-chain
1765 searching for changes
1765 searching for changes
1766 adding changesets
1766 adding changesets
1767 adding manifests
1767 adding manifests
1768 adding file changes
1768 adding file changes
1769 added 80 changesets with 44 changes to 25 files (+39 heads)
1769 added 80 changesets with 44 changes to 25 files (+39 heads)
1770 $ cd ../repo-chain
1770 $ cd ../repo-chain
1771 #endif
1771 #endif
1772
1772
1773 #if push-upgrade
1773 #if push-upgrade
1774 $ cat >> $HGRCPATH << EOF
1774 $ cat >> $HGRCPATH << EOF
1775 > [format]
1775 > [format]
1776 > exp-use-copies-side-data-changeset = yes
1776 > exp-use-copies-side-data-changeset = yes
1777 > [experimental]
1777 > [experimental]
1778 > changegroup4 = yes
1778 > changegroup4 = yes
1779 > EOF
1779 > EOF
1780 $ cd ..
1780 $ cd ..
1781 $ mv repo-chain repo-source
1781 $ mv repo-chain repo-source
1782 $ hg init repo-chain
1782 $ hg init repo-chain
1783 $ cd repo-source
1783 $ cd repo-source
1784 $ hg push ../repo-chain
1784 $ hg push ../repo-chain
1785 pushing to ../repo-chain
1785 pushing to ../repo-chain
1786 searching for changes
1786 searching for changes
1787 adding changesets
1787 adding changesets
1788 adding manifests
1788 adding manifests
1789 adding file changes
1789 adding file changes
1790 added 80 changesets with 44 changes to 25 files (+39 heads)
1790 added 80 changesets with 44 changes to 25 files (+39 heads)
1791 $ cd ../repo-chain
1791 $ cd ../repo-chain
1792 #endif
1792 #endif
1793
1793
1794 #if no-compatibility no-filelog no-changeset
1794 #if no-compatibility no-filelog no-changeset
1795
1795
1796 $ hg debugchangedfiles --compute 0
1796 $ hg debugchangedfiles --compute 0
1797 added : a, ;
1797 added : a, ;
1798 added : b, ;
1798 added : b, ;
1799 added : h, ;
1799 added : h, ;
1800 added : p, ;
1800 added : p, ;
1801 added : q, ;
1801 added : q, ;
1802 added : r, ;
1802 added : r, ;
1803
1803
1804 $ for rev in `hg log --rev 'all()' -T '{rev}\n'`; do
1804 $ for rev in `hg log --rev 'all()' -T '{rev}\n'`; do
1805 > case_id=`hg log -r $rev -T '{word(0, desc, ":")}\n'`
1805 > case_id=`hg log -r $rev -T '{word(0, desc, ":")}\n'`
1806 > echo "##### revision \"$case_id\" #####"
1806 > echo "##### revision \"$case_id\" #####"
1807 > hg debugsidedata -c -v -- $rev
1807 > hg debugsidedata -c -v -- $rev
1808 > hg debugchangedfiles $rev
1808 > hg debugchangedfiles $rev
1809 > done
1809 > done
1810 ##### revision "i-0 initial commit" #####
1810 ##### revision "i-0 initial commit" #####
1811 1 sidedata entries
1811 1 sidedata entries
1812 entry-0014 size 64
1812 entry-0014 size 64
1813 '\x00\x00\x00\x06\x04\x00\x00\x00\x01\x00\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x00\x04\x00\x00\x00\x06\x00\x00\x00\x00abhpqr'
1813 '\x00\x00\x00\x06\x04\x00\x00\x00\x01\x00\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x00\x04\x00\x00\x00\x06\x00\x00\x00\x00abhpqr'
1814 added : a, ;
1814 added : a, ;
1815 added : b, ;
1815 added : b, ;
1816 added : h, ;
1816 added : h, ;
1817 added : p, ;
1817 added : p, ;
1818 added : q, ;
1818 added : q, ;
1819 added : r, ;
1819 added : r, ;
1820 ##### revision "i-1" #####
1820 ##### revision "i-1" #####
1821 1 sidedata entries
1821 1 sidedata entries
1822 entry-0014 size 44
1822 entry-0014 size 44
1823 '\x00\x00\x00\x04\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00\x0c\x00\x00\x00\x03\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x02acps'
1823 '\x00\x00\x00\x04\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00\x0c\x00\x00\x00\x03\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x02acps'
1824 removed : a, ;
1824 removed : a, ;
1825 added p1: c, a;
1825 added p1: c, a;
1826 removed : p, ;
1826 removed : p, ;
1827 added p1: s, p;
1827 added p1: s, p;
1828 ##### revision "i-2" #####
1828 ##### revision "i-2" #####
1829 1 sidedata entries
1829 1 sidedata entries
1830 entry-0014 size 44
1830 entry-0014 size 44
1831 '\x00\x00\x00\x04\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00\x0c\x00\x00\x00\x03\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x02cdst'
1831 '\x00\x00\x00\x04\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00\x0c\x00\x00\x00\x03\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x02cdst'
1832 removed : c, ;
1832 removed : c, ;
1833 added p1: d, c;
1833 added p1: d, c;
1834 removed : s, ;
1834 removed : s, ;
1835 added p1: t, s;
1835 added p1: t, s;
1836 ##### revision "a-1" #####
1836 ##### revision "a-1" #####
1837 1 sidedata entries
1837 1 sidedata entries
1838 entry-0014 size 24
1838 entry-0014 size 24
1839 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00de'
1839 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00de'
1840 removed : d, ;
1840 removed : d, ;
1841 added p1: e, d;
1841 added p1: e, d;
1842 ##### revision "a-2" #####
1842 ##### revision "a-2" #####
1843 1 sidedata entries
1843 1 sidedata entries
1844 entry-0014 size 24
1844 entry-0014 size 24
1845 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00ef'
1845 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00ef'
1846 removed : e, ;
1846 removed : e, ;
1847 added p1: f, e;
1847 added p1: f, e;
1848 ##### revision "b-1" #####
1848 ##### revision "b-1" #####
1849 1 sidedata entries
1849 1 sidedata entries
1850 entry-0014 size 14
1850 entry-0014 size 14
1851 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00b'
1851 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00b'
1852 touched : b, ;
1852 touched : b, ;
1853 ##### revision "c-1 delete d" #####
1853 ##### revision "c-1 delete d" #####
1854 1 sidedata entries
1854 1 sidedata entries
1855 entry-0014 size 14
1855 entry-0014 size 14
1856 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
1856 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
1857 removed : d, ;
1857 removed : d, ;
1858 ##### revision "d-1 delete d" #####
1858 ##### revision "d-1 delete d" #####
1859 1 sidedata entries
1859 1 sidedata entries
1860 entry-0014 size 14
1860 entry-0014 size 14
1861 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
1861 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
1862 removed : d, ;
1862 removed : d, ;
1863 ##### revision "d-2 re-add d" #####
1863 ##### revision "d-2 re-add d" #####
1864 1 sidedata entries
1864 1 sidedata entries
1865 entry-0014 size 14
1865 entry-0014 size 14
1866 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1866 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1867 added : d, ;
1867 added : d, ;
1868 ##### revision "e-1 b -move-> g" #####
1868 ##### revision "e-1 b -move-> g" #####
1869 1 sidedata entries
1869 1 sidedata entries
1870 entry-0014 size 24
1870 entry-0014 size 24
1871 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00bg'
1871 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00bg'
1872 removed : b, ;
1872 removed : b, ;
1873 added p1: g, b;
1873 added p1: g, b;
1874 ##### revision "e-2 g -move-> f" #####
1874 ##### revision "e-2 g -move-> f" #####
1875 1 sidedata entries
1875 1 sidedata entries
1876 entry-0014 size 24
1876 entry-0014 size 24
1877 '\x00\x00\x00\x02\x06\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00fg'
1877 '\x00\x00\x00\x02\x06\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00fg'
1878 added p1: f, g;
1878 added p1: f, g;
1879 removed : g, ;
1879 removed : g, ;
1880 ##### revision "p-1" #####
1880 ##### revision "p-1" #####
1881 1 sidedata entries
1881 1 sidedata entries
1882 entry-0014 size 24
1882 entry-0014 size 24
1883 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00tu'
1883 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00tu'
1884 removed : t, ;
1884 removed : t, ;
1885 added p1: u, t;
1885 added p1: u, t;
1886 ##### revision "p-2" #####
1886 ##### revision "p-2" #####
1887 1 sidedata entries
1887 1 sidedata entries
1888 entry-0014 size 24
1888 entry-0014 size 24
1889 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00uv'
1889 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00uv'
1890 removed : u, ;
1890 removed : u, ;
1891 added p1: v, u;
1891 added p1: v, u;
1892 ##### revision "q-1 r -move-> w" #####
1892 ##### revision "q-1 r -move-> w" #####
1893 1 sidedata entries
1893 1 sidedata entries
1894 entry-0014 size 24
1894 entry-0014 size 24
1895 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00rw'
1895 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00rw'
1896 removed : r, ;
1896 removed : r, ;
1897 added p1: w, r;
1897 added p1: w, r;
1898 ##### revision "q-2 w -move-> v" #####
1898 ##### revision "q-2 w -move-> v" #####
1899 1 sidedata entries
1899 1 sidedata entries
1900 entry-0014 size 24
1900 entry-0014 size 24
1901 '\x00\x00\x00\x02\x06\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00vw'
1901 '\x00\x00\x00\x02\x06\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00vw'
1902 added p1: v, w;
1902 added p1: v, w;
1903 removed : w, ;
1903 removed : w, ;
1904 ##### revision "mBAm-0 simple merge - A side" #####
1904 ##### revision "mBAm-0 simple merge - A side" #####
1905 1 sidedata entries
1905 1 sidedata entries
1906 entry-0014 size 4
1906 entry-0014 size 4
1907 '\x00\x00\x00\x00'
1907 '\x00\x00\x00\x00'
1908 ##### revision "mABm-0 simple merge - A side" #####
1908 ##### revision "mABm-0 simple merge - A side" #####
1909 1 sidedata entries
1909 1 sidedata entries
1910 entry-0014 size 4
1910 entry-0014 size 4
1911 '\x00\x00\x00\x00'
1911 '\x00\x00\x00\x00'
1912 ##### revision "mBCm-0 simple merge - C side" #####
1912 ##### revision "mBCm-0 simple merge - C side" #####
1913 1 sidedata entries
1913 1 sidedata entries
1914 entry-0014 size 4
1914 entry-0014 size 4
1915 '\x00\x00\x00\x00'
1915 '\x00\x00\x00\x00'
1916 ##### revision "mBCm-1 re-add d" #####
1916 ##### revision "mBCm-1 re-add d" #####
1917 1 sidedata entries
1917 1 sidedata entries
1918 entry-0014 size 14
1918 entry-0014 size 14
1919 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1919 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1920 added : d, ;
1920 added : d, ;
1921 ##### revision "mCBm-0 simple merge - C side" #####
1921 ##### revision "mCBm-0 simple merge - C side" #####
1922 1 sidedata entries
1922 1 sidedata entries
1923 entry-0014 size 4
1923 entry-0014 size 4
1924 '\x00\x00\x00\x00'
1924 '\x00\x00\x00\x00'
1925 ##### revision "mCBm-1 re-add d" #####
1925 ##### revision "mCBm-1 re-add d" #####
1926 1 sidedata entries
1926 1 sidedata entries
1927 entry-0014 size 14
1927 entry-0014 size 14
1928 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1928 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
1929 added : d, ;
1929 added : d, ;
1930 ##### revision "mBDm-0 simple merge - B side" #####
1930 ##### revision "mBDm-0 simple merge - B side" #####
1931 1 sidedata entries
1931 1 sidedata entries
1932 entry-0014 size 4
1932 entry-0014 size 4
1933 '\x00\x00\x00\x00'
1933 '\x00\x00\x00\x00'
1934 ##### revision "mDBm-0 simple merge - B side" #####
1934 ##### revision "mDBm-0 simple merge - B side" #####
1935 1 sidedata entries
1935 1 sidedata entries
1936 entry-0014 size 4
1936 entry-0014 size 4
1937 '\x00\x00\x00\x00'
1937 '\x00\x00\x00\x00'
1938 ##### revision "mAEm-0 merge with copies info on both side - A side" #####
1938 ##### revision "mAEm-0 merge with copies info on both side - A side" #####
1939 1 sidedata entries
1939 1 sidedata entries
1940 entry-0014 size 14
1940 entry-0014 size 14
1941 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
1941 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
1942 merged : f, ;
1942 merged : f, ;
1943 ##### revision "mEAm-0 merge with copies info on both side - A side" #####
1943 ##### revision "mEAm-0 merge with copies info on both side - A side" #####
1944 1 sidedata entries
1944 1 sidedata entries
1945 entry-0014 size 14
1945 entry-0014 size 14
1946 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
1946 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
1947 merged : f, ;
1947 merged : f, ;
1948 ##### revision "mPQm-0 merge with copies info on both side - P side" #####
1948 ##### revision "mPQm-0 merge with copies info on both side - P side" #####
1949 1 sidedata entries
1949 1 sidedata entries
1950 entry-0014 size 14
1950 entry-0014 size 14
1951 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00v'
1951 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00v'
1952 merged : v, ;
1952 merged : v, ;
1953 ##### revision "mQPm-0 merge with copies info on both side - P side" #####
1953 ##### revision "mQPm-0 merge with copies info on both side - P side" #####
1954 1 sidedata entries
1954 1 sidedata entries
1955 entry-0014 size 14
1955 entry-0014 size 14
1956 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00v'
1956 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00v'
1957 merged : v, ;
1957 merged : v, ;
1958 ##### revision "f-1" #####
1958 ##### revision "f-1" #####
1959 1 sidedata entries
1959 1 sidedata entries
1960 entry-0014 size 24
1960 entry-0014 size 24
1961 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00hi'
1961 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00hi'
1962 removed : h, ;
1962 removed : h, ;
1963 added p1: i, h;
1963 added p1: i, h;
1964 ##### revision "f-2" #####
1964 ##### revision "f-2" #####
1965 1 sidedata entries
1965 1 sidedata entries
1966 entry-0014 size 24
1966 entry-0014 size 24
1967 '\x00\x00\x00\x02\x16\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00di'
1967 '\x00\x00\x00\x02\x16\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00di'
1968 touched p1: d, i;
1968 touched p1: d, i;
1969 removed : i, ;
1969 removed : i, ;
1970 ##### revision "mBFm-0 simple merge - B side" #####
1970 ##### revision "mBFm-0 simple merge - B side" #####
1971 1 sidedata entries
1971 1 sidedata entries
1972 entry-0014 size 4
1972 entry-0014 size 4
1973 '\x00\x00\x00\x00'
1973 '\x00\x00\x00\x00'
1974 ##### revision "mFBm-0 simple merge - B side" #####
1974 ##### revision "mFBm-0 simple merge - B side" #####
1975 1 sidedata entries
1975 1 sidedata entries
1976 entry-0014 size 4
1976 entry-0014 size 4
1977 '\x00\x00\x00\x00'
1977 '\x00\x00\x00\x00'
1978 ##### revision "r-1" #####
1978 ##### revision "r-1" #####
1979 1 sidedata entries
1979 1 sidedata entries
1980 entry-0014 size 24
1980 entry-0014 size 24
1981 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00rx'
1981 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00rx'
1982 removed : r, ;
1982 removed : r, ;
1983 added p1: x, r;
1983 added p1: x, r;
1984 ##### revision "r-2" #####
1984 ##### revision "r-2" #####
1985 1 sidedata entries
1985 1 sidedata entries
1986 entry-0014 size 24
1986 entry-0014 size 24
1987 '\x00\x00\x00\x02\x16\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00tx'
1987 '\x00\x00\x00\x02\x16\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00tx'
1988 touched p1: t, x;
1988 touched p1: t, x;
1989 removed : x, ;
1989 removed : x, ;
1990 ##### revision "mBRm-0 simple merge - B side" #####
1990 ##### revision "mBRm-0 simple merge - B side" #####
1991 1 sidedata entries
1991 1 sidedata entries
1992 entry-0014 size 4
1992 entry-0014 size 4
1993 '\x00\x00\x00\x00'
1993 '\x00\x00\x00\x00'
1994 ##### revision "mRBm-0 simple merge - B side" #####
1994 ##### revision "mRBm-0 simple merge - B side" #####
1995 1 sidedata entries
1995 1 sidedata entries
1996 entry-0014 size 4
1996 entry-0014 size 4
1997 '\x00\x00\x00\x00'
1997 '\x00\x00\x00\x00'
1998 ##### revision "g-1" #####
1998 ##### revision "g-1" #####
1999 1 sidedata entries
1999 1 sidedata entries
2000 entry-0014 size 14
2000 entry-0014 size 14
2001 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00d'
2001 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00d'
2002 touched : d, ;
2002 touched : d, ;
2003 ##### revision "mDGm-0 actual content merge, copies on one side - D side" #####
2003 ##### revision "mDGm-0 actual content merge, copies on one side - D side" #####
2004 1 sidedata entries
2004 1 sidedata entries
2005 entry-0014 size 14
2005 entry-0014 size 14
2006 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
2006 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
2007 merged : d, ;
2007 merged : d, ;
2008 ##### revision "mGDm-0 actual content merge, copies on one side - D side" #####
2008 ##### revision "mGDm-0 actual content merge, copies on one side - D side" #####
2009 1 sidedata entries
2009 1 sidedata entries
2010 entry-0014 size 14
2010 entry-0014 size 14
2011 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
2011 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
2012 merged : d, ;
2012 merged : d, ;
2013 ##### revision "mFGm-0 merge - G side" #####
2013 ##### revision "mFGm-0 merge - G side" #####
2014 1 sidedata entries
2014 1 sidedata entries
2015 entry-0014 size 14
2015 entry-0014 size 14
2016 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
2016 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
2017 merged : d, ;
2017 merged : d, ;
2018 ##### revision "mGFm-0 merge - G side" #####
2018 ##### revision "mGFm-0 merge - G side" #####
2019 1 sidedata entries
2019 1 sidedata entries
2020 entry-0014 size 14
2020 entry-0014 size 14
2021 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
2021 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
2022 merged : d, ;
2022 merged : d, ;
2023 ##### revision "mCGm-0 merge updated/deleted - revive the file (updated content) - one way" #####
2023 ##### revision "mCGm-0 merge updated/deleted - revive the file (updated content) - one way" #####
2024 1 sidedata entries
2024 1 sidedata entries
2025 entry-0014 size 14
2025 entry-0014 size 14
2026 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
2026 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
2027 salvaged : d, ;
2027 salvaged : d, ;
2028 ##### revision "mGCm-0 merge updated/deleted - revive the file (updated content) - the other way" #####
2028 ##### revision "mGCm-0 merge updated/deleted - revive the file (updated content) - the other way" #####
2029 1 sidedata entries
2029 1 sidedata entries
2030 entry-0014 size 14
2030 entry-0014 size 14
2031 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
2031 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
2032 salvaged : d, ;
2032 salvaged : d, ;
2033 ##### revision "mCB-revert-m-0 merge explicitely revive deleted file - B side" #####
2033 ##### revision "mCB-revert-m-0 merge explicitely revive deleted file - B side" #####
2034 1 sidedata entries
2034 1 sidedata entries
2035 entry-0014 size 14
2035 entry-0014 size 14
2036 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
2036 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
2037 salvaged : d, ;
2037 salvaged : d, ;
2038 ##### revision "mBC-revert-m-0 merge explicitely revive deleted file - B side" #####
2038 ##### revision "mBC-revert-m-0 merge explicitely revive deleted file - B side" #####
2039 1 sidedata entries
2039 1 sidedata entries
2040 entry-0014 size 14
2040 entry-0014 size 14
2041 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
2041 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
2042 salvaged : d, ;
2042 salvaged : d, ;
2043 ##### revision "h-1" #####
2043 ##### revision "h-1" #####
2044 1 sidedata entries
2044 1 sidedata entries
2045 entry-0014 size 24
2045 entry-0014 size 24
2046 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00bd'
2046 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00bd'
2047 removed : b, ;
2047 removed : b, ;
2048 added p1: d, b;
2048 added p1: d, b;
2049 ##### revision "mCH-delete-before-conflict-m-0 simple merge - C side" #####
2049 ##### revision "mCH-delete-before-conflict-m-0 simple merge - C side" #####
2050 1 sidedata entries
2050 1 sidedata entries
2051 entry-0014 size 4
2051 entry-0014 size 4
2052 '\x00\x00\x00\x00'
2052 '\x00\x00\x00\x00'
2053 ##### revision "mHC-delete-before-conflict-m-0 simple merge - C side" #####
2053 ##### revision "mHC-delete-before-conflict-m-0 simple merge - C side" #####
2054 1 sidedata entries
2054 1 sidedata entries
2055 entry-0014 size 4
2055 entry-0014 size 4
2056 '\x00\x00\x00\x00'
2056 '\x00\x00\x00\x00'
2057 ##### revision "mAE-change-m-0 merge with file update and copies info on both side - A side" #####
2057 ##### revision "mAE-change-m-0 merge with file update and copies info on both side - A side" #####
2058 1 sidedata entries
2058 1 sidedata entries
2059 entry-0014 size 14
2059 entry-0014 size 14
2060 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
2060 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
2061 merged : f, ;
2061 merged : f, ;
2062 ##### revision "mEA-change-m-0 merge with file update and copies info on both side - A side" #####
2062 ##### revision "mEA-change-m-0 merge with file update and copies info on both side - A side" #####
2063 1 sidedata entries
2063 1 sidedata entries
2064 entry-0014 size 14
2064 entry-0014 size 14
2065 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
2065 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
2066 merged : f, ;
2066 merged : f, ;
2067 ##### revision "mBF-change-m-0 merge with extra change - B side" #####
2067 ##### revision "mBF-change-m-0 merge with extra change - B side" #####
2068 1 sidedata entries
2068 1 sidedata entries
2069 entry-0014 size 14
2069 entry-0014 size 14
2070 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00d'
2070 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00d'
2071 touched : d, ;
2071 touched : d, ;
2072 ##### revision "mFB-change-m-0 merge with extra change - B side" #####
2072 ##### revision "mFB-change-m-0 merge with extra change - B side" #####
2073 1 sidedata entries
2073 1 sidedata entries
2074 entry-0014 size 14
2074 entry-0014 size 14
2075 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00d'
2075 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00d'
2076 touched : d, ;
2076 touched : d, ;
2077 ##### revision "mCB-change-m-0 merge explicitely revive deleted file - B side" #####
2077 ##### revision "mCB-change-m-0 merge explicitely revive deleted file - B side" #####
2078 1 sidedata entries
2078 1 sidedata entries
2079 entry-0014 size 14
2079 entry-0014 size 14
2080 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
2080 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
2081 salvaged : d, ;
2081 salvaged : d, ;
2082 ##### revision "mBC-change-m-0 merge explicitely revive deleted file - B side" #####
2082 ##### revision "mBC-change-m-0 merge explicitely revive deleted file - B side" #####
2083 1 sidedata entries
2083 1 sidedata entries
2084 entry-0014 size 14
2084 entry-0014 size 14
2085 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
2085 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
2086 salvaged : d, ;
2086 salvaged : d, ;
2087 ##### revision "j-1" #####
2087 ##### revision "j-1" #####
2088 1 sidedata entries
2088 1 sidedata entries
2089 entry-0014 size 24
2089 entry-0014 size 24
2090 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-j'
2090 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-j'
2091 added : unrelated-j, ;
2091 added : unrelated-j, ;
2092 ##### revision "k-1" #####
2092 ##### revision "k-1" #####
2093 1 sidedata entries
2093 1 sidedata entries
2094 entry-0014 size 24
2094 entry-0014 size 24
2095 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-k'
2095 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-k'
2096 added : unrelated-k, ;
2096 added : unrelated-k, ;
2097 ##### revision "mAE,Km" #####
2097 ##### revision "mAE,Km" #####
2098 1 sidedata entries
2098 1 sidedata entries
2099 entry-0014 size 4
2099 entry-0014 size 4
2100 '\x00\x00\x00\x00'
2100 '\x00\x00\x00\x00'
2101 ##### revision "mK,AEm" #####
2101 ##### revision "mK,AEm" #####
2102 1 sidedata entries
2102 1 sidedata entries
2103 entry-0014 size 4
2103 entry-0014 size 4
2104 '\x00\x00\x00\x00'
2104 '\x00\x00\x00\x00'
2105 ##### revision "mEA,Jm" #####
2105 ##### revision "mEA,Jm" #####
2106 1 sidedata entries
2106 1 sidedata entries
2107 entry-0014 size 24
2107 entry-0014 size 24
2108 '\x00\x00\x00\x01\x14\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-j'
2108 '\x00\x00\x00\x01\x14\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-j'
2109 touched : unrelated-j, ;
2109 touched : unrelated-j, ;
2110 ##### revision "mJ,EAm" #####
2110 ##### revision "mJ,EAm" #####
2111 1 sidedata entries
2111 1 sidedata entries
2112 entry-0014 size 24
2112 entry-0014 size 24
2113 '\x00\x00\x00\x01\x14\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-j'
2113 '\x00\x00\x00\x01\x14\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-j'
2114 touched : unrelated-j, ;
2114 touched : unrelated-j, ;
2115 ##### revision "s-1" #####
2115 ##### revision "s-1" #####
2116 1 sidedata entries
2116 1 sidedata entries
2117 entry-0014 size 24
2117 entry-0014 size 24
2118 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-s'
2118 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-s'
2119 added : unrelated-s, ;
2119 added : unrelated-s, ;
2120 ##### revision "t-1" #####
2120 ##### revision "t-1" #####
2121 1 sidedata entries
2121 1 sidedata entries
2122 entry-0014 size 24
2122 entry-0014 size 24
2123 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-t'
2123 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-t'
2124 added : unrelated-t, ;
2124 added : unrelated-t, ;
2125 ##### revision "mPQ,Tm" #####
2125 ##### revision "mPQ,Tm" #####
2126 1 sidedata entries
2126 1 sidedata entries
2127 entry-0014 size 4
2127 entry-0014 size 4
2128 '\x00\x00\x00\x00'
2128 '\x00\x00\x00\x00'
2129 ##### revision "mT,PQm" #####
2129 ##### revision "mT,PQm" #####
2130 1 sidedata entries
2130 1 sidedata entries
2131 entry-0014 size 4
2131 entry-0014 size 4
2132 '\x00\x00\x00\x00'
2132 '\x00\x00\x00\x00'
2133 ##### revision "mQP,Sm" #####
2133 ##### revision "mQP,Sm" #####
2134 1 sidedata entries
2134 1 sidedata entries
2135 entry-0014 size 4
2135 entry-0014 size 4
2136 '\x00\x00\x00\x00'
2136 '\x00\x00\x00\x00'
2137 ##### revision "mS,QPm" #####
2137 ##### revision "mS,QPm" #####
2138 1 sidedata entries
2138 1 sidedata entries
2139 entry-0014 size 4
2139 entry-0014 size 4
2140 '\x00\x00\x00\x00'
2140 '\x00\x00\x00\x00'
2141 ##### revision "l-1" #####
2141 ##### revision "l-1" #####
2142 1 sidedata entries
2142 1 sidedata entries
2143 entry-0014 size 24
2143 entry-0014 size 24
2144 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-l'
2144 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-l'
2145 added : unrelated-l, ;
2145 added : unrelated-l, ;
2146 ##### revision "mBC+revert,Lm" #####
2146 ##### revision "mBC+revert,Lm" #####
2147 1 sidedata entries
2147 1 sidedata entries
2148 entry-0014 size 4
2148 entry-0014 size 4
2149 '\x00\x00\x00\x00'
2149 '\x00\x00\x00\x00'
2150 ##### revision "mCB+revert,Lm" #####
2150 ##### revision "mCB+revert,Lm" #####
2151 1 sidedata entries
2151 1 sidedata entries
2152 entry-0014 size 4
2152 entry-0014 size 4
2153 '\x00\x00\x00\x00'
2153 '\x00\x00\x00\x00'
2154 ##### revision "mL,BC+revertm" #####
2154 ##### revision "mL,BC+revertm" #####
2155 1 sidedata entries
2155 1 sidedata entries
2156 entry-0014 size 4
2156 entry-0014 size 4
2157 '\x00\x00\x00\x00'
2157 '\x00\x00\x00\x00'
2158 ##### revision "mL,CB+revertm" #####
2158 ##### revision "mL,CB+revertm" #####
2159 1 sidedata entries
2159 1 sidedata entries
2160 entry-0014 size 4
2160 entry-0014 size 4
2161 '\x00\x00\x00\x00'
2161 '\x00\x00\x00\x00'
2162 ##### revision "n-1" #####
2162 ##### revision "n-1" #####
2163 1 sidedata entries
2163 1 sidedata entries
2164 entry-0014 size 24
2164 entry-0014 size 24
2165 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-n'
2165 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-n'
2166 added : unrelated-n, ;
2166 added : unrelated-n, ;
2167 ##### revision "o-1" #####
2167 ##### revision "o-1" #####
2168 1 sidedata entries
2168 1 sidedata entries
2169 entry-0014 size 24
2169 entry-0014 size 24
2170 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-o'
2170 '\x00\x00\x00\x01\x04\x00\x00\x00\x0b\x00\x00\x00\x00unrelated-o'
2171 added : unrelated-o, ;
2171 added : unrelated-o, ;
2172 ##### revision "mFG,Om" #####
2172 ##### revision "mFG,Om" #####
2173 1 sidedata entries
2173 1 sidedata entries
2174 entry-0014 size 4
2174 entry-0014 size 4
2175 '\x00\x00\x00\x00'
2175 '\x00\x00\x00\x00'
2176 ##### revision "mO,FGm" #####
2176 ##### revision "mO,FGm" #####
2177 1 sidedata entries
2177 1 sidedata entries
2178 entry-0014 size 4
2178 entry-0014 size 4
2179 '\x00\x00\x00\x00'
2179 '\x00\x00\x00\x00'
2180 ##### revision "mGF,Nm" #####
2180 ##### revision "mGF,Nm" #####
2181 1 sidedata entries
2181 1 sidedata entries
2182 entry-0014 size 4
2182 entry-0014 size 4
2183 '\x00\x00\x00\x00'
2183 '\x00\x00\x00\x00'
2184 ##### revision "mN,GFm" #####
2184 ##### revision "mN,GFm" #####
2185 1 sidedata entries
2185 1 sidedata entries
2186 entry-0014 size 4
2186 entry-0014 size 4
2187 '\x00\x00\x00\x00'
2187 '\x00\x00\x00\x00'
2188 ##### revision "mAE-change,Km" #####
2188 ##### revision "mAE-change,Km" #####
2189 1 sidedata entries
2189 1 sidedata entries
2190 entry-0014 size 4
2190 entry-0014 size 4
2191 '\x00\x00\x00\x00'
2191 '\x00\x00\x00\x00'
2192 ##### revision "mK,AE-change-m" #####
2192 ##### revision "mK,AE-change-m" #####
2193 1 sidedata entries
2193 1 sidedata entries
2194 entry-0014 size 4
2194 entry-0014 size 4
2195 '\x00\x00\x00\x00'
2195 '\x00\x00\x00\x00'
2196 ##### revision "mEA-change,Jm" #####
2196 ##### revision "mEA-change,Jm" #####
2197 1 sidedata entries
2197 1 sidedata entries
2198 entry-0014 size 4
2198 entry-0014 size 4
2199 '\x00\x00\x00\x00'
2199 '\x00\x00\x00\x00'
2200 ##### revision "mJ,EA-change-m" #####
2200 ##### revision "mJ,EA-change-m" #####
2201 1 sidedata entries
2201 1 sidedata entries
2202 entry-0014 size 4
2202 entry-0014 size 4
2203 '\x00\x00\x00\x00'
2203 '\x00\x00\x00\x00'
2204
2204
2205 #endif
2205 #endif
2206
2206
2207
2207
2208 Test copy information chaining
2208 Test copy information chaining
2209 ==============================
2209 ==============================
2210
2210
2211 Check that matching only affect the destination and not intermediate path
2211 Check that matching only affect the destination and not intermediate path
2212 -------------------------------------------------------------------------
2212 -------------------------------------------------------------------------
2213
2213
2214 The two status call should give the same value for f
2214 The two status call should give the same value for f
2215
2215
2216 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("a-2")'
2216 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("a-2")'
2217 A f
2217 A f
2218 a
2218 a
2219 A t
2219 A t
2220 p
2220 p
2221 R a
2221 R a
2222 R p
2222 R p
2223 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("a-2")' f
2223 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("a-2")' f
2224 A f
2224 A f
2225 a (no-changeset no-compatibility !)
2225 a (no-changeset no-compatibility !)
2226
2226
2227 merging with unrelated change does not interfere with the renames
2227 merging with unrelated change does not interfere with the renames
2228 ---------------------------------------------------------------
2228 ---------------------------------------------------------------
2229
2229
2230 - rename on one side
2230 - rename on one side
2231 - unrelated change on the other side
2231 - unrelated change on the other side
2232
2232
2233 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
2233 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
2234 o mABm-0 simple merge - A side: multiple renames, B side: unrelated update - the other way
2234 o mABm-0 simple merge - A side: multiple renames, B side: unrelated update - the other way
2235 |\
2235 |\
2236 +---o mBAm-0 simple merge - A side: multiple renames, B side: unrelated update - one way
2236 +---o mBAm-0 simple merge - A side: multiple renames, B side: unrelated update - one way
2237 | |/
2237 | |/
2238 | o b-1: b update
2238 | o b-1: b update
2239 | |
2239 | |
2240 o | a-2: e -move-> f
2240 o | a-2: e -move-> f
2241 | |
2241 | |
2242 o | a-1: d -move-> e
2242 o | a-1: d -move-> e
2243 |/
2243 |/
2244 o i-2: c -move-> d, s -move-> t
2244 o i-2: c -move-> d, s -move-> t
2245 |
2245 |
2246 o i-1: a -move-> c, p -move-> s
2246 o i-1: a -move-> c, p -move-> s
2247 |
2247 |
2248 o i-0 initial commit: a b h p q r
2248 o i-0 initial commit: a b h p q r
2249
2249
2250
2250
2251 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mABm")'
2251 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mABm")'
2252 A f
2252 A f
2253 d
2253 d
2254 R d
2254 R d
2255 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBAm")'
2255 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBAm")'
2256 A f
2256 A f
2257 d
2257 d
2258 R d
2258 R d
2259 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mABm")'
2259 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mABm")'
2260 M b
2260 M b
2261 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mBAm")'
2261 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mBAm")'
2262 M b
2262 M b
2263 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mABm")'
2263 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mABm")'
2264 M b
2264 M b
2265 A f
2265 A f
2266 d
2266 d
2267 R d
2267 R d
2268 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBAm")'
2268 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBAm")'
2269 M b
2269 M b
2270 A f
2270 A f
2271 d
2271 d
2272 R d
2272 R d
2273 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mABm")'
2273 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mABm")'
2274 M b
2274 M b
2275 A f
2275 A f
2276 a
2276 a
2277 A t
2277 A t
2278 p
2278 p
2279 R a
2279 R a
2280 R p
2280 R p
2281 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBAm")'
2281 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBAm")'
2282 M b
2282 M b
2283 A f
2283 A f
2284 a
2284 a
2285 A t
2285 A t
2286 p
2286 p
2287 R a
2287 R a
2288 R p
2288 R p
2289
2289
2290 merging with the side having a delete
2290 merging with the side having a delete
2291 -------------------------------------
2291 -------------------------------------
2292
2292
2293 case summary:
2293 case summary:
2294 - one with change to an unrelated file
2294 - one with change to an unrelated file
2295 - one deleting the change
2295 - one deleting the change
2296 and recreate an unrelated file after the merge
2296 and recreate an unrelated file after the merge
2297
2297
2298 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
2298 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
2299 o mCBm-1 re-add d
2299 o mCBm-1 re-add d
2300 |
2300 |
2301 o mCBm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - the other way
2301 o mCBm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - the other way
2302 |\
2302 |\
2303 | | o mBCm-1 re-add d
2303 | | o mBCm-1 re-add d
2304 | | |
2304 | | |
2305 +---o mBCm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - one way
2305 +---o mBCm-0 simple merge - C side: delete a file with copies history , B side: unrelated update - one way
2306 | |/
2306 | |/
2307 | o c-1 delete d
2307 | o c-1 delete d
2308 | |
2308 | |
2309 o | b-1: b update
2309 o | b-1: b update
2310 |/
2310 |/
2311 o i-2: c -move-> d, s -move-> t
2311 o i-2: c -move-> d, s -move-> t
2312 |
2312 |
2313 o i-1: a -move-> c, p -move-> s
2313 o i-1: a -move-> c, p -move-> s
2314 |
2314 |
2315 o i-0 initial commit: a b h p q r
2315 o i-0 initial commit: a b h p q r
2316
2316
2317 - comparing from the merge
2317 - comparing from the merge
2318
2318
2319 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-0")'
2319 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-0")'
2320 R d
2320 R d
2321 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-0")'
2321 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-0")'
2322 R d
2322 R d
2323 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-0")'
2323 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-0")'
2324 M b
2324 M b
2325 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-0")'
2325 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-0")'
2326 M b
2326 M b
2327 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-0")'
2327 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-0")'
2328 M b
2328 M b
2329 R d
2329 R d
2330 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-0")'
2330 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-0")'
2331 M b
2331 M b
2332 R d
2332 R d
2333 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-0")'
2333 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-0")'
2334 M b
2334 M b
2335 A t
2335 A t
2336 p
2336 p
2337 R a
2337 R a
2338 R p
2338 R p
2339 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-0")'
2339 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-0")'
2340 M b
2340 M b
2341 A t
2341 A t
2342 p
2342 p
2343 R a
2343 R a
2344 R p
2344 R p
2345
2345
2346 - comparing with the merge children re-adding the file
2346 - comparing with the merge children re-adding the file
2347
2347
2348 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-1")'
2348 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-1")'
2349 M d
2349 M d
2350 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-1")'
2350 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-1")'
2351 M d
2351 M d
2352 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-1")'
2352 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-1")'
2353 M b
2353 M b
2354 A d
2354 A d
2355 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-1")'
2355 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-1")'
2356 M b
2356 M b
2357 A d
2357 A d
2358 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-1")'
2358 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-1")'
2359 M b
2359 M b
2360 M d
2360 M d
2361 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-1")'
2361 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-1")'
2362 M b
2362 M b
2363 M d
2363 M d
2364 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-1")'
2364 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-1")'
2365 M b
2365 M b
2366 A d
2366 A d
2367 A t
2367 A t
2368 p
2368 p
2369 R a
2369 R a
2370 R p
2370 R p
2371 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-1")'
2371 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-1")'
2372 M b
2372 M b
2373 A d
2373 A d
2374 A t
2374 A t
2375 p
2375 p
2376 R a
2376 R a
2377 R p
2377 R p
2378
2378
2379 Comparing with a merge re-adding the file afterward
2379 Comparing with a merge re-adding the file afterward
2380 ---------------------------------------------------
2380 ---------------------------------------------------
2381
2381
2382 Merge:
2382 Merge:
2383 - one with change to an unrelated file
2383 - one with change to an unrelated file
2384 - one deleting and recreating the change
2384 - one deleting and recreating the change
2385
2385
2386 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
2386 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
2387 o mDBm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - the other way
2387 o mDBm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - the other way
2388 |\
2388 |\
2389 +---o mBDm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - one way
2389 +---o mBDm-0 simple merge - B side: unrelated update, D side: delete and recreate a file (with different content) - one way
2390 | |/
2390 | |/
2391 | o d-2 re-add d
2391 | o d-2 re-add d
2392 | |
2392 | |
2393 | o d-1 delete d
2393 | o d-1 delete d
2394 | |
2394 | |
2395 o | b-1: b update
2395 o | b-1: b update
2396 |/
2396 |/
2397 o i-2: c -move-> d, s -move-> t
2397 o i-2: c -move-> d, s -move-> t
2398 |
2398 |
2399 o i-1: a -move-> c, p -move-> s
2399 o i-1: a -move-> c, p -move-> s
2400 |
2400 |
2401 o i-0 initial commit: a b h p q r
2401 o i-0 initial commit: a b h p q r
2402
2402
2403 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBDm-0")'
2403 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBDm-0")'
2404 M d
2404 M d
2405 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mDBm-0")'
2405 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mDBm-0")'
2406 M d
2406 M d
2407 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mBDm-0")'
2407 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mBDm-0")'
2408 M b
2408 M b
2409 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDBm-0")'
2409 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDBm-0")'
2410 M b
2410 M b
2411 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBDm-0")'
2411 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBDm-0")'
2412 M b
2412 M b
2413 M d
2413 M d
2414 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mDBm-0")'
2414 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mDBm-0")'
2415 M b
2415 M b
2416 M d
2416 M d
2417
2417
2418 The bugs makes recorded copy is different depending of where we started the merge from since
2418 The bugs makes recorded copy is different depending of where we started the merge from since
2419
2419
2420 $ hg manifest --debug --rev 'desc("mBDm-0")' | grep '644 d'
2420 $ hg manifest --debug --rev 'desc("mBDm-0")' | grep '644 d'
2421 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2421 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2422 $ hg manifest --debug --rev 'desc("mDBm-0")' | grep '644 d'
2422 $ hg manifest --debug --rev 'desc("mDBm-0")' | grep '644 d'
2423 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2423 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2424
2424
2425 $ hg manifest --debug --rev 'desc("d-2")' | grep '644 d'
2425 $ hg manifest --debug --rev 'desc("d-2")' | grep '644 d'
2426 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2426 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2427 $ hg manifest --debug --rev 'desc("b-1")' | grep '644 d'
2427 $ hg manifest --debug --rev 'desc("b-1")' | grep '644 d'
2428 d8252ab2e760b0d4e5288fd44cbd15a0fa567e16 644 d (no-changeset !)
2428 d8252ab2e760b0d4e5288fd44cbd15a0fa567e16 644 d (no-changeset !)
2429 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 d (changeset !)
2429 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 d (changeset !)
2430 $ hg debugindex d | head -n 4 | "$PYTHON" ../no-linkrev
2430 $ hg debugindex d | head -n 4 | "$PYTHON" ../no-linkrev
2431 rev linkrev nodeid p1 p2
2431 rev linkrev nodeid p1 p2
2432 0 * d8252ab2e760 000000000000 000000000000 (no-changeset !)
2432 0 * d8252ab2e760 000000000000 000000000000 (no-changeset !)
2433 0 * ae258f702dfe 000000000000 000000000000 (changeset !)
2433 0 * ae258f702dfe 000000000000 000000000000 (changeset !)
2434 1 * b004912a8510 000000000000 000000000000
2434 1 * b004912a8510 000000000000 000000000000
2435 2 * 7b79e2fe0c89 000000000000 000000000000 (no-changeset !)
2435 2 * 7b79e2fe0c89 000000000000 000000000000 (no-changeset !)
2436 2 * 5cce88bf349f ae258f702dfe 000000000000 (changeset !)
2436 2 * 5cce88bf349f ae258f702dfe 000000000000 (changeset !)
2437
2437
2438 Log output should not include a merge commit as it did not happen
2438 Log output should not include a merge commit as it did not happen
2439
2439
2440 $ hg log -Gfr 'desc("mBDm-0")' d
2440 $ hg log -Gfr 'desc("mBDm-0")' d
2441 o d-2 re-add d
2441 o d-2 re-add d
2442 |
2442 |
2443 ~
2443 ~
2444
2444
2445 $ hg log -Gfr 'desc("mDBm-0")' d
2445 $ hg log -Gfr 'desc("mDBm-0")' d
2446 o d-2 re-add d
2446 o d-2 re-add d
2447 |
2447 |
2448 ~
2448 ~
2449
2449
2450 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBDm-0")'
2450 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBDm-0")'
2451 M b
2451 M b
2452 A d
2452 A d
2453 A t
2453 A t
2454 p
2454 p
2455 R a
2455 R a
2456 R p
2456 R p
2457 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDBm-0")'
2457 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDBm-0")'
2458 M b
2458 M b
2459 A d
2459 A d
2460 A t
2460 A t
2461 p
2461 p
2462 R a
2462 R a
2463 R p
2463 R p
2464
2464
2465
2465
2466 Comparing with a merge with colliding rename
2466 Comparing with a merge with colliding rename
2467 --------------------------------------------
2467 --------------------------------------------
2468
2468
2469 Subcase: new copy information on both side
2469 Subcase: new copy information on both side
2470 ``````````````````````````````````````````
2470 ``````````````````````````````````````````
2471
2471
2472 - the "e-" branch renaming b to f (through 'g')
2472 - the "e-" branch renaming b to f (through 'g')
2473 - the "a-" branch renaming d to f (through e)
2473 - the "a-" branch renaming d to f (through e)
2474
2474
2475 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
2475 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
2476 o mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
2476 o mEAm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - the other way
2477 |\
2477 |\
2478 +---o mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
2478 +---o mAEm-0 merge with copies info on both side - A side: rename d to f, E side: b to f, (same content for f) - one way
2479 | |/
2479 | |/
2480 | o e-2 g -move-> f
2480 | o e-2 g -move-> f
2481 | |
2481 | |
2482 | o e-1 b -move-> g
2482 | o e-1 b -move-> g
2483 | |
2483 | |
2484 o | a-2: e -move-> f
2484 o | a-2: e -move-> f
2485 | |
2485 | |
2486 o | a-1: d -move-> e
2486 o | a-1: d -move-> e
2487 |/
2487 |/
2488 o i-2: c -move-> d, s -move-> t
2488 o i-2: c -move-> d, s -move-> t
2489 |
2489 |
2490 o i-1: a -move-> c, p -move-> s
2490 o i-1: a -move-> c, p -move-> s
2491 |
2491 |
2492 o i-0 initial commit: a b h p q r
2492 o i-0 initial commit: a b h p q r
2493
2493
2494 #if no-changeset
2494 #if no-changeset
2495 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
2495 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
2496 2ff93c643948464ee1f871867910ae43a45b0bea 644 f
2496 2ff93c643948464ee1f871867910ae43a45b0bea 644 f
2497 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
2497 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
2498 2ff93c643948464ee1f871867910ae43a45b0bea 644 f
2498 2ff93c643948464ee1f871867910ae43a45b0bea 644 f
2499 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
2499 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
2500 b76eb76580df486c3d51d63c5c210d4dd43a8ac7 644 f
2500 b76eb76580df486c3d51d63c5c210d4dd43a8ac7 644 f
2501 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
2501 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
2502 e8825b386367b29fec957283a80bb47b47483fe1 644 f
2502 e8825b386367b29fec957283a80bb47b47483fe1 644 f
2503 $ hg debugindex f | "$PYTHON" ../no-linkrev
2503 $ hg debugindex f | "$PYTHON" ../no-linkrev
2504 rev linkrev nodeid p1 p2
2504 rev linkrev nodeid p1 p2
2505 0 * b76eb76580df 000000000000 000000000000
2505 0 * b76eb76580df 000000000000 000000000000
2506 1 * e8825b386367 000000000000 000000000000
2506 1 * e8825b386367 000000000000 000000000000
2507 2 * 2ff93c643948 b76eb76580df e8825b386367
2507 2 * 2ff93c643948 b76eb76580df e8825b386367
2508 3 * 2f649fba7eb2 b76eb76580df e8825b386367
2508 3 * 2f649fba7eb2 b76eb76580df e8825b386367
2509 4 * 774e7c1637d5 e8825b386367 b76eb76580df
2509 4 * 774e7c1637d5 e8825b386367 b76eb76580df
2510 #else
2510 #else
2511 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
2511 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
2512 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2512 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2513 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
2513 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
2514 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2514 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2515 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
2515 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
2516 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2516 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2517 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
2517 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
2518 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2518 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2519 $ hg debugindex f | "$PYTHON" ../no-linkrev
2519 $ hg debugindex f | "$PYTHON" ../no-linkrev
2520 rev linkrev nodeid p1 p2
2520 rev linkrev nodeid p1 p2
2521 0 * ae258f702dfe 000000000000 000000000000
2521 0 * ae258f702dfe 000000000000 000000000000
2522 1 * d3613c1ec831 ae258f702dfe 000000000000
2522 1 * d3613c1ec831 ae258f702dfe 000000000000
2523 2 * 05e03c868bbc ae258f702dfe 000000000000
2523 2 * 05e03c868bbc ae258f702dfe 000000000000
2524 #endif
2524 #endif
2525
2525
2526 # Here the filelog based implementation is not looking at the rename
2526 # Here the filelog based implementation is not looking at the rename
2527 # information (because the file exist on both side). However the changelog
2527 # information (because the file exist on both side). However the changelog
2528 # based on works fine. We have different output.
2528 # based on works fine. We have different output.
2529
2529
2530 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAEm-0")'
2530 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAEm-0")'
2531 M f (no-changeset !)
2531 M f (no-changeset !)
2532 b (no-filelog no-changeset !)
2532 b (no-filelog no-changeset !)
2533 R b
2533 R b
2534 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEAm-0")'
2534 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEAm-0")'
2535 M f (no-changeset !)
2535 M f (no-changeset !)
2536 b (no-filelog no-changeset !)
2536 b (no-filelog no-changeset !)
2537 R b
2537 R b
2538 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAEm-0")'
2538 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAEm-0")'
2539 M f (no-changeset !)
2539 M f (no-changeset !)
2540 d (no-filelog no-changeset !)
2540 d (no-filelog no-changeset !)
2541 R d
2541 R d
2542 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEAm-0")'
2542 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEAm-0")'
2543 M f (no-changeset !)
2543 M f (no-changeset !)
2544 d (no-filelog no-changeset !)
2544 d (no-filelog no-changeset !)
2545 R d
2545 R d
2546 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")'
2546 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")'
2547 A f
2547 A f
2548 d
2548 d
2549 R d
2549 R d
2550 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("e-2")'
2550 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("e-2")'
2551 A f
2551 A f
2552 b
2552 b
2553 R b
2553 R b
2554
2554
2555 # From here, we run status against revision where both source file exists.
2555 # From here, we run status against revision where both source file exists.
2556 #
2556 #
2557 # The filelog based implementation picks an arbitrary side based on revision
2557 # The filelog based implementation picks an arbitrary side based on revision
2558 # numbers. So the same side "wins" whatever the parents order is. This is
2558 # numbers. So the same side "wins" whatever the parents order is. This is
2559 # sub-optimal because depending on revision numbers means the result can be
2559 # sub-optimal because depending on revision numbers means the result can be
2560 # different from one repository to the next.
2560 # different from one repository to the next.
2561 #
2561 #
2562 # The changeset based algorithm use the parent order to break tie on conflicting
2562 # The changeset based algorithm use the parent order to break tie on conflicting
2563 # information and will have a different order depending on who is p1 and p2.
2563 # information and will have a different order depending on who is p1 and p2.
2564 # That order is stable accross repositories. (data from p1 prevails)
2564 # That order is stable accross repositories. (data from p1 prevails)
2565
2565
2566 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAEm-0")'
2566 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAEm-0")'
2567 A f
2567 A f
2568 d
2568 d
2569 R b
2569 R b
2570 R d
2570 R d
2571 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEAm-0")'
2571 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEAm-0")'
2572 A f
2572 A f
2573 d (filelog !)
2573 d (filelog !)
2574 b (no-filelog !)
2574 b (no-filelog !)
2575 R b
2575 R b
2576 R d
2576 R d
2577 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm-0")'
2577 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm-0")'
2578 A f
2578 A f
2579 a
2579 a
2580 A t
2580 A t
2581 p
2581 p
2582 R a
2582 R a
2583 R b
2583 R b
2584 R p
2584 R p
2585 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm-0")'
2585 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm-0")'
2586 A f
2586 A f
2587 a (filelog !)
2587 a (filelog !)
2588 b (no-filelog !)
2588 b (no-filelog !)
2589 A t
2589 A t
2590 p
2590 p
2591 R a
2591 R a
2592 R b
2592 R b
2593 R p
2593 R p
2594
2594
2595
2595
2596 Subcase: existing copy information overwritten on one branch
2596 Subcase: existing copy information overwritten on one branch
2597 ````````````````````````````````````````````````````````````
2597 ````````````````````````````````````````````````````````````
2598
2598
2599 Note:
2599 Note:
2600 | In this case, one of the merge wrongly record a merge while there is none.
2600 | In this case, one of the merge wrongly record a merge while there is none.
2601 | This lead to bad copy tracing information to be dug up.
2601 | This lead to bad copy tracing information to be dug up.
2602
2602
2603
2603
2604 Merge:
2604 Merge:
2605 - one with change to an unrelated file (b)
2605 - one with change to an unrelated file (b)
2606 - one overwriting a file (d) with a rename (from h to i to d)
2606 - one overwriting a file (d) with a rename (from h to i to d)
2607
2607
2608 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
2608 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
2609 o mFBm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
2609 o mFBm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
2610 |\
2610 |\
2611 +---o mBFm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
2611 +---o mBFm-0 simple merge - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
2612 | |/
2612 | |/
2613 | o f-2: rename i -> d
2613 | o f-2: rename i -> d
2614 | |
2614 | |
2615 | o f-1: rename h -> i
2615 | o f-1: rename h -> i
2616 | |
2616 | |
2617 o | b-1: b update
2617 o | b-1: b update
2618 |/
2618 |/
2619 o i-2: c -move-> d, s -move-> t
2619 o i-2: c -move-> d, s -move-> t
2620 |
2620 |
2621 o i-1: a -move-> c, p -move-> s
2621 o i-1: a -move-> c, p -move-> s
2622 |
2622 |
2623 o i-0 initial commit: a b h p q r
2623 o i-0 initial commit: a b h p q r
2624
2624
2625 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBFm-0")'
2625 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBFm-0")'
2626 M b
2626 M b
2627 A d
2627 A d
2628 h
2628 h
2629 A t
2629 A t
2630 p
2630 p
2631 R a
2631 R a
2632 R h
2632 R h
2633 R p
2633 R p
2634 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFBm-0")'
2634 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFBm-0")'
2635 M b
2635 M b
2636 A d
2636 A d
2637 h
2637 h
2638 A t
2638 A t
2639 p
2639 p
2640 R a
2640 R a
2641 R h
2641 R h
2642 R p
2642 R p
2643 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBFm-0")'
2643 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBFm-0")'
2644 M d (no-changeset !)
2644 M d (no-changeset !)
2645 h (no-filelog no-changeset !)
2645 h (no-filelog no-changeset !)
2646 R h
2646 R h
2647 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mBFm-0")'
2647 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mBFm-0")'
2648 M b
2648 M b
2649 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mBFm-0")'
2649 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mBFm-0")'
2650 M b
2650 M b
2651 M d (no-changeset !)
2651 M d (no-changeset !)
2652 i (no-filelog no-changeset !)
2652 i (no-filelog no-changeset !)
2653 R i
2653 R i
2654 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mFBm-0")'
2654 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mFBm-0")'
2655 M d (no-changeset !)
2655 M d (no-changeset !)
2656 h (no-filelog no-changeset !)
2656 h (no-filelog no-changeset !)
2657 R h
2657 R h
2658 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFBm-0")'
2658 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFBm-0")'
2659 M b
2659 M b
2660 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFBm-0")'
2660 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFBm-0")'
2661 M b
2661 M b
2662 M d (no-changeset !)
2662 M d (no-changeset !)
2663 i (no-filelog no-changeset !)
2663 i (no-filelog no-changeset !)
2664 R i
2664 R i
2665
2665
2666 #if no-changeset
2666 #if no-changeset
2667 $ hg log -Gfr 'desc("mBFm-0")' d
2667 $ hg log -Gfr 'desc("mBFm-0")' d
2668 o f-2: rename i -> d
2668 o f-2: rename i -> d
2669 |
2669 |
2670 o f-1: rename h -> i
2670 o f-1: rename h -> i
2671 :
2671 :
2672 o i-0 initial commit: a b h p q r
2672 o i-0 initial commit: a b h p q r
2673
2673
2674 #else
2674 #else
2675 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2675 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2676 $ hg log -Gfr 'desc("mBFm-0")' d
2676 $ hg log -Gfr 'desc("mBFm-0")' d
2677 o i-2: c -move-> d, s -move-> t
2677 o i-2: c -move-> d, s -move-> t
2678 |
2678 |
2679 ~
2679 ~
2680 #endif
2680 #endif
2681
2681
2682 #if no-changeset
2682 #if no-changeset
2683 $ hg log -Gfr 'desc("mFBm-0")' d
2683 $ hg log -Gfr 'desc("mFBm-0")' d
2684 o f-2: rename i -> d
2684 o f-2: rename i -> d
2685 |
2685 |
2686 o f-1: rename h -> i
2686 o f-1: rename h -> i
2687 :
2687 :
2688 o i-0 initial commit: a b h p q r
2688 o i-0 initial commit: a b h p q r
2689
2689
2690 #else
2690 #else
2691 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2691 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2692 $ hg log -Gfr 'desc("mFBm-0")' d
2692 $ hg log -Gfr 'desc("mFBm-0")' d
2693 o i-2: c -move-> d, s -move-> t
2693 o i-2: c -move-> d, s -move-> t
2694 |
2694 |
2695 ~
2695 ~
2696 #endif
2696 #endif
2697
2697
2698
2698
2699 Subcase: existing copy information overwritten on one branch, with different content)
2699 Subcase: existing copy information overwritten on one branch, with different content)
2700 `````````````````````````````````````````````````````````````````````````````````````
2700 `````````````````````````````````````````````````````````````````````````````````````
2701
2701
2702 Merge:
2702 Merge:
2703 - one with change to an unrelated file (b)
2703 - one with change to an unrelated file (b)
2704 - one overwriting a file (t) with a rename (from r to x to t), v content is not the same as on the other branch
2704 - one overwriting a file (t) with a rename (from r to x to t), v content is not the same as on the other branch
2705
2705
2706 $ hg log -G --rev '::(desc("mBRm")+desc("mRBm"))'
2706 $ hg log -G --rev '::(desc("mBRm")+desc("mRBm"))'
2707 o mRBm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - the other way
2707 o mRBm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - the other way
2708 |\
2708 |\
2709 +---o mBRm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - one way
2709 +---o mBRm-0 simple merge - B side: unrelated change, R side: overwrite d with a copy (from r->x->t) different content - one way
2710 | |/
2710 | |/
2711 | o r-2: rename t -> x
2711 | o r-2: rename t -> x
2712 | |
2712 | |
2713 | o r-1: rename r -> x
2713 | o r-1: rename r -> x
2714 | |
2714 | |
2715 o | b-1: b update
2715 o | b-1: b update
2716 |/
2716 |/
2717 o i-2: c -move-> d, s -move-> t
2717 o i-2: c -move-> d, s -move-> t
2718 |
2718 |
2719 o i-1: a -move-> c, p -move-> s
2719 o i-1: a -move-> c, p -move-> s
2720 |
2720 |
2721 o i-0 initial commit: a b h p q r
2721 o i-0 initial commit: a b h p q r
2722
2722
2723 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBRm-0")'
2723 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBRm-0")'
2724 M b
2724 M b
2725 A d
2725 A d
2726 a
2726 a
2727 A t
2727 A t
2728 r
2728 r
2729 R a
2729 R a
2730 R p
2730 R p
2731 R r
2731 R r
2732 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mRBm-0")'
2732 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mRBm-0")'
2733 M b
2733 M b
2734 A d
2734 A d
2735 a
2735 a
2736 A t
2736 A t
2737 r
2737 r
2738 R a
2738 R a
2739 R p
2739 R p
2740 R r
2740 R r
2741 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBRm-0")'
2741 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBRm-0")'
2742 M t
2742 M t
2743 r (no-filelog !)
2743 r (no-filelog !)
2744 R r
2744 R r
2745 $ hg status --copies --rev 'desc("r-2")' --rev 'desc("mBRm-0")'
2745 $ hg status --copies --rev 'desc("r-2")' --rev 'desc("mBRm-0")'
2746 M b
2746 M b
2747 $ hg status --copies --rev 'desc("r-1")' --rev 'desc("mBRm-0")'
2747 $ hg status --copies --rev 'desc("r-1")' --rev 'desc("mBRm-0")'
2748 M b
2748 M b
2749 M t
2749 M t
2750 x (no-filelog !)
2750 x (no-filelog !)
2751 R x
2751 R x
2752 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mRBm-0")'
2752 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mRBm-0")'
2753 M t
2753 M t
2754 r (no-filelog !)
2754 r (no-filelog !)
2755 R r
2755 R r
2756 $ hg status --copies --rev 'desc("r-2")' --rev 'desc("mRBm-0")'
2756 $ hg status --copies --rev 'desc("r-2")' --rev 'desc("mRBm-0")'
2757 M b
2757 M b
2758 $ hg status --copies --rev 'desc("r-1")' --rev 'desc("mRBm-0")'
2758 $ hg status --copies --rev 'desc("r-1")' --rev 'desc("mRBm-0")'
2759 M b
2759 M b
2760 M t
2760 M t
2761 x (no-filelog !)
2761 x (no-filelog !)
2762 R x
2762 R x
2763
2763
2764 #if no-changeset
2764 #if no-changeset
2765 $ hg log -Gfr 'desc("mBRm-0")' d
2765 $ hg log -Gfr 'desc("mBRm-0")' d
2766 o i-2: c -move-> d, s -move-> t
2766 o i-2: c -move-> d, s -move-> t
2767 |
2767 |
2768 o i-1: a -move-> c, p -move-> s
2768 o i-1: a -move-> c, p -move-> s
2769 |
2769 |
2770 o i-0 initial commit: a b h p q r
2770 o i-0 initial commit: a b h p q r
2771
2771
2772 #else
2772 #else
2773 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2773 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2774 $ hg log -Gfr 'desc("mBRm-0")' d
2774 $ hg log -Gfr 'desc("mBRm-0")' d
2775 o i-2: c -move-> d, s -move-> t
2775 o i-2: c -move-> d, s -move-> t
2776 |
2776 |
2777 ~
2777 ~
2778 #endif
2778 #endif
2779
2779
2780 #if no-changeset
2780 #if no-changeset
2781 $ hg log -Gfr 'desc("mRBm-0")' d
2781 $ hg log -Gfr 'desc("mRBm-0")' d
2782 o i-2: c -move-> d, s -move-> t
2782 o i-2: c -move-> d, s -move-> t
2783 |
2783 |
2784 o i-1: a -move-> c, p -move-> s
2784 o i-1: a -move-> c, p -move-> s
2785 |
2785 |
2786 o i-0 initial commit: a b h p q r
2786 o i-0 initial commit: a b h p q r
2787
2787
2788 #else
2788 #else
2789 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2789 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2790 $ hg log -Gfr 'desc("mRBm-0")' d
2790 $ hg log -Gfr 'desc("mRBm-0")' d
2791 o i-2: c -move-> d, s -move-> t
2791 o i-2: c -move-> d, s -move-> t
2792 |
2792 |
2793 ~
2793 ~
2794 #endif
2794 #endif
2795
2795
2796 Subcase: reset of the copy history on one side
2796 Subcase: reset of the copy history on one side
2797 ``````````````````````````````````````````````
2797 ``````````````````````````````````````````````
2798
2798
2799 Merge:
2799 Merge:
2800 - one with change to a file
2800 - one with change to a file
2801 - one deleting and recreating the file
2801 - one deleting and recreating the file
2802
2802
2803 Unlike in the 'BD/DB' cases, an actual merge happened here. So we should
2803 Unlike in the 'BD/DB' cases, an actual merge happened here. So we should
2804 consider history and rename on both branch of the merge.
2804 consider history and rename on both branch of the merge.
2805
2805
2806 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
2806 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
2807 o mGDm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - the other way
2807 o mGDm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - the other way
2808 |\
2808 |\
2809 +---o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2809 +---o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2810 | |/
2810 | |/
2811 | o g-1: update d
2811 | o g-1: update d
2812 | |
2812 | |
2813 o | d-2 re-add d
2813 o | d-2 re-add d
2814 | |
2814 | |
2815 o | d-1 delete d
2815 o | d-1 delete d
2816 |/
2816 |/
2817 o i-2: c -move-> d, s -move-> t
2817 o i-2: c -move-> d, s -move-> t
2818 |
2818 |
2819 o i-1: a -move-> c, p -move-> s
2819 o i-1: a -move-> c, p -move-> s
2820 |
2820 |
2821 o i-0 initial commit: a b h p q r
2821 o i-0 initial commit: a b h p q r
2822
2822
2823 One side of the merge have a long history with rename. The other side of the
2823 One side of the merge have a long history with rename. The other side of the
2824 merge point to a new file with a smaller history. Each side is "valid".
2824 merge point to a new file with a smaller history. Each side is "valid".
2825
2825
2826 (and again the filelog based algorithm only explore one, with a pick based on
2826 (and again the filelog based algorithm only explore one, with a pick based on
2827 revision numbers)
2827 revision numbers)
2828
2828
2829 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDGm-0")'
2829 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDGm-0")'
2830 A d
2830 A d
2831 a (filelog !)
2831 a (filelog !)
2832 A t
2832 A t
2833 p
2833 p
2834 R a
2834 R a
2835 R p
2835 R p
2836 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGDm-0")'
2836 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGDm-0")'
2837 A d
2837 A d
2838 a
2838 a
2839 A t
2839 A t
2840 p
2840 p
2841 R a
2841 R a
2842 R p
2842 R p
2843 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDGm-0")'
2843 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDGm-0")'
2844 M d
2844 M d
2845 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mGDm-0")'
2845 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mGDm-0")'
2846 M d
2846 M d
2847 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mDGm-0")'
2847 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mDGm-0")'
2848 M d
2848 M d
2849 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGDm-0")'
2849 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGDm-0")'
2850 M d
2850 M d
2851
2851
2852 #if no-changeset
2852 #if no-changeset
2853 $ hg log -Gfr 'desc("mDGm-0")' d
2853 $ hg log -Gfr 'desc("mDGm-0")' d
2854 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2854 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2855 |\
2855 |\
2856 | o g-1: update d
2856 | o g-1: update d
2857 | |
2857 | |
2858 o | d-2 re-add d
2858 o | d-2 re-add d
2859 |/
2859 |/
2860 o i-2: c -move-> d, s -move-> t
2860 o i-2: c -move-> d, s -move-> t
2861 |
2861 |
2862 o i-1: a -move-> c, p -move-> s
2862 o i-1: a -move-> c, p -move-> s
2863 |
2863 |
2864 o i-0 initial commit: a b h p q r
2864 o i-0 initial commit: a b h p q r
2865
2865
2866 #else
2866 #else
2867 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2867 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2868 $ hg log -Gfr 'desc("mDGm-0")' d
2868 $ hg log -Gfr 'desc("mDGm-0")' d
2869 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2869 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2870 |\
2870 |\
2871 | o g-1: update d
2871 | o g-1: update d
2872 | |
2872 | |
2873 o | d-2 re-add d
2873 o | d-2 re-add d
2874 |/
2874 |/
2875 o i-2: c -move-> d, s -move-> t
2875 o i-2: c -move-> d, s -move-> t
2876 |
2876 |
2877 ~
2877 ~
2878 #endif
2878 #endif
2879
2879
2880
2880
2881 #if no-changeset
2881 #if no-changeset
2882 $ hg log -Gfr 'desc("mDGm-0")' d
2882 $ hg log -Gfr 'desc("mDGm-0")' d
2883 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2883 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2884 |\
2884 |\
2885 | o g-1: update d
2885 | o g-1: update d
2886 | |
2886 | |
2887 o | d-2 re-add d
2887 o | d-2 re-add d
2888 |/
2888 |/
2889 o i-2: c -move-> d, s -move-> t
2889 o i-2: c -move-> d, s -move-> t
2890 |
2890 |
2891 o i-1: a -move-> c, p -move-> s
2891 o i-1: a -move-> c, p -move-> s
2892 |
2892 |
2893 o i-0 initial commit: a b h p q r
2893 o i-0 initial commit: a b h p q r
2894
2894
2895 #else
2895 #else
2896 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2896 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2897 $ hg log -Gfr 'desc("mDGm-0")' d
2897 $ hg log -Gfr 'desc("mDGm-0")' d
2898 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2898 o mDGm-0 actual content merge, copies on one side - D side: delete and re-add (different content), G side: update content - one way
2899 |\
2899 |\
2900 | o g-1: update d
2900 | o g-1: update d
2901 | |
2901 | |
2902 o | d-2 re-add d
2902 o | d-2 re-add d
2903 |/
2903 |/
2904 o i-2: c -move-> d, s -move-> t
2904 o i-2: c -move-> d, s -move-> t
2905 |
2905 |
2906 ~
2906 ~
2907 #endif
2907 #endif
2908
2908
2909 Subcase: merging a change to a file with a "copy overwrite" to that file from another branch
2909 Subcase: merging a change to a file with a "copy overwrite" to that file from another branch
2910 ````````````````````````````````````````````````````````````````````````````````````````````
2910 ````````````````````````````````````````````````````````````````````````````````````````````
2911
2911
2912 Merge:
2912 Merge:
2913 - one with change to a file (d)
2913 - one with change to a file (d)
2914 - one overwriting that file with a rename (from h to i, to d)
2914 - one overwriting that file with a rename (from h to i, to d)
2915
2915
2916 This case is similar to BF/FB, but an actual merge happens, so both side of the
2916 This case is similar to BF/FB, but an actual merge happens, so both side of the
2917 history are relevant.
2917 history are relevant.
2918
2918
2919
2919
2920 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
2920 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
2921 o mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
2921 o mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
2922 |\
2922 |\
2923 +---o mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
2923 +---o mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
2924 | |/
2924 | |/
2925 | o g-1: update d
2925 | o g-1: update d
2926 | |
2926 | |
2927 o | f-2: rename i -> d
2927 o | f-2: rename i -> d
2928 | |
2928 | |
2929 o | f-1: rename h -> i
2929 o | f-1: rename h -> i
2930 |/
2930 |/
2931 o i-2: c -move-> d, s -move-> t
2931 o i-2: c -move-> d, s -move-> t
2932 |
2932 |
2933 o i-1: a -move-> c, p -move-> s
2933 o i-1: a -move-> c, p -move-> s
2934 |
2934 |
2935 o i-0 initial commit: a b h p q r
2935 o i-0 initial commit: a b h p q r
2936
2936
2937
2937
2938 Note:
2938 Note:
2939 | In this case, the merge get conflicting information since on one side we have
2939 | In this case, the merge get conflicting information since on one side we have
2940 | "a -> c -> d". and one the other one we have "h -> i -> d".
2940 | "a -> c -> d". and one the other one we have "h -> i -> d".
2941 |
2941 |
2942 | The current code arbitrarily pick one side depending the ordering of the merged hash:
2942 | The current code arbitrarily pick one side depending the ordering of the merged hash:
2943
2943
2944 In this case, the file hash from "f-2" is lower, so it will be `p1` of the resulting filenode its copy tracing information will win (and trace back to "h"):
2944 In this case, the file hash from "f-2" is lower, so it will be `p1` of the resulting filenode its copy tracing information will win (and trace back to "h"):
2945
2945
2946 Details on this hash ordering pick:
2946 Details on this hash ordering pick:
2947
2947
2948 $ hg manifest --debug 'desc("g-1")' | egrep 'd$'
2948 $ hg manifest --debug 'desc("g-1")' | egrep 'd$'
2949 17ec97e605773eb44a117d1136b3849bcdc1924f 644 d (no-changeset !)
2949 17ec97e605773eb44a117d1136b3849bcdc1924f 644 d (no-changeset !)
2950 5cce88bf349f7c742bb440f2c53f81db9c294279 644 d (changeset !)
2950 5cce88bf349f7c742bb440f2c53f81db9c294279 644 d (changeset !)
2951 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("g-1")' d
2951 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("g-1")' d
2952 A d
2952 A d
2953 a (no-changeset no-compatibility !)
2953 a (no-changeset no-compatibility !)
2954
2954
2955 $ hg manifest --debug 'desc("f-2")' | egrep 'd$'
2955 $ hg manifest --debug 'desc("f-2")' | egrep 'd$'
2956 7b79e2fe0c8924e0e598a82f048a7b024afa4d96 644 d (no-changeset !)
2956 7b79e2fe0c8924e0e598a82f048a7b024afa4d96 644 d (no-changeset !)
2957 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 d (changeset !)
2957 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 d (changeset !)
2958 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("f-2")' d
2958 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("f-2")' d
2959 A d
2959 A d
2960 h (no-changeset no-compatibility !)
2960 h (no-changeset no-compatibility !)
2961
2961
2962 Copy tracing data on the resulting merge:
2962 Copy tracing data on the resulting merge:
2963
2963
2964 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm-0")'
2964 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm-0")'
2965 A d
2965 A d
2966 h (no-filelog !)
2966 h (no-filelog !)
2967 a (filelog !)
2967 a (filelog !)
2968 A t
2968 A t
2969 p
2969 p
2970 R a
2970 R a
2971 R h
2971 R h
2972 R p
2972 R p
2973 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm-0")'
2973 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm-0")'
2974 A d
2974 A d
2975 a (no-changeset !)
2975 a (no-changeset !)
2976 h (changeset !)
2976 h (changeset !)
2977 A t
2977 A t
2978 p
2978 p
2979 R a
2979 R a
2980 R h
2980 R h
2981 R p
2981 R p
2982 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFGm-0")'
2982 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFGm-0")'
2983 M d
2983 M d
2984 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mGFm-0")'
2984 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mGFm-0")'
2985 M d
2985 M d
2986 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFGm-0")'
2986 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFGm-0")'
2987 M d
2987 M d
2988 i (no-filelog !)
2988 i (no-filelog !)
2989 R i
2989 R i
2990 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mGFm-0")'
2990 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mGFm-0")'
2991 M d
2991 M d
2992 i (no-filelog !)
2992 i (no-filelog !)
2993 R i
2993 R i
2994 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mFGm-0")'
2994 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mFGm-0")'
2995 M d (no-changeset !)
2995 M d (no-changeset !)
2996 h (no-filelog no-changeset !)
2996 h (no-filelog no-changeset !)
2997 R h
2997 R h
2998 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGFm-0")'
2998 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGFm-0")'
2999 M d (no-changeset !)
2999 M d (no-changeset !)
3000 h (no-filelog no-changeset !)
3000 h (no-filelog no-changeset !)
3001 R h
3001 R h
3002
3002
3003 #if no-changeset
3003 #if no-changeset
3004 $ hg log -Gfr 'desc("mFGm-0")' d
3004 $ hg log -Gfr 'desc("mFGm-0")' d
3005 o mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
3005 o mFGm-0 merge - G side: content change, F side: copy overwrite, no content change - one way
3006 |\
3006 |\
3007 | o g-1: update d
3007 | o g-1: update d
3008 | |
3008 | |
3009 o | f-2: rename i -> d
3009 o | f-2: rename i -> d
3010 | |
3010 | |
3011 o | f-1: rename h -> i
3011 o | f-1: rename h -> i
3012 |/
3012 |/
3013 o i-2: c -move-> d, s -move-> t
3013 o i-2: c -move-> d, s -move-> t
3014 |
3014 |
3015 o i-1: a -move-> c, p -move-> s
3015 o i-1: a -move-> c, p -move-> s
3016 |
3016 |
3017 o i-0 initial commit: a b h p q r
3017 o i-0 initial commit: a b h p q r
3018
3018
3019 #else
3019 #else
3020 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3020 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3021 $ hg log -Gfr 'desc("mFGm-0")' d
3021 $ hg log -Gfr 'desc("mFGm-0")' d
3022 o g-1: update d
3022 o g-1: update d
3023 |
3023 |
3024 o i-2: c -move-> d, s -move-> t
3024 o i-2: c -move-> d, s -move-> t
3025 |
3025 |
3026 ~
3026 ~
3027 #endif
3027 #endif
3028
3028
3029 #if no-changeset
3029 #if no-changeset
3030 $ hg log -Gfr 'desc("mGFm-0")' d
3030 $ hg log -Gfr 'desc("mGFm-0")' d
3031 o mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
3031 o mGFm-0 merge - G side: content change, F side: copy overwrite, no content change - the other way
3032 |\
3032 |\
3033 | o g-1: update d
3033 | o g-1: update d
3034 | |
3034 | |
3035 o | f-2: rename i -> d
3035 o | f-2: rename i -> d
3036 | |
3036 | |
3037 o | f-1: rename h -> i
3037 o | f-1: rename h -> i
3038 |/
3038 |/
3039 o i-2: c -move-> d, s -move-> t
3039 o i-2: c -move-> d, s -move-> t
3040 |
3040 |
3041 o i-1: a -move-> c, p -move-> s
3041 o i-1: a -move-> c, p -move-> s
3042 |
3042 |
3043 o i-0 initial commit: a b h p q r
3043 o i-0 initial commit: a b h p q r
3044
3044
3045 #else
3045 #else
3046 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3046 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3047 $ hg log -Gfr 'desc("mGFm-0")' d
3047 $ hg log -Gfr 'desc("mGFm-0")' d
3048 o g-1: update d
3048 o g-1: update d
3049 |
3049 |
3050 o i-2: c -move-> d, s -move-> t
3050 o i-2: c -move-> d, s -move-> t
3051 |
3051 |
3052 ~
3052 ~
3053 #endif
3053 #endif
3054
3054
3055 Subcase: new copy information on both side with an actual merge happening
3055 Subcase: new copy information on both side with an actual merge happening
3056 `````````````````````````````````````````````````````````````````````````
3056 `````````````````````````````````````````````````````````````````````````
3057
3057
3058 - the "p-" branch renaming 't' to 'v' (through 'u')
3058 - the "p-" branch renaming 't' to 'v' (through 'u')
3059 - the "q-" branch renaming 'r' to 'v' (through 'w')
3059 - the "q-" branch renaming 'r' to 'v' (through 'w')
3060
3060
3061
3061
3062 $ hg log -G --rev '::(desc("mPQm")+desc("mQPm"))'
3062 $ hg log -G --rev '::(desc("mPQm")+desc("mQPm"))'
3063 o mQPm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - the other way
3063 o mQPm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - the other way
3064 |\
3064 |\
3065 +---o mPQm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - one way
3065 +---o mPQm-0 merge with copies info on both side - P side: rename t to v, Q side: r to v, (different content) - one way
3066 | |/
3066 | |/
3067 | o q-2 w -move-> v
3067 | o q-2 w -move-> v
3068 | |
3068 | |
3069 | o q-1 r -move-> w
3069 | o q-1 r -move-> w
3070 | |
3070 | |
3071 o | p-2: u -move-> v
3071 o | p-2: u -move-> v
3072 | |
3072 | |
3073 o | p-1: t -move-> u
3073 o | p-1: t -move-> u
3074 |/
3074 |/
3075 o i-2: c -move-> d, s -move-> t
3075 o i-2: c -move-> d, s -move-> t
3076 |
3076 |
3077 o i-1: a -move-> c, p -move-> s
3077 o i-1: a -move-> c, p -move-> s
3078 |
3078 |
3079 o i-0 initial commit: a b h p q r
3079 o i-0 initial commit: a b h p q r
3080
3080
3081
3081
3082 #if no-changeset
3082 #if no-changeset
3083 $ hg manifest --debug --rev 'desc("mPQm-0")' | grep '644 v'
3083 $ hg manifest --debug --rev 'desc("mPQm-0")' | grep '644 v'
3084 0946c662ef16e4e67397fd717389eb6693d41749 644 v
3084 0946c662ef16e4e67397fd717389eb6693d41749 644 v
3085 $ hg manifest --debug --rev 'desc("mQPm-0")' | grep '644 v'
3085 $ hg manifest --debug --rev 'desc("mQPm-0")' | grep '644 v'
3086 0db3aad7fcc1ec27fab57060e327b9e864ea0cc9 644 v
3086 0db3aad7fcc1ec27fab57060e327b9e864ea0cc9 644 v
3087 $ hg manifest --debug --rev 'desc("p-2")' | grep '644 v'
3087 $ hg manifest --debug --rev 'desc("p-2")' | grep '644 v'
3088 3f91841cd75cadc9a1f1b4e7c1aa6d411f76032e 644 v
3088 3f91841cd75cadc9a1f1b4e7c1aa6d411f76032e 644 v
3089 $ hg manifest --debug --rev 'desc("q-2")' | grep '644 v'
3089 $ hg manifest --debug --rev 'desc("q-2")' | grep '644 v'
3090 c43c088b811fd27983c0a9aadf44f3343cd4cd7e 644 v
3090 c43c088b811fd27983c0a9aadf44f3343cd4cd7e 644 v
3091 $ hg debugindex v | "$PYTHON" ../no-linkrev
3091 $ hg debugindex v | "$PYTHON" ../no-linkrev
3092 rev linkrev nodeid p1 p2
3092 rev linkrev nodeid p1 p2
3093 0 * 3f91841cd75c 000000000000 000000000000
3093 0 * 3f91841cd75c 000000000000 000000000000
3094 1 * c43c088b811f 000000000000 000000000000
3094 1 * c43c088b811f 000000000000 000000000000
3095 2 * 0946c662ef16 3f91841cd75c c43c088b811f
3095 2 * 0946c662ef16 3f91841cd75c c43c088b811f
3096 3 * 0db3aad7fcc1 c43c088b811f 3f91841cd75c
3096 3 * 0db3aad7fcc1 c43c088b811f 3f91841cd75c
3097 #else
3097 #else
3098 $ hg manifest --debug --rev 'desc("mPQm-0")' | grep '644 v'
3098 $ hg manifest --debug --rev 'desc("mPQm-0")' | grep '644 v'
3099 65fde9f6e4d4da23b3f610e07b53673ea9541d75 644 v
3099 65fde9f6e4d4da23b3f610e07b53673ea9541d75 644 v
3100 $ hg manifest --debug --rev 'desc("mQPm-0")' | grep '644 v'
3100 $ hg manifest --debug --rev 'desc("mQPm-0")' | grep '644 v'
3101 a098dda6413aecf154eefc976afc38b295acb7e5 644 v
3101 a098dda6413aecf154eefc976afc38b295acb7e5 644 v
3102 $ hg manifest --debug --rev 'desc("p-2")' | grep '644 v'
3102 $ hg manifest --debug --rev 'desc("p-2")' | grep '644 v'
3103 5aed6a8dbff0301328c08360d24354d3d064cf0d 644 v
3103 5aed6a8dbff0301328c08360d24354d3d064cf0d 644 v
3104 $ hg manifest --debug --rev 'desc("q-2")' | grep '644 v'
3104 $ hg manifest --debug --rev 'desc("q-2")' | grep '644 v'
3105 a38b2fa170219750dac9bc7d19df831f213ba708 644 v
3105 a38b2fa170219750dac9bc7d19df831f213ba708 644 v
3106 $ hg debugindex v | "$PYTHON" ../no-linkrev
3106 $ hg debugindex v | "$PYTHON" ../no-linkrev
3107 rev linkrev nodeid p1 p2
3107 rev linkrev nodeid p1 p2
3108 0 * 5aed6a8dbff0 000000000000 000000000000
3108 0 * 5aed6a8dbff0 000000000000 000000000000
3109 1 * a38b2fa17021 000000000000 000000000000
3109 1 * a38b2fa17021 000000000000 000000000000
3110 2 * 65fde9f6e4d4 5aed6a8dbff0 a38b2fa17021
3110 2 * 65fde9f6e4d4 5aed6a8dbff0 a38b2fa17021
3111 3 * a098dda6413a a38b2fa17021 5aed6a8dbff0
3111 3 * a098dda6413a a38b2fa17021 5aed6a8dbff0
3112 #endif
3112 #endif
3113
3113
3114 # Here the filelog based implementation is not looking at the rename
3114 # Here the filelog based implementation is not looking at the rename
3115 # information (because the file exist on both side). However the changelog
3115 # information (because the file exist on both side). However the changelog
3116 # based on works fine. We have different output.
3116 # based on works fine. We have different output.
3117
3117
3118 $ hg status --copies --rev 'desc("p-2")' --rev 'desc("mPQm-0")'
3118 $ hg status --copies --rev 'desc("p-2")' --rev 'desc("mPQm-0")'
3119 M v
3119 M v
3120 r (no-filelog !)
3120 r (no-filelog !)
3121 R r
3121 R r
3122 $ hg status --copies --rev 'desc("p-2")' --rev 'desc("mQPm-0")'
3122 $ hg status --copies --rev 'desc("p-2")' --rev 'desc("mQPm-0")'
3123 M v
3123 M v
3124 r (no-filelog !)
3124 r (no-filelog !)
3125 R r
3125 R r
3126 $ hg status --copies --rev 'desc("q-2")' --rev 'desc("mPQm-0")'
3126 $ hg status --copies --rev 'desc("q-2")' --rev 'desc("mPQm-0")'
3127 M v
3127 M v
3128 t (no-filelog !)
3128 t (no-filelog !)
3129 R t
3129 R t
3130 $ hg status --copies --rev 'desc("q-2")' --rev 'desc("mQPm-0")'
3130 $ hg status --copies --rev 'desc("q-2")' --rev 'desc("mQPm-0")'
3131 M v
3131 M v
3132 t (no-filelog !)
3132 t (no-filelog !)
3133 R t
3133 R t
3134 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("p-2")'
3134 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("p-2")'
3135 A v
3135 A v
3136 t
3136 t
3137 R t
3137 R t
3138 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("q-2")'
3138 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("q-2")'
3139 A v
3139 A v
3140 r
3140 r
3141 R r
3141 R r
3142
3142
3143 # From here, we run status against revision where both source file exists.
3143 # From here, we run status against revision where both source file exists.
3144 #
3144 #
3145 # The filelog based implementation picks an arbitrary side based on revision
3145 # The filelog based implementation picks an arbitrary side based on revision
3146 # numbers. So the same side "wins" whatever the parents order is. This is
3146 # numbers. So the same side "wins" whatever the parents order is. This is
3147 # sub-optimal because depending on revision numbers means the result can be
3147 # sub-optimal because depending on revision numbers means the result can be
3148 # different from one repository to the next.
3148 # different from one repository to the next.
3149 #
3149 #
3150 # The changeset based algorithm use the parent order to break tie on conflicting
3150 # The changeset based algorithm use the parent order to break tie on conflicting
3151 # information and will have a different order depending on who is p1 and p2.
3151 # information and will have a different order depending on who is p1 and p2.
3152 # That order is stable accross repositories. (data from p1 prevails)
3152 # That order is stable accross repositories. (data from p1 prevails)
3153
3153
3154 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mPQm-0")'
3154 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mPQm-0")'
3155 A v
3155 A v
3156 t
3156 t
3157 R r
3157 R r
3158 R t
3158 R t
3159 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mQPm-0")'
3159 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mQPm-0")'
3160 A v
3160 A v
3161 t (filelog !)
3161 t (filelog !)
3162 r (no-filelog !)
3162 r (no-filelog !)
3163 R r
3163 R r
3164 R t
3164 R t
3165 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mPQm-0")'
3165 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mPQm-0")'
3166 A d
3166 A d
3167 a
3167 a
3168 A v
3168 A v
3169 r (filelog !)
3169 r (filelog !)
3170 p (no-filelog !)
3170 p (no-filelog !)
3171 R a
3171 R a
3172 R p
3172 R p
3173 R r
3173 R r
3174 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mQPm-0")'
3174 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mQPm-0")'
3175 A d
3175 A d
3176 a
3176 a
3177 A v
3177 A v
3178 r
3178 r
3179 R a
3179 R a
3180 R p
3180 R p
3181 R r
3181 R r
3182
3182
3183
3183
3184 Comparing with merging with a deletion (and keeping the file)
3184 Comparing with merging with a deletion (and keeping the file)
3185 -------------------------------------------------------------
3185 -------------------------------------------------------------
3186
3186
3187 Merge:
3187 Merge:
3188 - one removing a file (d)
3188 - one removing a file (d)
3189 - one updating that file
3189 - one updating that file
3190 - the merge keep the modified version of the file (canceling the delete)
3190 - the merge keep the modified version of the file (canceling the delete)
3191
3191
3192 In this case, the file keep on living after the merge. So we should not drop its
3192 In this case, the file keep on living after the merge. So we should not drop its
3193 copy tracing chain.
3193 copy tracing chain.
3194
3194
3195 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
3195 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
3196 o mGCm-0 merge updated/deleted - revive the file (updated content) - the other way
3196 o mGCm-0 merge updated/deleted - revive the file (updated content) - the other way
3197 |\
3197 |\
3198 +---o mCGm-0 merge updated/deleted - revive the file (updated content) - one way
3198 +---o mCGm-0 merge updated/deleted - revive the file (updated content) - one way
3199 | |/
3199 | |/
3200 | o g-1: update d
3200 | o g-1: update d
3201 | |
3201 | |
3202 o | c-1 delete d
3202 o | c-1 delete d
3203 |/
3203 |/
3204 o i-2: c -move-> d, s -move-> t
3204 o i-2: c -move-> d, s -move-> t
3205 |
3205 |
3206 o i-1: a -move-> c, p -move-> s
3206 o i-1: a -move-> c, p -move-> s
3207 |
3207 |
3208 o i-0 initial commit: a b h p q r
3208 o i-0 initial commit: a b h p q r
3209
3209
3210
3210
3211 'a' is the copy source of 'd'
3211 'a' is the copy source of 'd'
3212
3212
3213 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCGm-0")'
3213 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCGm-0")'
3214 A d
3214 A d
3215 a (no-compatibility no-changeset !)
3215 a (no-compatibility no-changeset !)
3216 A t
3216 A t
3217 p
3217 p
3218 R a
3218 R a
3219 R p
3219 R p
3220 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGCm-0")'
3220 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGCm-0")'
3221 A d
3221 A d
3222 a (no-compatibility no-changeset !)
3222 a (no-compatibility no-changeset !)
3223 A t
3223 A t
3224 p
3224 p
3225 R a
3225 R a
3226 R p
3226 R p
3227 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCGm-0")'
3227 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCGm-0")'
3228 A d
3228 A d
3229 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mGCm-0")'
3229 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mGCm-0")'
3230 A d
3230 A d
3231 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mCGm-0")'
3231 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mCGm-0")'
3232 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGCm-0")'
3232 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGCm-0")'
3233
3233
3234
3234
3235 Comparing with merge restoring an untouched deleted file
3235 Comparing with merge restoring an untouched deleted file
3236 --------------------------------------------------------
3236 --------------------------------------------------------
3237
3237
3238 Merge:
3238 Merge:
3239 - one removing a file (d)
3239 - one removing a file (d)
3240 - one leaving the file untouched
3240 - one leaving the file untouched
3241 - the merge actively restore the file to the same content.
3241 - the merge actively restore the file to the same content.
3242
3242
3243 In this case, the file keep on living after the merge. So we should not drop its
3243 In this case, the file keep on living after the merge. So we should not drop its
3244 copy tracing chain.
3244 copy tracing chain.
3245
3245
3246 $ hg log -G --rev '::(desc("mCB-revert-m")+desc("mBC-revert-m"))'
3246 $ hg log -G --rev '::(desc("mCB-revert-m")+desc("mBC-revert-m"))'
3247 o mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
3247 o mBC-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
3248 |\
3248 |\
3249 +---o mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
3249 +---o mCB-revert-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
3250 | |/
3250 | |/
3251 | o c-1 delete d
3251 | o c-1 delete d
3252 | |
3252 | |
3253 o | b-1: b update
3253 o | b-1: b update
3254 |/
3254 |/
3255 o i-2: c -move-> d, s -move-> t
3255 o i-2: c -move-> d, s -move-> t
3256 |
3256 |
3257 o i-1: a -move-> c, p -move-> s
3257 o i-1: a -move-> c, p -move-> s
3258 |
3258 |
3259 o i-0 initial commit: a b h p q r
3259 o i-0 initial commit: a b h p q r
3260
3260
3261
3261
3262 'a' is the the copy source of 'd'
3262 'a' is the the copy source of 'd'
3263
3263
3264 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB-revert-m-0")'
3264 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB-revert-m-0")'
3265 M b
3265 M b
3266 A d
3266 A d
3267 a (no-compatibility no-changeset !)
3267 a (no-compatibility no-changeset !)
3268 A t
3268 A t
3269 p
3269 p
3270 R a
3270 R a
3271 R p
3271 R p
3272 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC-revert-m-0")'
3272 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC-revert-m-0")'
3273 M b
3273 M b
3274 A d
3274 A d
3275 a (no-compatibility no-changeset !)
3275 a (no-compatibility no-changeset !)
3276 A t
3276 A t
3277 p
3277 p
3278 R a
3278 R a
3279 R p
3279 R p
3280 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCB-revert-m-0")'
3280 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCB-revert-m-0")'
3281 M b
3281 M b
3282 A d
3282 A d
3283 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBC-revert-m-0")'
3283 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBC-revert-m-0")'
3284 M b
3284 M b
3285 A d
3285 A d
3286 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCB-revert-m-0")'
3286 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCB-revert-m-0")'
3287 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBC-revert-m-0")'
3287 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBC-revert-m-0")'
3288
3288
3289
3289
3290 Merging a branch where a rename was deleted with a branch where the same file was renamed
3290 Merging a branch where a rename was deleted with a branch where the same file was renamed
3291 ------------------------------------------------------------------------------------------
3291 ------------------------------------------------------------------------------------------
3292
3292
3293 Create a "conflicting" merge where `d` get removed on one branch before its
3293 Create a "conflicting" merge where `d` get removed on one branch before its
3294 rename information actually conflict with the other branch.
3294 rename information actually conflict with the other branch.
3295
3295
3296 (the copy information from the branch that was not deleted should win).
3296 (the copy information from the branch that was not deleted should win).
3297
3297
3298 $ hg log -G --rev '::(desc("mCH-delete-before-conflict-m")+desc("mHC-delete-before-conflict-m"))'
3298 $ hg log -G --rev '::(desc("mCH-delete-before-conflict-m")+desc("mHC-delete-before-conflict-m"))'
3299 o mHC-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - the other way
3299 o mHC-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - the other way
3300 |\
3300 |\
3301 +---o mCH-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - one way
3301 +---o mCH-delete-before-conflict-m-0 simple merge - C side: d is the results of renames then deleted, H side: d is result of another rename (same content as the other branch) - one way
3302 | |/
3302 | |/
3303 | o h-1: b -(move)-> d
3303 | o h-1: b -(move)-> d
3304 | |
3304 | |
3305 o | c-1 delete d
3305 o | c-1 delete d
3306 | |
3306 | |
3307 o | i-2: c -move-> d, s -move-> t
3307 o | i-2: c -move-> d, s -move-> t
3308 | |
3308 | |
3309 o | i-1: a -move-> c, p -move-> s
3309 o | i-1: a -move-> c, p -move-> s
3310 |/
3310 |/
3311 o i-0 initial commit: a b h p q r
3311 o i-0 initial commit: a b h p q r
3312
3312
3313
3313
3314 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCH-delete-before-conflict-m")'
3314 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCH-delete-before-conflict-m")'
3315 A d
3315 A d
3316 b (no-compatibility no-changeset !)
3316 b (no-compatibility no-changeset !)
3317 A t
3317 A t
3318 p
3318 p
3319 R a
3319 R a
3320 R b
3320 R b
3321 R p
3321 R p
3322 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mHC-delete-before-conflict-m")'
3322 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mHC-delete-before-conflict-m")'
3323 A d
3323 A d
3324 b
3324 b
3325 A t
3325 A t
3326 p
3326 p
3327 R a
3327 R a
3328 R b
3328 R b
3329 R p
3329 R p
3330 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCH-delete-before-conflict-m")'
3330 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCH-delete-before-conflict-m")'
3331 A d
3331 A d
3332 b
3332 b
3333 R b
3333 R b
3334 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mHC-delete-before-conflict-m")'
3334 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mHC-delete-before-conflict-m")'
3335 A d
3335 A d
3336 b
3336 b
3337 R b
3337 R b
3338 $ hg status --copies --rev 'desc("h-1")' --rev 'desc("mCH-delete-before-conflict-m")'
3338 $ hg status --copies --rev 'desc("h-1")' --rev 'desc("mCH-delete-before-conflict-m")'
3339 A t
3339 A t
3340 p
3340 p
3341 R a
3341 R a
3342 R p
3342 R p
3343 $ hg status --copies --rev 'desc("h-1")' --rev 'desc("mHC-delete-before-conflict-m")'
3343 $ hg status --copies --rev 'desc("h-1")' --rev 'desc("mHC-delete-before-conflict-m")'
3344 A t
3344 A t
3345 p
3345 p
3346 R a
3346 R a
3347 R p
3347 R p
3348
3348
3349 Variant of previous with extra changes introduced by the merge
3349 Variant of previous with extra changes introduced by the merge
3350 --------------------------------------------------------------
3350 --------------------------------------------------------------
3351
3351
3352 (see case declaration for details)
3352 (see case declaration for details)
3353
3353
3354 Subcase: merge has same initial content on both side, but merge introduced a change
3354 Subcase: merge has same initial content on both side, but merge introduced a change
3355 ```````````````````````````````````````````````````````````````````````````````````
3355 ```````````````````````````````````````````````````````````````````````````````````
3356
3356
3357 - the "e-" branch renaming b to f (through 'g')
3357 - the "e-" branch renaming b to f (through 'g')
3358 - the "a-" branch renaming d to f (through e)
3358 - the "a-" branch renaming d to f (through e)
3359 - the merge add new change to b
3359 - the merge add new change to b
3360
3360
3361 $ hg log -G --rev '::(desc("mAE-change-m")+desc("mEA-change-m"))'
3361 $ hg log -G --rev '::(desc("mAE-change-m")+desc("mEA-change-m"))'
3362 o mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
3362 o mEA-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - the other way
3363 |\
3363 |\
3364 +---o mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
3364 +---o mAE-change-m-0 merge with file update and copies info on both side - A side: rename d to f, E side: b to f, (same content for f in parent) - one way
3365 | |/
3365 | |/
3366 | o e-2 g -move-> f
3366 | o e-2 g -move-> f
3367 | |
3367 | |
3368 | o e-1 b -move-> g
3368 | o e-1 b -move-> g
3369 | |
3369 | |
3370 o | a-2: e -move-> f
3370 o | a-2: e -move-> f
3371 | |
3371 | |
3372 o | a-1: d -move-> e
3372 o | a-1: d -move-> e
3373 |/
3373 |/
3374 o i-2: c -move-> d, s -move-> t
3374 o i-2: c -move-> d, s -move-> t
3375 |
3375 |
3376 o i-1: a -move-> c, p -move-> s
3376 o i-1: a -move-> c, p -move-> s
3377 |
3377 |
3378 o i-0 initial commit: a b h p q r
3378 o i-0 initial commit: a b h p q r
3379
3379
3380 #if no-changeset
3380 #if no-changeset
3381 $ hg manifest --debug --rev 'desc("mAE-change-m-0")' | grep '644 f'
3381 $ hg manifest --debug --rev 'desc("mAE-change-m-0")' | grep '644 f'
3382 2f649fba7eb284e720d02b61f0546fcef694c045 644 f
3382 2f649fba7eb284e720d02b61f0546fcef694c045 644 f
3383 $ hg manifest --debug --rev 'desc("mEA-change-m-0")' | grep '644 f'
3383 $ hg manifest --debug --rev 'desc("mEA-change-m-0")' | grep '644 f'
3384 774e7c1637d536b99e2d8ef16fd731f87a82bd09 644 f
3384 774e7c1637d536b99e2d8ef16fd731f87a82bd09 644 f
3385 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
3385 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
3386 b76eb76580df486c3d51d63c5c210d4dd43a8ac7 644 f
3386 b76eb76580df486c3d51d63c5c210d4dd43a8ac7 644 f
3387 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
3387 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
3388 e8825b386367b29fec957283a80bb47b47483fe1 644 f
3388 e8825b386367b29fec957283a80bb47b47483fe1 644 f
3389 $ hg debugindex f | "$PYTHON" ../no-linkrev
3389 $ hg debugindex f | "$PYTHON" ../no-linkrev
3390 rev linkrev nodeid p1 p2
3390 rev linkrev nodeid p1 p2
3391 0 * b76eb76580df 000000000000 000000000000
3391 0 * b76eb76580df 000000000000 000000000000
3392 1 * e8825b386367 000000000000 000000000000
3392 1 * e8825b386367 000000000000 000000000000
3393 2 * 2ff93c643948 b76eb76580df e8825b386367
3393 2 * 2ff93c643948 b76eb76580df e8825b386367
3394 3 * 2f649fba7eb2 b76eb76580df e8825b386367
3394 3 * 2f649fba7eb2 b76eb76580df e8825b386367
3395 4 * 774e7c1637d5 e8825b386367 b76eb76580df
3395 4 * 774e7c1637d5 e8825b386367 b76eb76580df
3396 #else
3396 #else
3397 $ hg manifest --debug --rev 'desc("mAE-change-m-0")' | grep '644 f'
3397 $ hg manifest --debug --rev 'desc("mAE-change-m-0")' | grep '644 f'
3398 d3613c1ec8310a812ac4268fd853ac576b6caea5 644 f
3398 d3613c1ec8310a812ac4268fd853ac576b6caea5 644 f
3399 $ hg manifest --debug --rev 'desc("mEA-change-m-0")' | grep '644 f'
3399 $ hg manifest --debug --rev 'desc("mEA-change-m-0")' | grep '644 f'
3400 05e03c868bbcab4a649cb33a238d7aa07398a469 644 f
3400 05e03c868bbcab4a649cb33a238d7aa07398a469 644 f
3401 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
3401 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
3402 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
3402 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
3403 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
3403 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
3404 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
3404 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
3405 $ hg debugindex f | "$PYTHON" ../no-linkrev
3405 $ hg debugindex f | "$PYTHON" ../no-linkrev
3406 rev linkrev nodeid p1 p2
3406 rev linkrev nodeid p1 p2
3407 0 * ae258f702dfe 000000000000 000000000000
3407 0 * ae258f702dfe 000000000000 000000000000
3408 1 * d3613c1ec831 ae258f702dfe 000000000000
3408 1 * d3613c1ec831 ae258f702dfe 000000000000
3409 2 * 05e03c868bbc ae258f702dfe 000000000000
3409 2 * 05e03c868bbc ae258f702dfe 000000000000
3410 #endif
3410 #endif
3411
3411
3412 # Here the filelog based implementation is not looking at the rename
3412 # Here the filelog based implementation is not looking at the rename
3413 # information (because the file exist on both side). However the changelog
3413 # information (because the file exist on both side). However the changelog
3414 # based on works fine. We have different output.
3414 # based on works fine. We have different output.
3415
3415
3416 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAE-change-m-0")'
3416 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAE-change-m-0")'
3417 M f
3417 M f
3418 b (no-filelog !)
3418 b (no-filelog !)
3419 R b
3419 R b
3420 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEA-change-m-0")'
3420 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEA-change-m-0")'
3421 M f
3421 M f
3422 b (no-filelog !)
3422 b (no-filelog !)
3423 R b
3423 R b
3424 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAE-change-m-0")'
3424 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAE-change-m-0")'
3425 M f
3425 M f
3426 d (no-filelog !)
3426 d (no-filelog !)
3427 R d
3427 R d
3428 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEA-change-m-0")'
3428 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEA-change-m-0")'
3429 M f
3429 M f
3430 d (no-filelog !)
3430 d (no-filelog !)
3431 R d
3431 R d
3432 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")'
3432 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")'
3433 A f
3433 A f
3434 d
3434 d
3435 R d
3435 R d
3436 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("e-2")'
3436 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("e-2")'
3437 A f
3437 A f
3438 b
3438 b
3439 R b
3439 R b
3440
3440
3441 # From here, we run status against revision where both source file exists.
3441 # From here, we run status against revision where both source file exists.
3442 #
3442 #
3443 # The filelog based implementation picks an arbitrary side based on revision
3443 # The filelog based implementation picks an arbitrary side based on revision
3444 # numbers. So the same side "wins" whatever the parents order is. This is
3444 # numbers. So the same side "wins" whatever the parents order is. This is
3445 # sub-optimal because depending on revision numbers means the result can be
3445 # sub-optimal because depending on revision numbers means the result can be
3446 # different from one repository to the next.
3446 # different from one repository to the next.
3447 #
3447 #
3448 # The changeset based algorithm use the parent order to break tie on conflicting
3448 # The changeset based algorithm use the parent order to break tie on conflicting
3449 # information and will have a different order depending on who is p1 and p2.
3449 # information and will have a different order depending on who is p1 and p2.
3450 # That order is stable accross repositories. (data from p1 prevails)
3450 # That order is stable accross repositories. (data from p1 prevails)
3451
3451
3452 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAE-change-m-0")'
3452 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAE-change-m-0")'
3453 A f
3453 A f
3454 d
3454 d
3455 R b
3455 R b
3456 R d
3456 R d
3457 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEA-change-m-0")'
3457 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEA-change-m-0")'
3458 A f
3458 A f
3459 d (filelog !)
3459 d (filelog !)
3460 b (no-filelog !)
3460 b (no-filelog !)
3461 R b
3461 R b
3462 R d
3462 R d
3463 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE-change-m-0")'
3463 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE-change-m-0")'
3464 A f
3464 A f
3465 a
3465 a
3466 A t
3466 A t
3467 p
3467 p
3468 R a
3468 R a
3469 R b
3469 R b
3470 R p
3470 R p
3471 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA-change-m-0")'
3471 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA-change-m-0")'
3472 A f
3472 A f
3473 a (filelog !)
3473 a (filelog !)
3474 b (no-filelog !)
3474 b (no-filelog !)
3475 A t
3475 A t
3476 p
3476 p
3477 R a
3477 R a
3478 R b
3478 R b
3479 R p
3479 R p
3480
3480
3481
3481
3482 Subcase: merge overwrite common copy information, but with extra change during the merge
3482 Subcase: merge overwrite common copy information, but with extra change during the merge
3483 ```````````````````````````````````````````````````````````````````````````````````
3483 ```````````````````````````````````````````````````````````````````````````````````
3484
3484
3485 Merge:
3485 Merge:
3486 - one with change to an unrelated file (b)
3486 - one with change to an unrelated file (b)
3487 - one overwriting a file (d) with a rename (from h to i to d)
3487 - one overwriting a file (d) with a rename (from h to i to d)
3488
3488
3489 $ hg log -G --rev '::(desc("mBF-change-m")+desc("mFB-change-m"))'
3489 $ hg log -G --rev '::(desc("mBF-change-m")+desc("mFB-change-m"))'
3490 o mFB-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
3490 o mFB-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
3491 |\
3491 |\
3492 +---o mBF-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
3492 +---o mBF-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
3493 | |/
3493 | |/
3494 | o f-2: rename i -> d
3494 | o f-2: rename i -> d
3495 | |
3495 | |
3496 | o f-1: rename h -> i
3496 | o f-1: rename h -> i
3497 | |
3497 | |
3498 o | b-1: b update
3498 o | b-1: b update
3499 |/
3499 |/
3500 o i-2: c -move-> d, s -move-> t
3500 o i-2: c -move-> d, s -move-> t
3501 |
3501 |
3502 o i-1: a -move-> c, p -move-> s
3502 o i-1: a -move-> c, p -move-> s
3503 |
3503 |
3504 o i-0 initial commit: a b h p q r
3504 o i-0 initial commit: a b h p q r
3505
3505
3506 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBF-change-m-0")'
3506 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBF-change-m-0")'
3507 M b
3507 M b
3508 A d
3508 A d
3509 h
3509 h
3510 A t
3510 A t
3511 p
3511 p
3512 R a
3512 R a
3513 R h
3513 R h
3514 R p
3514 R p
3515 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFB-change-m-0")'
3515 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFB-change-m-0")'
3516 M b
3516 M b
3517 A d
3517 A d
3518 h
3518 h
3519 A t
3519 A t
3520 p
3520 p
3521 R a
3521 R a
3522 R h
3522 R h
3523 R p
3523 R p
3524 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBF-change-m-0")'
3524 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBF-change-m-0")'
3525 M d
3525 M d
3526 h (no-filelog !)
3526 h (no-filelog !)
3527 R h
3527 R h
3528 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mBF-change-m-0")'
3528 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mBF-change-m-0")'
3529 M b
3529 M b
3530 M d
3530 M d
3531 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mBF-change-m-0")'
3531 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mBF-change-m-0")'
3532 M b
3532 M b
3533 M d
3533 M d
3534 i (no-filelog !)
3534 i (no-filelog !)
3535 R i
3535 R i
3536 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mFB-change-m-0")'
3536 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mFB-change-m-0")'
3537 M d
3537 M d
3538 h (no-filelog !)
3538 h (no-filelog !)
3539 R h
3539 R h
3540 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFB-change-m-0")'
3540 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFB-change-m-0")'
3541 M b
3541 M b
3542 M d
3542 M d
3543 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFB-change-m-0")'
3543 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFB-change-m-0")'
3544 M b
3544 M b
3545 M d
3545 M d
3546 i (no-filelog !)
3546 i (no-filelog !)
3547 R i
3547 R i
3548
3548
3549 #if no-changeset
3549 #if no-changeset
3550 $ hg log -Gfr 'desc("mBF-change-m-0")' d
3550 $ hg log -Gfr 'desc("mBF-change-m-0")' d
3551 o mBF-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
3551 o mBF-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
3552 |\
3552 |\
3553 o : f-2: rename i -> d
3553 o : f-2: rename i -> d
3554 | :
3554 | :
3555 o : f-1: rename h -> i
3555 o : f-1: rename h -> i
3556 :/
3556 :/
3557 o i-0 initial commit: a b h p q r
3557 o i-0 initial commit: a b h p q r
3558
3558
3559 #else
3559 #else
3560 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3560 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3561 $ hg log -Gfr 'desc("mBF-change-m-0")' d
3561 $ hg log -Gfr 'desc("mBF-change-m-0")' d
3562 o mBF-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
3562 o mBF-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - one way
3563 :
3563 :
3564 o i-2: c -move-> d, s -move-> t
3564 o i-2: c -move-> d, s -move-> t
3565 |
3565 |
3566 ~
3566 ~
3567 #endif
3567 #endif
3568
3568
3569 #if no-changeset
3569 #if no-changeset
3570 $ hg log -Gfr 'desc("mFB-change-m-0")' d
3570 $ hg log -Gfr 'desc("mFB-change-m-0")' d
3571 o mFB-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
3571 o mFB-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
3572 |\
3572 |\
3573 o : f-2: rename i -> d
3573 o : f-2: rename i -> d
3574 | :
3574 | :
3575 o : f-1: rename h -> i
3575 o : f-1: rename h -> i
3576 :/
3576 :/
3577 o i-0 initial commit: a b h p q r
3577 o i-0 initial commit: a b h p q r
3578
3578
3579 #else
3579 #else
3580 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3580 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3581 $ hg log -Gfr 'desc("mFB-change-m-0")' d
3581 $ hg log -Gfr 'desc("mFB-change-m-0")' d
3582 o mFB-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
3582 o mFB-change-m-0 merge with extra change - B side: unrelated change, F side: overwrite d with a copy (from h->i->d) - the other way
3583 :
3583 :
3584 o i-2: c -move-> d, s -move-> t
3584 o i-2: c -move-> d, s -move-> t
3585 |
3585 |
3586 ~
3586 ~
3587 #endif
3587 #endif
3588
3588
3589
3589
3590 Subcase: restoring and untouched deleted file, while touching it
3590 Subcase: restoring and untouched deleted file, while touching it
3591 ````````````````````````````````````````````````````````````````
3591 ````````````````````````````````````````````````````````````````
3592
3592
3593 Merge:
3593 Merge:
3594 - one removing a file (d)
3594 - one removing a file (d)
3595 - one leaving the file untouched
3595 - one leaving the file untouched
3596 - the merge actively restore the file to the same content.
3596 - the merge actively restore the file to the same content.
3597
3597
3598 In this case, the file keep on living after the merge. So we should not drop its
3598 In this case, the file keep on living after the merge. So we should not drop its
3599 copy tracing chain.
3599 copy tracing chain.
3600
3600
3601 $ hg log -G --rev '::(desc("mCB-change-m")+desc("mBC-change-m"))'
3601 $ hg log -G --rev '::(desc("mCB-change-m")+desc("mBC-change-m"))'
3602 o mBC-change-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
3602 o mBC-change-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - the other way
3603 |\
3603 |\
3604 +---o mCB-change-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
3604 +---o mCB-change-m-0 merge explicitely revive deleted file - B side: unrelated change, C side: delete d (restored by merge) - one way
3605 | |/
3605 | |/
3606 | o c-1 delete d
3606 | o c-1 delete d
3607 | |
3607 | |
3608 o | b-1: b update
3608 o | b-1: b update
3609 |/
3609 |/
3610 o i-2: c -move-> d, s -move-> t
3610 o i-2: c -move-> d, s -move-> t
3611 |
3611 |
3612 o i-1: a -move-> c, p -move-> s
3612 o i-1: a -move-> c, p -move-> s
3613 |
3613 |
3614 o i-0 initial commit: a b h p q r
3614 o i-0 initial commit: a b h p q r
3615
3615
3616
3616
3617 'a' is the the copy source of 'd'
3617 'a' is the the copy source of 'd'
3618
3618
3619 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB-change-m-0")'
3619 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB-change-m-0")'
3620 M b
3620 M b
3621 A d
3621 A d
3622 a (no-compatibility no-changeset !)
3622 a (no-compatibility no-changeset !)
3623 A t
3623 A t
3624 p
3624 p
3625 R a
3625 R a
3626 R p
3626 R p
3627 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC-change-m-0")'
3627 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC-change-m-0")'
3628 M b
3628 M b
3629 A d
3629 A d
3630 a (no-compatibility no-changeset !)
3630 a (no-compatibility no-changeset !)
3631 A t
3631 A t
3632 p
3632 p
3633 R a
3633 R a
3634 R p
3634 R p
3635 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCB-change-m-0")'
3635 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCB-change-m-0")'
3636 M b
3636 M b
3637 A d
3637 A d
3638 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBC-change-m-0")'
3638 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBC-change-m-0")'
3639 M b
3639 M b
3640 A d
3640 A d
3641 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCB-change-m-0")'
3641 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCB-change-m-0")'
3642 M d
3642 M d
3643 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBC-change-m-0")'
3643 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBC-change-m-0")'
3644 M d
3644 M d
3645
3645
3646
3646
3647 Decision from previous merge are properly chained with later merge
3647 Decision from previous merge are properly chained with later merge
3648 ------------------------------------------------------------------
3648 ------------------------------------------------------------------
3649
3649
3650
3650
3651 Subcase: chaining conflicting rename resolution
3651 Subcase: chaining conflicting rename resolution
3652 ```````````````````````````````````````````````
3652 ```````````````````````````````````````````````
3653
3653
3654 The "mAEm" and "mEAm" case create a rename tracking conflict on file 'f'. We
3654 The "mAEm" and "mEAm" case create a rename tracking conflict on file 'f'. We
3655 add more change on the respective branch and merge again. These second merge
3655 add more change on the respective branch and merge again. These second merge
3656 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
3656 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
3657 about that file should stay unchanged.
3657 about that file should stay unchanged.
3658
3658
3659 The result from mAEm is the same for the subsequent merge:
3659 The result from mAEm is the same for the subsequent merge:
3660
3660
3661 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm")' f
3661 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm")' f
3662 A f
3662 A f
3663 a (no-changeset no-compatibility !)
3663 a (no-changeset no-compatibility !)
3664
3664
3665 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE,Km")' f
3665 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE,Km")' f
3666 A f
3666 A f
3667 a (no-changeset no-compatibility !)
3667 a (no-changeset no-compatibility !)
3668
3668
3669 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mK,AEm")' f
3669 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mK,AEm")' f
3670 A f
3670 A f
3671 a (no-changeset no-compatibility !)
3671 a (no-changeset no-compatibility !)
3672
3672
3673
3673
3674 The result from mEAm is the same for the subsequent merge:
3674 The result from mEAm is the same for the subsequent merge:
3675
3675
3676 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm")' f
3676 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm")' f
3677 A f
3677 A f
3678 a (filelog !)
3678 a (filelog !)
3679 b (no-changeset no-compatibility no-filelog !)
3679 b (no-changeset no-compatibility no-filelog !)
3680
3680
3681 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA,Jm")' f
3681 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA,Jm")' f
3682 A f
3682 A f
3683 a (filelog !)
3683 a (filelog !)
3684 b (no-changeset no-compatibility no-filelog !)
3684 b (no-changeset no-compatibility no-filelog !)
3685
3685
3686 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mJ,EAm")' f
3686 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mJ,EAm")' f
3687 A f
3687 A f
3688 a (filelog !)
3688 a (filelog !)
3689 b (no-changeset no-compatibility no-filelog !)
3689 b (no-changeset no-compatibility no-filelog !)
3690
3690
3691 Subcase: chaining conflicting rename resolution
3691 Subcase: chaining conflicting rename resolution
3692 ```````````````````````````````````````````````
3692 ```````````````````````````````````````````````
3693
3693
3694 The "mPQm" and "mQPm" case create a rename tracking conflict on file 'v'. We
3694 The "mPQm" and "mQPm" case create a rename tracking conflict on file 'v'. We
3695 add more change on the respective branch and merge again. These second merge
3695 add more change on the respective branch and merge again. These second merge
3696 does not involve the file 'v' and the arbitration done within "mPQm" and "mQP"
3696 does not involve the file 'v' and the arbitration done within "mPQm" and "mQP"
3697 about that file should stay unchanged.
3697 about that file should stay unchanged.
3698
3698
3699 The result from mPQm is the same for the subsequent merge:
3699 The result from mPQm is the same for the subsequent merge:
3700
3700
3701 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mPQm")' v
3701 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mPQm")' v
3702 A v
3702 A v
3703 r (filelog !)
3703 r (filelog !)
3704 p (no-changeset no-compatibility no-filelog !)
3704 p (no-changeset no-compatibility no-filelog !)
3705
3705
3706 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mPQ,Tm")' v
3706 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mPQ,Tm")' v
3707 A v
3707 A v
3708 r (filelog !)
3708 r (filelog !)
3709 p (no-changeset no-compatibility no-filelog !)
3709 p (no-changeset no-compatibility no-filelog !)
3710
3710
3711 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mT,PQm")' v
3711 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mT,PQm")' v
3712 A v
3712 A v
3713 r (filelog !)
3713 r (filelog !)
3714 p (no-changeset no-compatibility no-filelog !)
3714 p (no-changeset no-compatibility no-filelog !)
3715
3715
3716 The result from mQPm is the same for the subsequent merge:
3716 The result from mQPm is the same for the subsequent merge:
3717
3717
3718 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mQPm")' v
3718 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mQPm")' v
3719 A v
3719 A v
3720 r (no-changeset no-compatibility !)
3720 r (no-changeset no-compatibility !)
3721
3721
3722 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mQP,Sm")' v
3722 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mQP,Sm")' v
3723 A v
3723 A v
3724 r (no-changeset no-compatibility !)
3724 r (no-changeset no-compatibility !)
3725
3725
3726 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mS,QPm")' v
3726 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mS,QPm")' v
3727 A v
3727 A v
3728 r (filelog !)
3728 r (filelog !)
3729 r (no-changeset no-compatibility no-filelog !)
3729 r (no-changeset no-compatibility no-filelog !)
3730
3730
3731
3731
3732 Subcase: chaining salvage information during a merge
3732 Subcase: chaining salvage information during a merge
3733 ````````````````````````````````````````````````````
3733 ````````````````````````````````````````````````````
3734
3734
3735 We add more change on the branch were the file was deleted. merging again
3735 We add more change on the branch were the file was deleted. merging again
3736 should preserve the fact eh file was salvaged.
3736 should preserve the fact eh file was salvaged.
3737
3737
3738 reference output:
3738 reference output:
3739
3739
3740 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB-revert-m-0")'
3740 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB-revert-m-0")'
3741 M b
3741 M b
3742 A d
3742 A d
3743 a (no-changeset no-compatibility !)
3743 a (no-changeset no-compatibility !)
3744 A t
3744 A t
3745 p
3745 p
3746 R a
3746 R a
3747 R p
3747 R p
3748 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC-revert-m-0")'
3748 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC-revert-m-0")'
3749 M b
3749 M b
3750 A d
3750 A d
3751 a (no-changeset no-compatibility !)
3751 a (no-changeset no-compatibility !)
3752 A t
3752 A t
3753 p
3753 p
3754 R a
3754 R a
3755 R p
3755 R p
3756
3756
3757 chained output
3757 chained output
3758 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC+revert,Lm")'
3758 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC+revert,Lm")'
3759 M b
3759 M b
3760 A d
3760 A d
3761 a (no-changeset no-compatibility !)
3761 a (no-changeset no-compatibility !)
3762 A t
3762 A t
3763 p
3763 p
3764 A unrelated-l
3764 A unrelated-l
3765 R a
3765 R a
3766 R p
3766 R p
3767 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB+revert,Lm")'
3767 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB+revert,Lm")'
3768 M b
3768 M b
3769 A d
3769 A d
3770 a (no-changeset no-compatibility !)
3770 a (no-changeset no-compatibility !)
3771 A t
3771 A t
3772 p
3772 p
3773 A unrelated-l
3773 A unrelated-l
3774 R a
3774 R a
3775 R p
3775 R p
3776 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mL,BC+revertm")'
3776 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mL,BC+revertm")'
3777 M b
3777 M b
3778 A d
3778 A d
3779 a (no-changeset no-compatibility !)
3779 a (no-changeset no-compatibility !)
3780 A t
3780 A t
3781 p
3781 p
3782 A unrelated-l
3782 A unrelated-l
3783 R a
3783 R a
3784 R p
3784 R p
3785 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mL,CB+revertm")'
3785 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mL,CB+revertm")'
3786 M b
3786 M b
3787 A d
3787 A d
3788 a (no-changeset no-compatibility !)
3788 a (no-changeset no-compatibility !)
3789 A t
3789 A t
3790 p
3790 p
3791 A unrelated-l
3791 A unrelated-l
3792 R a
3792 R a
3793 R p
3793 R p
3794
3794
3795 Subcase: chaining "merged" information during a merge
3795 Subcase: chaining "merged" information during a merge
3796 ``````````````````````````````````````````````````````
3796 ``````````````````````````````````````````````````````
3797
3797
3798 When a non-rename change are merged with a copy overwrite, the merge pick the copy source from (p1) as the reference. We should preserve this information in subsequent merges.
3798 When a non-rename change are merged with a copy overwrite, the merge pick the copy source from (p1) as the reference. We should preserve this information in subsequent merges.
3799
3799
3800
3800
3801 reference output:
3801 reference output:
3802
3802
3803 (for details about the filelog pick, check the mFGm/mGFm case)
3803 (for details about the filelog pick, check the mFGm/mGFm case)
3804
3804
3805 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm")' d
3805 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm")' d
3806 A d
3806 A d
3807 a (filelog !)
3807 a (filelog !)
3808 h (no-changeset no-compatibility no-filelog !)
3808 h (no-changeset no-compatibility no-filelog !)
3809 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm")' d
3809 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm")' d
3810 A d
3810 A d
3811 a (filelog !)
3811 a (filelog !)
3812 a (no-changeset no-compatibility no-filelog !)
3812 a (no-changeset no-compatibility no-filelog !)
3813
3813
3814 Chained output
3814 Chained output
3815
3815
3816 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mO,FGm")' d
3816 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mO,FGm")' d
3817 A d
3817 A d
3818 a (filelog !)
3818 a (filelog !)
3819 h (no-changeset no-compatibility no-filelog !)
3819 h (no-changeset no-compatibility no-filelog !)
3820 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFG,Om")' d
3820 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFG,Om")' d
3821 A d
3821 A d
3822 a (filelog !)
3822 a (filelog !)
3823 h (no-changeset no-compatibility no-filelog !)
3823 h (no-changeset no-compatibility no-filelog !)
3824
3824
3825
3825
3826 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGF,Nm")' d
3826 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGF,Nm")' d
3827 A d
3827 A d
3828 a (no-changeset no-compatibility !)
3828 a (no-changeset no-compatibility !)
3829 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mN,GFm")' d
3829 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mN,GFm")' d
3830 A d
3830 A d
3831 a (no-changeset no-compatibility !)
3831 a (no-changeset no-compatibility !)
3832
3832
3833
3833
3834 Subcase: chaining conflicting rename resolution, with extra change during the merge
3834 Subcase: chaining conflicting rename resolution, with extra change during the merge
3835 ```````````````````````````````````````````````````````````````````````````````````
3835 ```````````````````````````````````````````````````````````````````````````````````
3836
3836
3837 The "mAEm" and "mEAm" case create a rename tracking conflict on file 'f'. We
3837 The "mAEm" and "mEAm" case create a rename tracking conflict on file 'f'. We
3838 add more change on the respective branch and merge again. These second merge
3838 add more change on the respective branch and merge again. These second merge
3839 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
3839 does not involve the file 'f' and the arbitration done within "mAEm" and "mEA"
3840 about that file should stay unchanged.
3840 about that file should stay unchanged.
3841
3841
3842 The result from mAEm is the same for the subsequent merge:
3842 The result from mAEm is the same for the subsequent merge:
3843
3843
3844 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE-change-m")' f
3844 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE-change-m")' f
3845 A f
3845 A f
3846 a (no-changeset no-compatibility !)
3846 a (no-changeset no-compatibility !)
3847
3847
3848 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE-change,Km")' f
3848 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAE-change,Km")' f
3849 A f
3849 A f
3850 a (no-changeset no-compatibility !)
3850 a (no-changeset no-compatibility !)
3851
3851
3852 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mK,AE-change-m")' f
3852 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mK,AE-change-m")' f
3853 A f
3853 A f
3854 a (no-changeset no-compatibility !)
3854 a (no-changeset no-compatibility !)
3855
3855
3856
3856
3857 The result from mEAm is the same for the subsequent merge:
3857 The result from mEAm is the same for the subsequent merge:
3858
3858
3859 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA-change-m")' f
3859 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA-change-m")' f
3860 A f
3860 A f
3861 a (filelog !)
3861 a (filelog !)
3862 b (no-changeset no-compatibility no-filelog !)
3862 b (no-changeset no-compatibility no-filelog !)
3863
3863
3864 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA-change,Jm")' f
3864 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEA-change,Jm")' f
3865 A f
3865 A f
3866 a (filelog !)
3866 a (filelog !)
3867 b (no-changeset no-compatibility no-filelog !)
3867 b (no-changeset no-compatibility no-filelog !)
3868
3868
3869 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mJ,EA-change-m")' f
3869 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mJ,EA-change-m")' f
3870 A f
3870 A f
3871 a (filelog !)
3871 a (filelog !)
3872 b (no-changeset no-compatibility no-filelog !)
3872 b (no-changeset no-compatibility no-filelog !)
General Comments 0
You need to be logged in to leave comments. Login now