##// END OF EJS Templates
changelogv2: `copies-side-data` now implies `changelogv2`...
marmoute -
r48039:a0738175 default
parent child Browse files
Show More
@@ -1,3781 +1,3780 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 (
740 if (
741 requirementsmod.REVLOGV2_REQUIREMENT in requirements
741 requirementsmod.REVLOGV2_REQUIREMENT in requirements
742 or requirementsmod.CHANGELOGV2_REQUIREMENT in requirements
742 or requirementsmod.CHANGELOGV2_REQUIREMENT in requirements
743 ):
743 ):
744 features.add(repository.REPO_FEATURE_SIDE_DATA)
744 features.add(repository.REPO_FEATURE_SIDE_DATA)
745 # the revlogv2 docket introduced race condition that we need to fix
745 # the revlogv2 docket introduced race condition that we need to fix
746 features.discard(repository.REPO_FEATURE_STREAM_CLONE)
746 features.discard(repository.REPO_FEATURE_STREAM_CLONE)
747
747
748 # The cache vfs is used to manage cache files.
748 # The cache vfs is used to manage cache files.
749 cachevfs = vfsmod.vfs(cachepath, cacheaudited=True)
749 cachevfs = vfsmod.vfs(cachepath, cacheaudited=True)
750 cachevfs.createmode = store.createmode
750 cachevfs.createmode = store.createmode
751 # The cache vfs is used to manage cache files related to the working copy
751 # The cache vfs is used to manage cache files related to the working copy
752 wcachevfs = vfsmod.vfs(wcachepath, cacheaudited=True)
752 wcachevfs = vfsmod.vfs(wcachepath, cacheaudited=True)
753 wcachevfs.createmode = store.createmode
753 wcachevfs.createmode = store.createmode
754
754
755 # Now resolve the type for the repository object. We do this by repeatedly
755 # Now resolve the type for the repository object. We do this by repeatedly
756 # calling a factory function to produces types for specific aspects of the
756 # calling a factory function to produces types for specific aspects of the
757 # repo's operation. The aggregate returned types are used as base classes
757 # repo's operation. The aggregate returned types are used as base classes
758 # for a dynamically-derived type, which will represent our new repository.
758 # for a dynamically-derived type, which will represent our new repository.
759
759
760 bases = []
760 bases = []
761 extrastate = {}
761 extrastate = {}
762
762
763 for iface, fn in REPO_INTERFACES:
763 for iface, fn in REPO_INTERFACES:
764 # We pass all potentially useful state to give extensions tons of
764 # We pass all potentially useful state to give extensions tons of
765 # flexibility.
765 # flexibility.
766 typ = fn()(
766 typ = fn()(
767 ui=ui,
767 ui=ui,
768 intents=intents,
768 intents=intents,
769 requirements=requirements,
769 requirements=requirements,
770 features=features,
770 features=features,
771 wdirvfs=wdirvfs,
771 wdirvfs=wdirvfs,
772 hgvfs=hgvfs,
772 hgvfs=hgvfs,
773 store=store,
773 store=store,
774 storevfs=storevfs,
774 storevfs=storevfs,
775 storeoptions=storevfs.options,
775 storeoptions=storevfs.options,
776 cachevfs=cachevfs,
776 cachevfs=cachevfs,
777 wcachevfs=wcachevfs,
777 wcachevfs=wcachevfs,
778 extensionmodulenames=extensionmodulenames,
778 extensionmodulenames=extensionmodulenames,
779 extrastate=extrastate,
779 extrastate=extrastate,
780 baseclasses=bases,
780 baseclasses=bases,
781 )
781 )
782
782
783 if not isinstance(typ, type):
783 if not isinstance(typ, type):
784 raise error.ProgrammingError(
784 raise error.ProgrammingError(
785 b'unable to construct type for %s' % iface
785 b'unable to construct type for %s' % iface
786 )
786 )
787
787
788 bases.append(typ)
788 bases.append(typ)
789
789
790 # type() allows you to use characters in type names that wouldn't be
790 # type() allows you to use characters in type names that wouldn't be
791 # recognized as Python symbols in source code. We abuse that to add
791 # recognized as Python symbols in source code. We abuse that to add
792 # rich information about our constructed repo.
792 # rich information about our constructed repo.
793 name = pycompat.sysstr(
793 name = pycompat.sysstr(
794 b'derivedrepo:%s<%s>' % (wdirvfs.base, b','.join(sorted(requirements)))
794 b'derivedrepo:%s<%s>' % (wdirvfs.base, b','.join(sorted(requirements)))
795 )
795 )
796
796
797 cls = type(name, tuple(bases), {})
797 cls = type(name, tuple(bases), {})
798
798
799 return cls(
799 return cls(
800 baseui=baseui,
800 baseui=baseui,
801 ui=ui,
801 ui=ui,
802 origroot=path,
802 origroot=path,
803 wdirvfs=wdirvfs,
803 wdirvfs=wdirvfs,
804 hgvfs=hgvfs,
804 hgvfs=hgvfs,
805 requirements=requirements,
805 requirements=requirements,
806 supportedrequirements=supportedrequirements,
806 supportedrequirements=supportedrequirements,
807 sharedpath=storebasepath,
807 sharedpath=storebasepath,
808 store=store,
808 store=store,
809 cachevfs=cachevfs,
809 cachevfs=cachevfs,
810 wcachevfs=wcachevfs,
810 wcachevfs=wcachevfs,
811 features=features,
811 features=features,
812 intents=intents,
812 intents=intents,
813 )
813 )
814
814
815
815
816 def loadhgrc(ui, wdirvfs, hgvfs, requirements, sharedvfs=None):
816 def loadhgrc(ui, wdirvfs, hgvfs, requirements, sharedvfs=None):
817 """Load hgrc files/content into a ui instance.
817 """Load hgrc files/content into a ui instance.
818
818
819 This is called during repository opening to load any additional
819 This is called during repository opening to load any additional
820 config files or settings relevant to the current repository.
820 config files or settings relevant to the current repository.
821
821
822 Returns a bool indicating whether any additional configs were loaded.
822 Returns a bool indicating whether any additional configs were loaded.
823
823
824 Extensions should monkeypatch this function to modify how per-repo
824 Extensions should monkeypatch this function to modify how per-repo
825 configs are loaded. For example, an extension may wish to pull in
825 configs are loaded. For example, an extension may wish to pull in
826 configs from alternate files or sources.
826 configs from alternate files or sources.
827
827
828 sharedvfs is vfs object pointing to source repo if the current one is a
828 sharedvfs is vfs object pointing to source repo if the current one is a
829 shared one
829 shared one
830 """
830 """
831 if not rcutil.use_repo_hgrc():
831 if not rcutil.use_repo_hgrc():
832 return False
832 return False
833
833
834 ret = False
834 ret = False
835 # first load config from shared source if we has to
835 # first load config from shared source if we has to
836 if requirementsmod.SHARESAFE_REQUIREMENT in requirements and sharedvfs:
836 if requirementsmod.SHARESAFE_REQUIREMENT in requirements and sharedvfs:
837 try:
837 try:
838 ui.readconfig(sharedvfs.join(b'hgrc'), root=sharedvfs.base)
838 ui.readconfig(sharedvfs.join(b'hgrc'), root=sharedvfs.base)
839 ret = True
839 ret = True
840 except IOError:
840 except IOError:
841 pass
841 pass
842
842
843 try:
843 try:
844 ui.readconfig(hgvfs.join(b'hgrc'), root=wdirvfs.base)
844 ui.readconfig(hgvfs.join(b'hgrc'), root=wdirvfs.base)
845 ret = True
845 ret = True
846 except IOError:
846 except IOError:
847 pass
847 pass
848
848
849 try:
849 try:
850 ui.readconfig(hgvfs.join(b'hgrc-not-shared'), root=wdirvfs.base)
850 ui.readconfig(hgvfs.join(b'hgrc-not-shared'), root=wdirvfs.base)
851 ret = True
851 ret = True
852 except IOError:
852 except IOError:
853 pass
853 pass
854
854
855 return ret
855 return ret
856
856
857
857
858 def afterhgrcload(ui, wdirvfs, hgvfs, requirements):
858 def afterhgrcload(ui, wdirvfs, hgvfs, requirements):
859 """Perform additional actions after .hg/hgrc is loaded.
859 """Perform additional actions after .hg/hgrc is loaded.
860
860
861 This function is called during repository loading immediately after
861 This function is called during repository loading immediately after
862 the .hg/hgrc file is loaded and before per-repo extensions are loaded.
862 the .hg/hgrc file is loaded and before per-repo extensions are loaded.
863
863
864 The function can be used to validate configs, automatically add
864 The function can be used to validate configs, automatically add
865 options (including extensions) based on requirements, etc.
865 options (including extensions) based on requirements, etc.
866 """
866 """
867
867
868 # Map of requirements to list of extensions to load automatically when
868 # Map of requirements to list of extensions to load automatically when
869 # requirement is present.
869 # requirement is present.
870 autoextensions = {
870 autoextensions = {
871 b'git': [b'git'],
871 b'git': [b'git'],
872 b'largefiles': [b'largefiles'],
872 b'largefiles': [b'largefiles'],
873 b'lfs': [b'lfs'],
873 b'lfs': [b'lfs'],
874 }
874 }
875
875
876 for requirement, names in sorted(autoextensions.items()):
876 for requirement, names in sorted(autoextensions.items()):
877 if requirement not in requirements:
877 if requirement not in requirements:
878 continue
878 continue
879
879
880 for name in names:
880 for name in names:
881 if not ui.hasconfig(b'extensions', name):
881 if not ui.hasconfig(b'extensions', name):
882 ui.setconfig(b'extensions', name, b'', source=b'autoload')
882 ui.setconfig(b'extensions', name, b'', source=b'autoload')
883
883
884
884
885 def gathersupportedrequirements(ui):
885 def gathersupportedrequirements(ui):
886 """Determine the complete set of recognized requirements."""
886 """Determine the complete set of recognized requirements."""
887 # Start with all requirements supported by this file.
887 # Start with all requirements supported by this file.
888 supported = set(localrepository._basesupported)
888 supported = set(localrepository._basesupported)
889
889
890 # Execute ``featuresetupfuncs`` entries if they belong to an extension
890 # Execute ``featuresetupfuncs`` entries if they belong to an extension
891 # relevant to this ui instance.
891 # relevant to this ui instance.
892 modules = {m.__name__ for n, m in extensions.extensions(ui)}
892 modules = {m.__name__ for n, m in extensions.extensions(ui)}
893
893
894 for fn in featuresetupfuncs:
894 for fn in featuresetupfuncs:
895 if fn.__module__ in modules:
895 if fn.__module__ in modules:
896 fn(ui, supported)
896 fn(ui, supported)
897
897
898 # Add derived requirements from registered compression engines.
898 # Add derived requirements from registered compression engines.
899 for name in util.compengines:
899 for name in util.compengines:
900 engine = util.compengines[name]
900 engine = util.compengines[name]
901 if engine.available() and engine.revlogheader():
901 if engine.available() and engine.revlogheader():
902 supported.add(b'exp-compression-%s' % name)
902 supported.add(b'exp-compression-%s' % name)
903 if engine.name() == b'zstd':
903 if engine.name() == b'zstd':
904 supported.add(b'revlog-compression-zstd')
904 supported.add(b'revlog-compression-zstd')
905
905
906 return supported
906 return supported
907
907
908
908
909 def ensurerequirementsrecognized(requirements, supported):
909 def ensurerequirementsrecognized(requirements, supported):
910 """Validate that a set of local requirements is recognized.
910 """Validate that a set of local requirements is recognized.
911
911
912 Receives a set of requirements. Raises an ``error.RepoError`` if there
912 Receives a set of requirements. Raises an ``error.RepoError`` if there
913 exists any requirement in that set that currently loaded code doesn't
913 exists any requirement in that set that currently loaded code doesn't
914 recognize.
914 recognize.
915
915
916 Returns a set of supported requirements.
916 Returns a set of supported requirements.
917 """
917 """
918 missing = set()
918 missing = set()
919
919
920 for requirement in requirements:
920 for requirement in requirements:
921 if requirement in supported:
921 if requirement in supported:
922 continue
922 continue
923
923
924 if not requirement or not requirement[0:1].isalnum():
924 if not requirement or not requirement[0:1].isalnum():
925 raise error.RequirementError(_(b'.hg/requires file is corrupt'))
925 raise error.RequirementError(_(b'.hg/requires file is corrupt'))
926
926
927 missing.add(requirement)
927 missing.add(requirement)
928
928
929 if missing:
929 if missing:
930 raise error.RequirementError(
930 raise error.RequirementError(
931 _(b'repository requires features unknown to this Mercurial: %s')
931 _(b'repository requires features unknown to this Mercurial: %s')
932 % b' '.join(sorted(missing)),
932 % b' '.join(sorted(missing)),
933 hint=_(
933 hint=_(
934 b'see https://mercurial-scm.org/wiki/MissingRequirement '
934 b'see https://mercurial-scm.org/wiki/MissingRequirement '
935 b'for more information'
935 b'for more information'
936 ),
936 ),
937 )
937 )
938
938
939
939
940 def ensurerequirementscompatible(ui, requirements):
940 def ensurerequirementscompatible(ui, requirements):
941 """Validates that a set of recognized requirements is mutually compatible.
941 """Validates that a set of recognized requirements is mutually compatible.
942
942
943 Some requirements may not be compatible with others or require
943 Some requirements may not be compatible with others or require
944 config options that aren't enabled. This function is called during
944 config options that aren't enabled. This function is called during
945 repository opening to ensure that the set of requirements needed
945 repository opening to ensure that the set of requirements needed
946 to open a repository is sane and compatible with config options.
946 to open a repository is sane and compatible with config options.
947
947
948 Extensions can monkeypatch this function to perform additional
948 Extensions can monkeypatch this function to perform additional
949 checking.
949 checking.
950
950
951 ``error.RepoError`` should be raised on failure.
951 ``error.RepoError`` should be raised on failure.
952 """
952 """
953 if (
953 if (
954 requirementsmod.SPARSE_REQUIREMENT in requirements
954 requirementsmod.SPARSE_REQUIREMENT in requirements
955 and not sparse.enabled
955 and not sparse.enabled
956 ):
956 ):
957 raise error.RepoError(
957 raise error.RepoError(
958 _(
958 _(
959 b'repository is using sparse feature but '
959 b'repository is using sparse feature but '
960 b'sparse is not enabled; enable the '
960 b'sparse is not enabled; enable the '
961 b'"sparse" extensions to access'
961 b'"sparse" extensions to access'
962 )
962 )
963 )
963 )
964
964
965
965
966 def makestore(requirements, path, vfstype):
966 def makestore(requirements, path, vfstype):
967 """Construct a storage object for a repository."""
967 """Construct a storage object for a repository."""
968 if requirementsmod.STORE_REQUIREMENT in requirements:
968 if requirementsmod.STORE_REQUIREMENT in requirements:
969 if requirementsmod.FNCACHE_REQUIREMENT in requirements:
969 if requirementsmod.FNCACHE_REQUIREMENT in requirements:
970 dotencode = requirementsmod.DOTENCODE_REQUIREMENT in requirements
970 dotencode = requirementsmod.DOTENCODE_REQUIREMENT in requirements
971 return storemod.fncachestore(path, vfstype, dotencode)
971 return storemod.fncachestore(path, vfstype, dotencode)
972
972
973 return storemod.encodedstore(path, vfstype)
973 return storemod.encodedstore(path, vfstype)
974
974
975 return storemod.basicstore(path, vfstype)
975 return storemod.basicstore(path, vfstype)
976
976
977
977
978 def resolvestorevfsoptions(ui, requirements, features):
978 def resolvestorevfsoptions(ui, requirements, features):
979 """Resolve the options to pass to the store vfs opener.
979 """Resolve the options to pass to the store vfs opener.
980
980
981 The returned dict is used to influence behavior of the storage layer.
981 The returned dict is used to influence behavior of the storage layer.
982 """
982 """
983 options = {}
983 options = {}
984
984
985 if requirementsmod.TREEMANIFEST_REQUIREMENT in requirements:
985 if requirementsmod.TREEMANIFEST_REQUIREMENT in requirements:
986 options[b'treemanifest'] = True
986 options[b'treemanifest'] = True
987
987
988 # experimental config: format.manifestcachesize
988 # experimental config: format.manifestcachesize
989 manifestcachesize = ui.configint(b'format', b'manifestcachesize')
989 manifestcachesize = ui.configint(b'format', b'manifestcachesize')
990 if manifestcachesize is not None:
990 if manifestcachesize is not None:
991 options[b'manifestcachesize'] = manifestcachesize
991 options[b'manifestcachesize'] = manifestcachesize
992
992
993 # In the absence of another requirement superseding a revlog-related
993 # In the absence of another requirement superseding a revlog-related
994 # requirement, we have to assume the repo is using revlog version 0.
994 # requirement, we have to assume the repo is using revlog version 0.
995 # This revlog format is super old and we don't bother trying to parse
995 # This revlog format is super old and we don't bother trying to parse
996 # opener options for it because those options wouldn't do anything
996 # opener options for it because those options wouldn't do anything
997 # meaningful on such old repos.
997 # meaningful on such old repos.
998 if (
998 if (
999 requirementsmod.REVLOGV1_REQUIREMENT in requirements
999 requirementsmod.REVLOGV1_REQUIREMENT in requirements
1000 or requirementsmod.REVLOGV2_REQUIREMENT in requirements
1000 or requirementsmod.REVLOGV2_REQUIREMENT in requirements
1001 ):
1001 ):
1002 options.update(resolverevlogstorevfsoptions(ui, requirements, features))
1002 options.update(resolverevlogstorevfsoptions(ui, requirements, features))
1003 else: # explicitly mark repo as using revlogv0
1003 else: # explicitly mark repo as using revlogv0
1004 options[b'revlogv0'] = True
1004 options[b'revlogv0'] = True
1005
1005
1006 if requirementsmod.COPIESSDC_REQUIREMENT in requirements:
1006 if requirementsmod.COPIESSDC_REQUIREMENT in requirements:
1007 options[b'copies-storage'] = b'changeset-sidedata'
1007 options[b'copies-storage'] = b'changeset-sidedata'
1008 else:
1008 else:
1009 writecopiesto = ui.config(b'experimental', b'copies.write-to')
1009 writecopiesto = ui.config(b'experimental', b'copies.write-to')
1010 copiesextramode = (b'changeset-only', b'compatibility')
1010 copiesextramode = (b'changeset-only', b'compatibility')
1011 if writecopiesto in copiesextramode:
1011 if writecopiesto in copiesextramode:
1012 options[b'copies-storage'] = b'extra'
1012 options[b'copies-storage'] = b'extra'
1013
1013
1014 return options
1014 return options
1015
1015
1016
1016
1017 def resolverevlogstorevfsoptions(ui, requirements, features):
1017 def resolverevlogstorevfsoptions(ui, requirements, features):
1018 """Resolve opener options specific to revlogs."""
1018 """Resolve opener options specific to revlogs."""
1019
1019
1020 options = {}
1020 options = {}
1021 options[b'flagprocessors'] = {}
1021 options[b'flagprocessors'] = {}
1022
1022
1023 if requirementsmod.REVLOGV1_REQUIREMENT in requirements:
1023 if requirementsmod.REVLOGV1_REQUIREMENT in requirements:
1024 options[b'revlogv1'] = True
1024 options[b'revlogv1'] = True
1025 if requirementsmod.REVLOGV2_REQUIREMENT in requirements:
1025 if requirementsmod.REVLOGV2_REQUIREMENT in requirements:
1026 options[b'revlogv2'] = True
1026 options[b'revlogv2'] = True
1027 if requirementsmod.CHANGELOGV2_REQUIREMENT in requirements:
1027 if requirementsmod.CHANGELOGV2_REQUIREMENT in requirements:
1028 options[b'changelogv2'] = True
1028 options[b'changelogv2'] = True
1029
1029
1030 if requirementsmod.GENERALDELTA_REQUIREMENT in requirements:
1030 if requirementsmod.GENERALDELTA_REQUIREMENT in requirements:
1031 options[b'generaldelta'] = True
1031 options[b'generaldelta'] = True
1032
1032
1033 # experimental config: format.chunkcachesize
1033 # experimental config: format.chunkcachesize
1034 chunkcachesize = ui.configint(b'format', b'chunkcachesize')
1034 chunkcachesize = ui.configint(b'format', b'chunkcachesize')
1035 if chunkcachesize is not None:
1035 if chunkcachesize is not None:
1036 options[b'chunkcachesize'] = chunkcachesize
1036 options[b'chunkcachesize'] = chunkcachesize
1037
1037
1038 deltabothparents = ui.configbool(
1038 deltabothparents = ui.configbool(
1039 b'storage', b'revlog.optimize-delta-parent-choice'
1039 b'storage', b'revlog.optimize-delta-parent-choice'
1040 )
1040 )
1041 options[b'deltabothparents'] = deltabothparents
1041 options[b'deltabothparents'] = deltabothparents
1042
1042
1043 lazydelta = ui.configbool(b'storage', b'revlog.reuse-external-delta')
1043 lazydelta = ui.configbool(b'storage', b'revlog.reuse-external-delta')
1044 lazydeltabase = False
1044 lazydeltabase = False
1045 if lazydelta:
1045 if lazydelta:
1046 lazydeltabase = ui.configbool(
1046 lazydeltabase = ui.configbool(
1047 b'storage', b'revlog.reuse-external-delta-parent'
1047 b'storage', b'revlog.reuse-external-delta-parent'
1048 )
1048 )
1049 if lazydeltabase is None:
1049 if lazydeltabase is None:
1050 lazydeltabase = not scmutil.gddeltaconfig(ui)
1050 lazydeltabase = not scmutil.gddeltaconfig(ui)
1051 options[b'lazydelta'] = lazydelta
1051 options[b'lazydelta'] = lazydelta
1052 options[b'lazydeltabase'] = lazydeltabase
1052 options[b'lazydeltabase'] = lazydeltabase
1053
1053
1054 chainspan = ui.configbytes(b'experimental', b'maxdeltachainspan')
1054 chainspan = ui.configbytes(b'experimental', b'maxdeltachainspan')
1055 if 0 <= chainspan:
1055 if 0 <= chainspan:
1056 options[b'maxdeltachainspan'] = chainspan
1056 options[b'maxdeltachainspan'] = chainspan
1057
1057
1058 mmapindexthreshold = ui.configbytes(b'experimental', b'mmapindexthreshold')
1058 mmapindexthreshold = ui.configbytes(b'experimental', b'mmapindexthreshold')
1059 if mmapindexthreshold is not None:
1059 if mmapindexthreshold is not None:
1060 options[b'mmapindexthreshold'] = mmapindexthreshold
1060 options[b'mmapindexthreshold'] = mmapindexthreshold
1061
1061
1062 withsparseread = ui.configbool(b'experimental', b'sparse-read')
1062 withsparseread = ui.configbool(b'experimental', b'sparse-read')
1063 srdensitythres = float(
1063 srdensitythres = float(
1064 ui.config(b'experimental', b'sparse-read.density-threshold')
1064 ui.config(b'experimental', b'sparse-read.density-threshold')
1065 )
1065 )
1066 srmingapsize = ui.configbytes(b'experimental', b'sparse-read.min-gap-size')
1066 srmingapsize = ui.configbytes(b'experimental', b'sparse-read.min-gap-size')
1067 options[b'with-sparse-read'] = withsparseread
1067 options[b'with-sparse-read'] = withsparseread
1068 options[b'sparse-read-density-threshold'] = srdensitythres
1068 options[b'sparse-read-density-threshold'] = srdensitythres
1069 options[b'sparse-read-min-gap-size'] = srmingapsize
1069 options[b'sparse-read-min-gap-size'] = srmingapsize
1070
1070
1071 sparserevlog = requirementsmod.SPARSEREVLOG_REQUIREMENT in requirements
1071 sparserevlog = requirementsmod.SPARSEREVLOG_REQUIREMENT in requirements
1072 options[b'sparse-revlog'] = sparserevlog
1072 options[b'sparse-revlog'] = sparserevlog
1073 if sparserevlog:
1073 if sparserevlog:
1074 options[b'generaldelta'] = True
1074 options[b'generaldelta'] = True
1075
1075
1076 maxchainlen = None
1076 maxchainlen = None
1077 if sparserevlog:
1077 if sparserevlog:
1078 maxchainlen = revlogconst.SPARSE_REVLOG_MAX_CHAIN_LENGTH
1078 maxchainlen = revlogconst.SPARSE_REVLOG_MAX_CHAIN_LENGTH
1079 # experimental config: format.maxchainlen
1079 # experimental config: format.maxchainlen
1080 maxchainlen = ui.configint(b'format', b'maxchainlen', maxchainlen)
1080 maxchainlen = ui.configint(b'format', b'maxchainlen', maxchainlen)
1081 if maxchainlen is not None:
1081 if maxchainlen is not None:
1082 options[b'maxchainlen'] = maxchainlen
1082 options[b'maxchainlen'] = maxchainlen
1083
1083
1084 for r in requirements:
1084 for r in requirements:
1085 # we allow multiple compression engine requirement to co-exist because
1085 # we allow multiple compression engine requirement to co-exist because
1086 # strickly speaking, revlog seems to support mixed compression style.
1086 # strickly speaking, revlog seems to support mixed compression style.
1087 #
1087 #
1088 # The compression used for new entries will be "the last one"
1088 # The compression used for new entries will be "the last one"
1089 prefix = r.startswith
1089 prefix = r.startswith
1090 if prefix(b'revlog-compression-') or prefix(b'exp-compression-'):
1090 if prefix(b'revlog-compression-') or prefix(b'exp-compression-'):
1091 options[b'compengine'] = r.split(b'-', 2)[2]
1091 options[b'compengine'] = r.split(b'-', 2)[2]
1092
1092
1093 options[b'zlib.level'] = ui.configint(b'storage', b'revlog.zlib.level')
1093 options[b'zlib.level'] = ui.configint(b'storage', b'revlog.zlib.level')
1094 if options[b'zlib.level'] is not None:
1094 if options[b'zlib.level'] is not None:
1095 if not (0 <= options[b'zlib.level'] <= 9):
1095 if not (0 <= options[b'zlib.level'] <= 9):
1096 msg = _(b'invalid value for `storage.revlog.zlib.level` config: %d')
1096 msg = _(b'invalid value for `storage.revlog.zlib.level` config: %d')
1097 raise error.Abort(msg % options[b'zlib.level'])
1097 raise error.Abort(msg % options[b'zlib.level'])
1098 options[b'zstd.level'] = ui.configint(b'storage', b'revlog.zstd.level')
1098 options[b'zstd.level'] = ui.configint(b'storage', b'revlog.zstd.level')
1099 if options[b'zstd.level'] is not None:
1099 if options[b'zstd.level'] is not None:
1100 if not (0 <= options[b'zstd.level'] <= 22):
1100 if not (0 <= options[b'zstd.level'] <= 22):
1101 msg = _(b'invalid value for `storage.revlog.zstd.level` config: %d')
1101 msg = _(b'invalid value for `storage.revlog.zstd.level` config: %d')
1102 raise error.Abort(msg % options[b'zstd.level'])
1102 raise error.Abort(msg % options[b'zstd.level'])
1103
1103
1104 if requirementsmod.NARROW_REQUIREMENT in requirements:
1104 if requirementsmod.NARROW_REQUIREMENT in requirements:
1105 options[b'enableellipsis'] = True
1105 options[b'enableellipsis'] = True
1106
1106
1107 if ui.configbool(b'experimental', b'rust.index'):
1107 if ui.configbool(b'experimental', b'rust.index'):
1108 options[b'rust.index'] = True
1108 options[b'rust.index'] = True
1109 if requirementsmod.NODEMAP_REQUIREMENT in requirements:
1109 if requirementsmod.NODEMAP_REQUIREMENT in requirements:
1110 slow_path = ui.config(
1110 slow_path = ui.config(
1111 b'storage', b'revlog.persistent-nodemap.slow-path'
1111 b'storage', b'revlog.persistent-nodemap.slow-path'
1112 )
1112 )
1113 if slow_path not in (b'allow', b'warn', b'abort'):
1113 if slow_path not in (b'allow', b'warn', b'abort'):
1114 default = ui.config_default(
1114 default = ui.config_default(
1115 b'storage', b'revlog.persistent-nodemap.slow-path'
1115 b'storage', b'revlog.persistent-nodemap.slow-path'
1116 )
1116 )
1117 msg = _(
1117 msg = _(
1118 b'unknown value for config '
1118 b'unknown value for config '
1119 b'"storage.revlog.persistent-nodemap.slow-path": "%s"\n'
1119 b'"storage.revlog.persistent-nodemap.slow-path": "%s"\n'
1120 )
1120 )
1121 ui.warn(msg % slow_path)
1121 ui.warn(msg % slow_path)
1122 if not ui.quiet:
1122 if not ui.quiet:
1123 ui.warn(_(b'falling back to default value: %s\n') % default)
1123 ui.warn(_(b'falling back to default value: %s\n') % default)
1124 slow_path = default
1124 slow_path = default
1125
1125
1126 msg = _(
1126 msg = _(
1127 b"accessing `persistent-nodemap` repository without associated "
1127 b"accessing `persistent-nodemap` repository without associated "
1128 b"fast implementation."
1128 b"fast implementation."
1129 )
1129 )
1130 hint = _(
1130 hint = _(
1131 b"check `hg help config.format.use-persistent-nodemap` "
1131 b"check `hg help config.format.use-persistent-nodemap` "
1132 b"for details"
1132 b"for details"
1133 )
1133 )
1134 if not revlog.HAS_FAST_PERSISTENT_NODEMAP:
1134 if not revlog.HAS_FAST_PERSISTENT_NODEMAP:
1135 if slow_path == b'warn':
1135 if slow_path == b'warn':
1136 msg = b"warning: " + msg + b'\n'
1136 msg = b"warning: " + msg + b'\n'
1137 ui.warn(msg)
1137 ui.warn(msg)
1138 if not ui.quiet:
1138 if not ui.quiet:
1139 hint = b'(' + hint + b')\n'
1139 hint = b'(' + hint + b')\n'
1140 ui.warn(hint)
1140 ui.warn(hint)
1141 if slow_path == b'abort':
1141 if slow_path == b'abort':
1142 raise error.Abort(msg, hint=hint)
1142 raise error.Abort(msg, hint=hint)
1143 options[b'persistent-nodemap'] = True
1143 options[b'persistent-nodemap'] = True
1144 if ui.configbool(b'storage', b'revlog.persistent-nodemap.mmap'):
1144 if ui.configbool(b'storage', b'revlog.persistent-nodemap.mmap'):
1145 options[b'persistent-nodemap.mmap'] = True
1145 options[b'persistent-nodemap.mmap'] = True
1146 if ui.configbool(b'devel', b'persistent-nodemap'):
1146 if ui.configbool(b'devel', b'persistent-nodemap'):
1147 options[b'devel-force-nodemap'] = True
1147 options[b'devel-force-nodemap'] = True
1148
1148
1149 return options
1149 return options
1150
1150
1151
1151
1152 def makemain(**kwargs):
1152 def makemain(**kwargs):
1153 """Produce a type conforming to ``ilocalrepositorymain``."""
1153 """Produce a type conforming to ``ilocalrepositorymain``."""
1154 return localrepository
1154 return localrepository
1155
1155
1156
1156
1157 @interfaceutil.implementer(repository.ilocalrepositoryfilestorage)
1157 @interfaceutil.implementer(repository.ilocalrepositoryfilestorage)
1158 class revlogfilestorage(object):
1158 class revlogfilestorage(object):
1159 """File storage when using revlogs."""
1159 """File storage when using revlogs."""
1160
1160
1161 def file(self, path):
1161 def file(self, path):
1162 if path.startswith(b'/'):
1162 if path.startswith(b'/'):
1163 path = path[1:]
1163 path = path[1:]
1164
1164
1165 return filelog.filelog(self.svfs, path)
1165 return filelog.filelog(self.svfs, path)
1166
1166
1167
1167
1168 @interfaceutil.implementer(repository.ilocalrepositoryfilestorage)
1168 @interfaceutil.implementer(repository.ilocalrepositoryfilestorage)
1169 class revlognarrowfilestorage(object):
1169 class revlognarrowfilestorage(object):
1170 """File storage when using revlogs and narrow files."""
1170 """File storage when using revlogs and narrow files."""
1171
1171
1172 def file(self, path):
1172 def file(self, path):
1173 if path.startswith(b'/'):
1173 if path.startswith(b'/'):
1174 path = path[1:]
1174 path = path[1:]
1175
1175
1176 return filelog.narrowfilelog(self.svfs, path, self._storenarrowmatch)
1176 return filelog.narrowfilelog(self.svfs, path, self._storenarrowmatch)
1177
1177
1178
1178
1179 def makefilestorage(requirements, features, **kwargs):
1179 def makefilestorage(requirements, features, **kwargs):
1180 """Produce a type conforming to ``ilocalrepositoryfilestorage``."""
1180 """Produce a type conforming to ``ilocalrepositoryfilestorage``."""
1181 features.add(repository.REPO_FEATURE_REVLOG_FILE_STORAGE)
1181 features.add(repository.REPO_FEATURE_REVLOG_FILE_STORAGE)
1182 features.add(repository.REPO_FEATURE_STREAM_CLONE)
1182 features.add(repository.REPO_FEATURE_STREAM_CLONE)
1183
1183
1184 if requirementsmod.NARROW_REQUIREMENT in requirements:
1184 if requirementsmod.NARROW_REQUIREMENT in requirements:
1185 return revlognarrowfilestorage
1185 return revlognarrowfilestorage
1186 else:
1186 else:
1187 return revlogfilestorage
1187 return revlogfilestorage
1188
1188
1189
1189
1190 # List of repository interfaces and factory functions for them. Each
1190 # List of repository interfaces and factory functions for them. Each
1191 # will be called in order during ``makelocalrepository()`` to iteratively
1191 # will be called in order during ``makelocalrepository()`` to iteratively
1192 # derive the final type for a local repository instance. We capture the
1192 # derive the final type for a local repository instance. We capture the
1193 # function as a lambda so we don't hold a reference and the module-level
1193 # function as a lambda so we don't hold a reference and the module-level
1194 # functions can be wrapped.
1194 # functions can be wrapped.
1195 REPO_INTERFACES = [
1195 REPO_INTERFACES = [
1196 (repository.ilocalrepositorymain, lambda: makemain),
1196 (repository.ilocalrepositorymain, lambda: makemain),
1197 (repository.ilocalrepositoryfilestorage, lambda: makefilestorage),
1197 (repository.ilocalrepositoryfilestorage, lambda: makefilestorage),
1198 ]
1198 ]
1199
1199
1200
1200
1201 @interfaceutil.implementer(repository.ilocalrepositorymain)
1201 @interfaceutil.implementer(repository.ilocalrepositorymain)
1202 class localrepository(object):
1202 class localrepository(object):
1203 """Main class for representing local repositories.
1203 """Main class for representing local repositories.
1204
1204
1205 All local repositories are instances of this class.
1205 All local repositories are instances of this class.
1206
1206
1207 Constructed on its own, instances of this class are not usable as
1207 Constructed on its own, instances of this class are not usable as
1208 repository objects. To obtain a usable repository object, call
1208 repository objects. To obtain a usable repository object, call
1209 ``hg.repository()``, ``localrepo.instance()``, or
1209 ``hg.repository()``, ``localrepo.instance()``, or
1210 ``localrepo.makelocalrepository()``. The latter is the lowest-level.
1210 ``localrepo.makelocalrepository()``. The latter is the lowest-level.
1211 ``instance()`` adds support for creating new repositories.
1211 ``instance()`` adds support for creating new repositories.
1212 ``hg.repository()`` adds more extension integration, including calling
1212 ``hg.repository()`` adds more extension integration, including calling
1213 ``reposetup()``. Generally speaking, ``hg.repository()`` should be
1213 ``reposetup()``. Generally speaking, ``hg.repository()`` should be
1214 used.
1214 used.
1215 """
1215 """
1216
1216
1217 # obsolete experimental requirements:
1217 # obsolete experimental requirements:
1218 # - manifestv2: An experimental new manifest format that allowed
1218 # - manifestv2: An experimental new manifest format that allowed
1219 # for stem compression of long paths. Experiment ended up not
1219 # for stem compression of long paths. Experiment ended up not
1220 # being successful (repository sizes went up due to worse delta
1220 # being successful (repository sizes went up due to worse delta
1221 # chains), and the code was deleted in 4.6.
1221 # chains), and the code was deleted in 4.6.
1222 supportedformats = {
1222 supportedformats = {
1223 requirementsmod.REVLOGV1_REQUIREMENT,
1223 requirementsmod.REVLOGV1_REQUIREMENT,
1224 requirementsmod.GENERALDELTA_REQUIREMENT,
1224 requirementsmod.GENERALDELTA_REQUIREMENT,
1225 requirementsmod.TREEMANIFEST_REQUIREMENT,
1225 requirementsmod.TREEMANIFEST_REQUIREMENT,
1226 requirementsmod.COPIESSDC_REQUIREMENT,
1226 requirementsmod.COPIESSDC_REQUIREMENT,
1227 requirementsmod.REVLOGV2_REQUIREMENT,
1227 requirementsmod.REVLOGV2_REQUIREMENT,
1228 requirementsmod.CHANGELOGV2_REQUIREMENT,
1228 requirementsmod.CHANGELOGV2_REQUIREMENT,
1229 requirementsmod.SPARSEREVLOG_REQUIREMENT,
1229 requirementsmod.SPARSEREVLOG_REQUIREMENT,
1230 requirementsmod.NODEMAP_REQUIREMENT,
1230 requirementsmod.NODEMAP_REQUIREMENT,
1231 bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT,
1231 bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT,
1232 requirementsmod.SHARESAFE_REQUIREMENT,
1232 requirementsmod.SHARESAFE_REQUIREMENT,
1233 }
1233 }
1234 _basesupported = supportedformats | {
1234 _basesupported = supportedformats | {
1235 requirementsmod.STORE_REQUIREMENT,
1235 requirementsmod.STORE_REQUIREMENT,
1236 requirementsmod.FNCACHE_REQUIREMENT,
1236 requirementsmod.FNCACHE_REQUIREMENT,
1237 requirementsmod.SHARED_REQUIREMENT,
1237 requirementsmod.SHARED_REQUIREMENT,
1238 requirementsmod.RELATIVE_SHARED_REQUIREMENT,
1238 requirementsmod.RELATIVE_SHARED_REQUIREMENT,
1239 requirementsmod.DOTENCODE_REQUIREMENT,
1239 requirementsmod.DOTENCODE_REQUIREMENT,
1240 requirementsmod.SPARSE_REQUIREMENT,
1240 requirementsmod.SPARSE_REQUIREMENT,
1241 requirementsmod.INTERNAL_PHASE_REQUIREMENT,
1241 requirementsmod.INTERNAL_PHASE_REQUIREMENT,
1242 }
1242 }
1243
1243
1244 # list of prefix for file which can be written without 'wlock'
1244 # list of prefix for file which can be written without 'wlock'
1245 # Extensions should extend this list when needed
1245 # Extensions should extend this list when needed
1246 _wlockfreeprefix = {
1246 _wlockfreeprefix = {
1247 # We migh consider requiring 'wlock' for the next
1247 # We migh consider requiring 'wlock' for the next
1248 # two, but pretty much all the existing code assume
1248 # two, but pretty much all the existing code assume
1249 # wlock is not needed so we keep them excluded for
1249 # wlock is not needed so we keep them excluded for
1250 # now.
1250 # now.
1251 b'hgrc',
1251 b'hgrc',
1252 b'requires',
1252 b'requires',
1253 # XXX cache is a complicatged business someone
1253 # XXX cache is a complicatged business someone
1254 # should investigate this in depth at some point
1254 # should investigate this in depth at some point
1255 b'cache/',
1255 b'cache/',
1256 # XXX shouldn't be dirstate covered by the wlock?
1256 # XXX shouldn't be dirstate covered by the wlock?
1257 b'dirstate',
1257 b'dirstate',
1258 # XXX bisect was still a bit too messy at the time
1258 # XXX bisect was still a bit too messy at the time
1259 # this changeset was introduced. Someone should fix
1259 # this changeset was introduced. Someone should fix
1260 # the remainig bit and drop this line
1260 # the remainig bit and drop this line
1261 b'bisect.state',
1261 b'bisect.state',
1262 }
1262 }
1263
1263
1264 def __init__(
1264 def __init__(
1265 self,
1265 self,
1266 baseui,
1266 baseui,
1267 ui,
1267 ui,
1268 origroot,
1268 origroot,
1269 wdirvfs,
1269 wdirvfs,
1270 hgvfs,
1270 hgvfs,
1271 requirements,
1271 requirements,
1272 supportedrequirements,
1272 supportedrequirements,
1273 sharedpath,
1273 sharedpath,
1274 store,
1274 store,
1275 cachevfs,
1275 cachevfs,
1276 wcachevfs,
1276 wcachevfs,
1277 features,
1277 features,
1278 intents=None,
1278 intents=None,
1279 ):
1279 ):
1280 """Create a new local repository instance.
1280 """Create a new local repository instance.
1281
1281
1282 Most callers should use ``hg.repository()``, ``localrepo.instance()``,
1282 Most callers should use ``hg.repository()``, ``localrepo.instance()``,
1283 or ``localrepo.makelocalrepository()`` for obtaining a new repository
1283 or ``localrepo.makelocalrepository()`` for obtaining a new repository
1284 object.
1284 object.
1285
1285
1286 Arguments:
1286 Arguments:
1287
1287
1288 baseui
1288 baseui
1289 ``ui.ui`` instance that ``ui`` argument was based off of.
1289 ``ui.ui`` instance that ``ui`` argument was based off of.
1290
1290
1291 ui
1291 ui
1292 ``ui.ui`` instance for use by the repository.
1292 ``ui.ui`` instance for use by the repository.
1293
1293
1294 origroot
1294 origroot
1295 ``bytes`` path to working directory root of this repository.
1295 ``bytes`` path to working directory root of this repository.
1296
1296
1297 wdirvfs
1297 wdirvfs
1298 ``vfs.vfs`` rooted at the working directory.
1298 ``vfs.vfs`` rooted at the working directory.
1299
1299
1300 hgvfs
1300 hgvfs
1301 ``vfs.vfs`` rooted at .hg/
1301 ``vfs.vfs`` rooted at .hg/
1302
1302
1303 requirements
1303 requirements
1304 ``set`` of bytestrings representing repository opening requirements.
1304 ``set`` of bytestrings representing repository opening requirements.
1305
1305
1306 supportedrequirements
1306 supportedrequirements
1307 ``set`` of bytestrings representing repository requirements that we
1307 ``set`` of bytestrings representing repository requirements that we
1308 know how to open. May be a supetset of ``requirements``.
1308 know how to open. May be a supetset of ``requirements``.
1309
1309
1310 sharedpath
1310 sharedpath
1311 ``bytes`` Defining path to storage base directory. Points to a
1311 ``bytes`` Defining path to storage base directory. Points to a
1312 ``.hg/`` directory somewhere.
1312 ``.hg/`` directory somewhere.
1313
1313
1314 store
1314 store
1315 ``store.basicstore`` (or derived) instance providing access to
1315 ``store.basicstore`` (or derived) instance providing access to
1316 versioned storage.
1316 versioned storage.
1317
1317
1318 cachevfs
1318 cachevfs
1319 ``vfs.vfs`` used for cache files.
1319 ``vfs.vfs`` used for cache files.
1320
1320
1321 wcachevfs
1321 wcachevfs
1322 ``vfs.vfs`` used for cache files related to the working copy.
1322 ``vfs.vfs`` used for cache files related to the working copy.
1323
1323
1324 features
1324 features
1325 ``set`` of bytestrings defining features/capabilities of this
1325 ``set`` of bytestrings defining features/capabilities of this
1326 instance.
1326 instance.
1327
1327
1328 intents
1328 intents
1329 ``set`` of system strings indicating what this repo will be used
1329 ``set`` of system strings indicating what this repo will be used
1330 for.
1330 for.
1331 """
1331 """
1332 self.baseui = baseui
1332 self.baseui = baseui
1333 self.ui = ui
1333 self.ui = ui
1334 self.origroot = origroot
1334 self.origroot = origroot
1335 # vfs rooted at working directory.
1335 # vfs rooted at working directory.
1336 self.wvfs = wdirvfs
1336 self.wvfs = wdirvfs
1337 self.root = wdirvfs.base
1337 self.root = wdirvfs.base
1338 # vfs rooted at .hg/. Used to access most non-store paths.
1338 # vfs rooted at .hg/. Used to access most non-store paths.
1339 self.vfs = hgvfs
1339 self.vfs = hgvfs
1340 self.path = hgvfs.base
1340 self.path = hgvfs.base
1341 self.requirements = requirements
1341 self.requirements = requirements
1342 self.nodeconstants = sha1nodeconstants
1342 self.nodeconstants = sha1nodeconstants
1343 self.nullid = self.nodeconstants.nullid
1343 self.nullid = self.nodeconstants.nullid
1344 self.supported = supportedrequirements
1344 self.supported = supportedrequirements
1345 self.sharedpath = sharedpath
1345 self.sharedpath = sharedpath
1346 self.store = store
1346 self.store = store
1347 self.cachevfs = cachevfs
1347 self.cachevfs = cachevfs
1348 self.wcachevfs = wcachevfs
1348 self.wcachevfs = wcachevfs
1349 self.features = features
1349 self.features = features
1350
1350
1351 self.filtername = None
1351 self.filtername = None
1352
1352
1353 if self.ui.configbool(b'devel', b'all-warnings') or self.ui.configbool(
1353 if self.ui.configbool(b'devel', b'all-warnings') or self.ui.configbool(
1354 b'devel', b'check-locks'
1354 b'devel', b'check-locks'
1355 ):
1355 ):
1356 self.vfs.audit = self._getvfsward(self.vfs.audit)
1356 self.vfs.audit = self._getvfsward(self.vfs.audit)
1357 # A list of callback to shape the phase if no data were found.
1357 # A list of callback to shape the phase if no data were found.
1358 # Callback are in the form: func(repo, roots) --> processed root.
1358 # Callback are in the form: func(repo, roots) --> processed root.
1359 # This list it to be filled by extension during repo setup
1359 # This list it to be filled by extension during repo setup
1360 self._phasedefaults = []
1360 self._phasedefaults = []
1361
1361
1362 color.setup(self.ui)
1362 color.setup(self.ui)
1363
1363
1364 self.spath = self.store.path
1364 self.spath = self.store.path
1365 self.svfs = self.store.vfs
1365 self.svfs = self.store.vfs
1366 self.sjoin = self.store.join
1366 self.sjoin = self.store.join
1367 if self.ui.configbool(b'devel', b'all-warnings') or self.ui.configbool(
1367 if self.ui.configbool(b'devel', b'all-warnings') or self.ui.configbool(
1368 b'devel', b'check-locks'
1368 b'devel', b'check-locks'
1369 ):
1369 ):
1370 if util.safehasattr(self.svfs, b'vfs'): # this is filtervfs
1370 if util.safehasattr(self.svfs, b'vfs'): # this is filtervfs
1371 self.svfs.vfs.audit = self._getsvfsward(self.svfs.vfs.audit)
1371 self.svfs.vfs.audit = self._getsvfsward(self.svfs.vfs.audit)
1372 else: # standard vfs
1372 else: # standard vfs
1373 self.svfs.audit = self._getsvfsward(self.svfs.audit)
1373 self.svfs.audit = self._getsvfsward(self.svfs.audit)
1374
1374
1375 self._dirstatevalidatewarned = False
1375 self._dirstatevalidatewarned = False
1376
1376
1377 self._branchcaches = branchmap.BranchMapCache()
1377 self._branchcaches = branchmap.BranchMapCache()
1378 self._revbranchcache = None
1378 self._revbranchcache = None
1379 self._filterpats = {}
1379 self._filterpats = {}
1380 self._datafilters = {}
1380 self._datafilters = {}
1381 self._transref = self._lockref = self._wlockref = None
1381 self._transref = self._lockref = self._wlockref = None
1382
1382
1383 # A cache for various files under .hg/ that tracks file changes,
1383 # A cache for various files under .hg/ that tracks file changes,
1384 # (used by the filecache decorator)
1384 # (used by the filecache decorator)
1385 #
1385 #
1386 # Maps a property name to its util.filecacheentry
1386 # Maps a property name to its util.filecacheentry
1387 self._filecache = {}
1387 self._filecache = {}
1388
1388
1389 # hold sets of revision to be filtered
1389 # hold sets of revision to be filtered
1390 # should be cleared when something might have changed the filter value:
1390 # should be cleared when something might have changed the filter value:
1391 # - new changesets,
1391 # - new changesets,
1392 # - phase change,
1392 # - phase change,
1393 # - new obsolescence marker,
1393 # - new obsolescence marker,
1394 # - working directory parent change,
1394 # - working directory parent change,
1395 # - bookmark changes
1395 # - bookmark changes
1396 self.filteredrevcache = {}
1396 self.filteredrevcache = {}
1397
1397
1398 # post-dirstate-status hooks
1398 # post-dirstate-status hooks
1399 self._postdsstatus = []
1399 self._postdsstatus = []
1400
1400
1401 # generic mapping between names and nodes
1401 # generic mapping between names and nodes
1402 self.names = namespaces.namespaces()
1402 self.names = namespaces.namespaces()
1403
1403
1404 # Key to signature value.
1404 # Key to signature value.
1405 self._sparsesignaturecache = {}
1405 self._sparsesignaturecache = {}
1406 # Signature to cached matcher instance.
1406 # Signature to cached matcher instance.
1407 self._sparsematchercache = {}
1407 self._sparsematchercache = {}
1408
1408
1409 self._extrafilterid = repoview.extrafilter(ui)
1409 self._extrafilterid = repoview.extrafilter(ui)
1410
1410
1411 self.filecopiesmode = None
1411 self.filecopiesmode = None
1412 if requirementsmod.COPIESSDC_REQUIREMENT in self.requirements:
1412 if requirementsmod.COPIESSDC_REQUIREMENT in self.requirements:
1413 self.filecopiesmode = b'changeset-sidedata'
1413 self.filecopiesmode = b'changeset-sidedata'
1414
1414
1415 self._wanted_sidedata = set()
1415 self._wanted_sidedata = set()
1416 self._sidedata_computers = {}
1416 self._sidedata_computers = {}
1417 sidedatamod.set_sidedata_spec_for_repo(self)
1417 sidedatamod.set_sidedata_spec_for_repo(self)
1418
1418
1419 def _getvfsward(self, origfunc):
1419 def _getvfsward(self, origfunc):
1420 """build a ward for self.vfs"""
1420 """build a ward for self.vfs"""
1421 rref = weakref.ref(self)
1421 rref = weakref.ref(self)
1422
1422
1423 def checkvfs(path, mode=None):
1423 def checkvfs(path, mode=None):
1424 ret = origfunc(path, mode=mode)
1424 ret = origfunc(path, mode=mode)
1425 repo = rref()
1425 repo = rref()
1426 if (
1426 if (
1427 repo is None
1427 repo is None
1428 or not util.safehasattr(repo, b'_wlockref')
1428 or not util.safehasattr(repo, b'_wlockref')
1429 or not util.safehasattr(repo, b'_lockref')
1429 or not util.safehasattr(repo, b'_lockref')
1430 ):
1430 ):
1431 return
1431 return
1432 if mode in (None, b'r', b'rb'):
1432 if mode in (None, b'r', b'rb'):
1433 return
1433 return
1434 if path.startswith(repo.path):
1434 if path.startswith(repo.path):
1435 # truncate name relative to the repository (.hg)
1435 # truncate name relative to the repository (.hg)
1436 path = path[len(repo.path) + 1 :]
1436 path = path[len(repo.path) + 1 :]
1437 if path.startswith(b'cache/'):
1437 if path.startswith(b'cache/'):
1438 msg = b'accessing cache with vfs instead of cachevfs: "%s"'
1438 msg = b'accessing cache with vfs instead of cachevfs: "%s"'
1439 repo.ui.develwarn(msg % path, stacklevel=3, config=b"cache-vfs")
1439 repo.ui.develwarn(msg % path, stacklevel=3, config=b"cache-vfs")
1440 # path prefixes covered by 'lock'
1440 # path prefixes covered by 'lock'
1441 vfs_path_prefixes = (
1441 vfs_path_prefixes = (
1442 b'journal.',
1442 b'journal.',
1443 b'undo.',
1443 b'undo.',
1444 b'strip-backup/',
1444 b'strip-backup/',
1445 b'cache/',
1445 b'cache/',
1446 )
1446 )
1447 if any(path.startswith(prefix) for prefix in vfs_path_prefixes):
1447 if any(path.startswith(prefix) for prefix in vfs_path_prefixes):
1448 if repo._currentlock(repo._lockref) is None:
1448 if repo._currentlock(repo._lockref) is None:
1449 repo.ui.develwarn(
1449 repo.ui.develwarn(
1450 b'write with no lock: "%s"' % path,
1450 b'write with no lock: "%s"' % path,
1451 stacklevel=3,
1451 stacklevel=3,
1452 config=b'check-locks',
1452 config=b'check-locks',
1453 )
1453 )
1454 elif repo._currentlock(repo._wlockref) is None:
1454 elif repo._currentlock(repo._wlockref) is None:
1455 # rest of vfs files are covered by 'wlock'
1455 # rest of vfs files are covered by 'wlock'
1456 #
1456 #
1457 # exclude special files
1457 # exclude special files
1458 for prefix in self._wlockfreeprefix:
1458 for prefix in self._wlockfreeprefix:
1459 if path.startswith(prefix):
1459 if path.startswith(prefix):
1460 return
1460 return
1461 repo.ui.develwarn(
1461 repo.ui.develwarn(
1462 b'write with no wlock: "%s"' % path,
1462 b'write with no wlock: "%s"' % path,
1463 stacklevel=3,
1463 stacklevel=3,
1464 config=b'check-locks',
1464 config=b'check-locks',
1465 )
1465 )
1466 return ret
1466 return ret
1467
1467
1468 return checkvfs
1468 return checkvfs
1469
1469
1470 def _getsvfsward(self, origfunc):
1470 def _getsvfsward(self, origfunc):
1471 """build a ward for self.svfs"""
1471 """build a ward for self.svfs"""
1472 rref = weakref.ref(self)
1472 rref = weakref.ref(self)
1473
1473
1474 def checksvfs(path, mode=None):
1474 def checksvfs(path, mode=None):
1475 ret = origfunc(path, mode=mode)
1475 ret = origfunc(path, mode=mode)
1476 repo = rref()
1476 repo = rref()
1477 if repo is None or not util.safehasattr(repo, b'_lockref'):
1477 if repo is None or not util.safehasattr(repo, b'_lockref'):
1478 return
1478 return
1479 if mode in (None, b'r', b'rb'):
1479 if mode in (None, b'r', b'rb'):
1480 return
1480 return
1481 if path.startswith(repo.sharedpath):
1481 if path.startswith(repo.sharedpath):
1482 # truncate name relative to the repository (.hg)
1482 # truncate name relative to the repository (.hg)
1483 path = path[len(repo.sharedpath) + 1 :]
1483 path = path[len(repo.sharedpath) + 1 :]
1484 if repo._currentlock(repo._lockref) is None:
1484 if repo._currentlock(repo._lockref) is None:
1485 repo.ui.develwarn(
1485 repo.ui.develwarn(
1486 b'write with no lock: "%s"' % path, stacklevel=4
1486 b'write with no lock: "%s"' % path, stacklevel=4
1487 )
1487 )
1488 return ret
1488 return ret
1489
1489
1490 return checksvfs
1490 return checksvfs
1491
1491
1492 def close(self):
1492 def close(self):
1493 self._writecaches()
1493 self._writecaches()
1494
1494
1495 def _writecaches(self):
1495 def _writecaches(self):
1496 if self._revbranchcache:
1496 if self._revbranchcache:
1497 self._revbranchcache.write()
1497 self._revbranchcache.write()
1498
1498
1499 def _restrictcapabilities(self, caps):
1499 def _restrictcapabilities(self, caps):
1500 if self.ui.configbool(b'experimental', b'bundle2-advertise'):
1500 if self.ui.configbool(b'experimental', b'bundle2-advertise'):
1501 caps = set(caps)
1501 caps = set(caps)
1502 capsblob = bundle2.encodecaps(
1502 capsblob = bundle2.encodecaps(
1503 bundle2.getrepocaps(self, role=b'client')
1503 bundle2.getrepocaps(self, role=b'client')
1504 )
1504 )
1505 caps.add(b'bundle2=' + urlreq.quote(capsblob))
1505 caps.add(b'bundle2=' + urlreq.quote(capsblob))
1506 if self.ui.configbool(b'experimental', b'narrow'):
1506 if self.ui.configbool(b'experimental', b'narrow'):
1507 caps.add(wireprototypes.NARROWCAP)
1507 caps.add(wireprototypes.NARROWCAP)
1508 return caps
1508 return caps
1509
1509
1510 # Don't cache auditor/nofsauditor, or you'll end up with reference cycle:
1510 # Don't cache auditor/nofsauditor, or you'll end up with reference cycle:
1511 # self -> auditor -> self._checknested -> self
1511 # self -> auditor -> self._checknested -> self
1512
1512
1513 @property
1513 @property
1514 def auditor(self):
1514 def auditor(self):
1515 # This is only used by context.workingctx.match in order to
1515 # This is only used by context.workingctx.match in order to
1516 # detect files in subrepos.
1516 # detect files in subrepos.
1517 return pathutil.pathauditor(self.root, callback=self._checknested)
1517 return pathutil.pathauditor(self.root, callback=self._checknested)
1518
1518
1519 @property
1519 @property
1520 def nofsauditor(self):
1520 def nofsauditor(self):
1521 # This is only used by context.basectx.match in order to detect
1521 # This is only used by context.basectx.match in order to detect
1522 # files in subrepos.
1522 # files in subrepos.
1523 return pathutil.pathauditor(
1523 return pathutil.pathauditor(
1524 self.root, callback=self._checknested, realfs=False, cached=True
1524 self.root, callback=self._checknested, realfs=False, cached=True
1525 )
1525 )
1526
1526
1527 def _checknested(self, path):
1527 def _checknested(self, path):
1528 """Determine if path is a legal nested repository."""
1528 """Determine if path is a legal nested repository."""
1529 if not path.startswith(self.root):
1529 if not path.startswith(self.root):
1530 return False
1530 return False
1531 subpath = path[len(self.root) + 1 :]
1531 subpath = path[len(self.root) + 1 :]
1532 normsubpath = util.pconvert(subpath)
1532 normsubpath = util.pconvert(subpath)
1533
1533
1534 # XXX: Checking against the current working copy is wrong in
1534 # XXX: Checking against the current working copy is wrong in
1535 # the sense that it can reject things like
1535 # the sense that it can reject things like
1536 #
1536 #
1537 # $ hg cat -r 10 sub/x.txt
1537 # $ hg cat -r 10 sub/x.txt
1538 #
1538 #
1539 # if sub/ is no longer a subrepository in the working copy
1539 # if sub/ is no longer a subrepository in the working copy
1540 # parent revision.
1540 # parent revision.
1541 #
1541 #
1542 # However, it can of course also allow things that would have
1542 # However, it can of course also allow things that would have
1543 # been rejected before, such as the above cat command if sub/
1543 # been rejected before, such as the above cat command if sub/
1544 # is a subrepository now, but was a normal directory before.
1544 # is a subrepository now, but was a normal directory before.
1545 # The old path auditor would have rejected by mistake since it
1545 # The old path auditor would have rejected by mistake since it
1546 # panics when it sees sub/.hg/.
1546 # panics when it sees sub/.hg/.
1547 #
1547 #
1548 # All in all, checking against the working copy seems sensible
1548 # All in all, checking against the working copy seems sensible
1549 # since we want to prevent access to nested repositories on
1549 # since we want to prevent access to nested repositories on
1550 # the filesystem *now*.
1550 # the filesystem *now*.
1551 ctx = self[None]
1551 ctx = self[None]
1552 parts = util.splitpath(subpath)
1552 parts = util.splitpath(subpath)
1553 while parts:
1553 while parts:
1554 prefix = b'/'.join(parts)
1554 prefix = b'/'.join(parts)
1555 if prefix in ctx.substate:
1555 if prefix in ctx.substate:
1556 if prefix == normsubpath:
1556 if prefix == normsubpath:
1557 return True
1557 return True
1558 else:
1558 else:
1559 sub = ctx.sub(prefix)
1559 sub = ctx.sub(prefix)
1560 return sub.checknested(subpath[len(prefix) + 1 :])
1560 return sub.checknested(subpath[len(prefix) + 1 :])
1561 else:
1561 else:
1562 parts.pop()
1562 parts.pop()
1563 return False
1563 return False
1564
1564
1565 def peer(self):
1565 def peer(self):
1566 return localpeer(self) # not cached to avoid reference cycle
1566 return localpeer(self) # not cached to avoid reference cycle
1567
1567
1568 def unfiltered(self):
1568 def unfiltered(self):
1569 """Return unfiltered version of the repository
1569 """Return unfiltered version of the repository
1570
1570
1571 Intended to be overwritten by filtered repo."""
1571 Intended to be overwritten by filtered repo."""
1572 return self
1572 return self
1573
1573
1574 def filtered(self, name, visibilityexceptions=None):
1574 def filtered(self, name, visibilityexceptions=None):
1575 """Return a filtered version of a repository
1575 """Return a filtered version of a repository
1576
1576
1577 The `name` parameter is the identifier of the requested view. This
1577 The `name` parameter is the identifier of the requested view. This
1578 will return a repoview object set "exactly" to the specified view.
1578 will return a repoview object set "exactly" to the specified view.
1579
1579
1580 This function does not apply recursive filtering to a repository. For
1580 This function does not apply recursive filtering to a repository. For
1581 example calling `repo.filtered("served")` will return a repoview using
1581 example calling `repo.filtered("served")` will return a repoview using
1582 the "served" view, regardless of the initial view used by `repo`.
1582 the "served" view, regardless of the initial view used by `repo`.
1583
1583
1584 In other word, there is always only one level of `repoview` "filtering".
1584 In other word, there is always only one level of `repoview` "filtering".
1585 """
1585 """
1586 if self._extrafilterid is not None and b'%' not in name:
1586 if self._extrafilterid is not None and b'%' not in name:
1587 name = name + b'%' + self._extrafilterid
1587 name = name + b'%' + self._extrafilterid
1588
1588
1589 cls = repoview.newtype(self.unfiltered().__class__)
1589 cls = repoview.newtype(self.unfiltered().__class__)
1590 return cls(self, name, visibilityexceptions)
1590 return cls(self, name, visibilityexceptions)
1591
1591
1592 @mixedrepostorecache(
1592 @mixedrepostorecache(
1593 (b'bookmarks', b'plain'),
1593 (b'bookmarks', b'plain'),
1594 (b'bookmarks.current', b'plain'),
1594 (b'bookmarks.current', b'plain'),
1595 (b'bookmarks', b''),
1595 (b'bookmarks', b''),
1596 (b'00changelog.i', b''),
1596 (b'00changelog.i', b''),
1597 )
1597 )
1598 def _bookmarks(self):
1598 def _bookmarks(self):
1599 # Since the multiple files involved in the transaction cannot be
1599 # Since the multiple files involved in the transaction cannot be
1600 # written atomically (with current repository format), there is a race
1600 # written atomically (with current repository format), there is a race
1601 # condition here.
1601 # condition here.
1602 #
1602 #
1603 # 1) changelog content A is read
1603 # 1) changelog content A is read
1604 # 2) outside transaction update changelog to content B
1604 # 2) outside transaction update changelog to content B
1605 # 3) outside transaction update bookmark file referring to content B
1605 # 3) outside transaction update bookmark file referring to content B
1606 # 4) bookmarks file content is read and filtered against changelog-A
1606 # 4) bookmarks file content is read and filtered against changelog-A
1607 #
1607 #
1608 # When this happens, bookmarks against nodes missing from A are dropped.
1608 # When this happens, bookmarks against nodes missing from A are dropped.
1609 #
1609 #
1610 # Having this happening during read is not great, but it become worse
1610 # Having this happening during read is not great, but it become worse
1611 # when this happen during write because the bookmarks to the "unknown"
1611 # when this happen during write because the bookmarks to the "unknown"
1612 # nodes will be dropped for good. However, writes happen within locks.
1612 # nodes will be dropped for good. However, writes happen within locks.
1613 # This locking makes it possible to have a race free consistent read.
1613 # This locking makes it possible to have a race free consistent read.
1614 # For this purpose data read from disc before locking are
1614 # For this purpose data read from disc before locking are
1615 # "invalidated" right after the locks are taken. This invalidations are
1615 # "invalidated" right after the locks are taken. This invalidations are
1616 # "light", the `filecache` mechanism keep the data in memory and will
1616 # "light", the `filecache` mechanism keep the data in memory and will
1617 # reuse them if the underlying files did not changed. Not parsing the
1617 # reuse them if the underlying files did not changed. Not parsing the
1618 # same data multiple times helps performances.
1618 # same data multiple times helps performances.
1619 #
1619 #
1620 # Unfortunately in the case describe above, the files tracked by the
1620 # Unfortunately in the case describe above, the files tracked by the
1621 # bookmarks file cache might not have changed, but the in-memory
1621 # bookmarks file cache might not have changed, but the in-memory
1622 # content is still "wrong" because we used an older changelog content
1622 # content is still "wrong" because we used an older changelog content
1623 # to process the on-disk data. So after locking, the changelog would be
1623 # to process the on-disk data. So after locking, the changelog would be
1624 # refreshed but `_bookmarks` would be preserved.
1624 # refreshed but `_bookmarks` would be preserved.
1625 # Adding `00changelog.i` to the list of tracked file is not
1625 # Adding `00changelog.i` to the list of tracked file is not
1626 # enough, because at the time we build the content for `_bookmarks` in
1626 # enough, because at the time we build the content for `_bookmarks` in
1627 # (4), the changelog file has already diverged from the content used
1627 # (4), the changelog file has already diverged from the content used
1628 # for loading `changelog` in (1)
1628 # for loading `changelog` in (1)
1629 #
1629 #
1630 # To prevent the issue, we force the changelog to be explicitly
1630 # To prevent the issue, we force the changelog to be explicitly
1631 # reloaded while computing `_bookmarks`. The data race can still happen
1631 # reloaded while computing `_bookmarks`. The data race can still happen
1632 # without the lock (with a narrower window), but it would no longer go
1632 # without the lock (with a narrower window), but it would no longer go
1633 # undetected during the lock time refresh.
1633 # undetected during the lock time refresh.
1634 #
1634 #
1635 # The new schedule is as follow
1635 # The new schedule is as follow
1636 #
1636 #
1637 # 1) filecache logic detect that `_bookmarks` needs to be computed
1637 # 1) filecache logic detect that `_bookmarks` needs to be computed
1638 # 2) cachestat for `bookmarks` and `changelog` are captured (for book)
1638 # 2) cachestat for `bookmarks` and `changelog` are captured (for book)
1639 # 3) We force `changelog` filecache to be tested
1639 # 3) We force `changelog` filecache to be tested
1640 # 4) cachestat for `changelog` are captured (for changelog)
1640 # 4) cachestat for `changelog` are captured (for changelog)
1641 # 5) `_bookmarks` is computed and cached
1641 # 5) `_bookmarks` is computed and cached
1642 #
1642 #
1643 # The step in (3) ensure we have a changelog at least as recent as the
1643 # The step in (3) ensure we have a changelog at least as recent as the
1644 # cache stat computed in (1). As a result at locking time:
1644 # cache stat computed in (1). As a result at locking time:
1645 # * if the changelog did not changed since (1) -> we can reuse the data
1645 # * if the changelog did not changed since (1) -> we can reuse the data
1646 # * otherwise -> the bookmarks get refreshed.
1646 # * otherwise -> the bookmarks get refreshed.
1647 self._refreshchangelog()
1647 self._refreshchangelog()
1648 return bookmarks.bmstore(self)
1648 return bookmarks.bmstore(self)
1649
1649
1650 def _refreshchangelog(self):
1650 def _refreshchangelog(self):
1651 """make sure the in memory changelog match the on-disk one"""
1651 """make sure the in memory changelog match the on-disk one"""
1652 if 'changelog' in vars(self) and self.currenttransaction() is None:
1652 if 'changelog' in vars(self) and self.currenttransaction() is None:
1653 del self.changelog
1653 del self.changelog
1654
1654
1655 @property
1655 @property
1656 def _activebookmark(self):
1656 def _activebookmark(self):
1657 return self._bookmarks.active
1657 return self._bookmarks.active
1658
1658
1659 # _phasesets depend on changelog. what we need is to call
1659 # _phasesets depend on changelog. what we need is to call
1660 # _phasecache.invalidate() if '00changelog.i' was changed, but it
1660 # _phasecache.invalidate() if '00changelog.i' was changed, but it
1661 # can't be easily expressed in filecache mechanism.
1661 # can't be easily expressed in filecache mechanism.
1662 @storecache(b'phaseroots', b'00changelog.i')
1662 @storecache(b'phaseroots', b'00changelog.i')
1663 def _phasecache(self):
1663 def _phasecache(self):
1664 return phases.phasecache(self, self._phasedefaults)
1664 return phases.phasecache(self, self._phasedefaults)
1665
1665
1666 @storecache(b'obsstore')
1666 @storecache(b'obsstore')
1667 def obsstore(self):
1667 def obsstore(self):
1668 return obsolete.makestore(self.ui, self)
1668 return obsolete.makestore(self.ui, self)
1669
1669
1670 @storecache(b'00changelog.i')
1670 @storecache(b'00changelog.i')
1671 def changelog(self):
1671 def changelog(self):
1672 # load dirstate before changelog to avoid race see issue6303
1672 # load dirstate before changelog to avoid race see issue6303
1673 self.dirstate.prefetch_parents()
1673 self.dirstate.prefetch_parents()
1674 return self.store.changelog(
1674 return self.store.changelog(
1675 txnutil.mayhavepending(self.root),
1675 txnutil.mayhavepending(self.root),
1676 concurrencychecker=revlogchecker.get_checker(self.ui, b'changelog'),
1676 concurrencychecker=revlogchecker.get_checker(self.ui, b'changelog'),
1677 )
1677 )
1678
1678
1679 @storecache(b'00manifest.i')
1679 @storecache(b'00manifest.i')
1680 def manifestlog(self):
1680 def manifestlog(self):
1681 return self.store.manifestlog(self, self._storenarrowmatch)
1681 return self.store.manifestlog(self, self._storenarrowmatch)
1682
1682
1683 @repofilecache(b'dirstate')
1683 @repofilecache(b'dirstate')
1684 def dirstate(self):
1684 def dirstate(self):
1685 return self._makedirstate()
1685 return self._makedirstate()
1686
1686
1687 def _makedirstate(self):
1687 def _makedirstate(self):
1688 """Extension point for wrapping the dirstate per-repo."""
1688 """Extension point for wrapping the dirstate per-repo."""
1689 sparsematchfn = lambda: sparse.matcher(self)
1689 sparsematchfn = lambda: sparse.matcher(self)
1690
1690
1691 return dirstate.dirstate(
1691 return dirstate.dirstate(
1692 self.vfs,
1692 self.vfs,
1693 self.ui,
1693 self.ui,
1694 self.root,
1694 self.root,
1695 self._dirstatevalidate,
1695 self._dirstatevalidate,
1696 sparsematchfn,
1696 sparsematchfn,
1697 self.nodeconstants,
1697 self.nodeconstants,
1698 )
1698 )
1699
1699
1700 def _dirstatevalidate(self, node):
1700 def _dirstatevalidate(self, node):
1701 try:
1701 try:
1702 self.changelog.rev(node)
1702 self.changelog.rev(node)
1703 return node
1703 return node
1704 except error.LookupError:
1704 except error.LookupError:
1705 if not self._dirstatevalidatewarned:
1705 if not self._dirstatevalidatewarned:
1706 self._dirstatevalidatewarned = True
1706 self._dirstatevalidatewarned = True
1707 self.ui.warn(
1707 self.ui.warn(
1708 _(b"warning: ignoring unknown working parent %s!\n")
1708 _(b"warning: ignoring unknown working parent %s!\n")
1709 % short(node)
1709 % short(node)
1710 )
1710 )
1711 return self.nullid
1711 return self.nullid
1712
1712
1713 @storecache(narrowspec.FILENAME)
1713 @storecache(narrowspec.FILENAME)
1714 def narrowpats(self):
1714 def narrowpats(self):
1715 """matcher patterns for this repository's narrowspec
1715 """matcher patterns for this repository's narrowspec
1716
1716
1717 A tuple of (includes, excludes).
1717 A tuple of (includes, excludes).
1718 """
1718 """
1719 return narrowspec.load(self)
1719 return narrowspec.load(self)
1720
1720
1721 @storecache(narrowspec.FILENAME)
1721 @storecache(narrowspec.FILENAME)
1722 def _storenarrowmatch(self):
1722 def _storenarrowmatch(self):
1723 if requirementsmod.NARROW_REQUIREMENT not in self.requirements:
1723 if requirementsmod.NARROW_REQUIREMENT not in self.requirements:
1724 return matchmod.always()
1724 return matchmod.always()
1725 include, exclude = self.narrowpats
1725 include, exclude = self.narrowpats
1726 return narrowspec.match(self.root, include=include, exclude=exclude)
1726 return narrowspec.match(self.root, include=include, exclude=exclude)
1727
1727
1728 @storecache(narrowspec.FILENAME)
1728 @storecache(narrowspec.FILENAME)
1729 def _narrowmatch(self):
1729 def _narrowmatch(self):
1730 if requirementsmod.NARROW_REQUIREMENT not in self.requirements:
1730 if requirementsmod.NARROW_REQUIREMENT not in self.requirements:
1731 return matchmod.always()
1731 return matchmod.always()
1732 narrowspec.checkworkingcopynarrowspec(self)
1732 narrowspec.checkworkingcopynarrowspec(self)
1733 include, exclude = self.narrowpats
1733 include, exclude = self.narrowpats
1734 return narrowspec.match(self.root, include=include, exclude=exclude)
1734 return narrowspec.match(self.root, include=include, exclude=exclude)
1735
1735
1736 def narrowmatch(self, match=None, includeexact=False):
1736 def narrowmatch(self, match=None, includeexact=False):
1737 """matcher corresponding the the repo's narrowspec
1737 """matcher corresponding the the repo's narrowspec
1738
1738
1739 If `match` is given, then that will be intersected with the narrow
1739 If `match` is given, then that will be intersected with the narrow
1740 matcher.
1740 matcher.
1741
1741
1742 If `includeexact` is True, then any exact matches from `match` will
1742 If `includeexact` is True, then any exact matches from `match` will
1743 be included even if they're outside the narrowspec.
1743 be included even if they're outside the narrowspec.
1744 """
1744 """
1745 if match:
1745 if match:
1746 if includeexact and not self._narrowmatch.always():
1746 if includeexact and not self._narrowmatch.always():
1747 # do not exclude explicitly-specified paths so that they can
1747 # do not exclude explicitly-specified paths so that they can
1748 # be warned later on
1748 # be warned later on
1749 em = matchmod.exact(match.files())
1749 em = matchmod.exact(match.files())
1750 nm = matchmod.unionmatcher([self._narrowmatch, em])
1750 nm = matchmod.unionmatcher([self._narrowmatch, em])
1751 return matchmod.intersectmatchers(match, nm)
1751 return matchmod.intersectmatchers(match, nm)
1752 return matchmod.intersectmatchers(match, self._narrowmatch)
1752 return matchmod.intersectmatchers(match, self._narrowmatch)
1753 return self._narrowmatch
1753 return self._narrowmatch
1754
1754
1755 def setnarrowpats(self, newincludes, newexcludes):
1755 def setnarrowpats(self, newincludes, newexcludes):
1756 narrowspec.save(self, newincludes, newexcludes)
1756 narrowspec.save(self, newincludes, newexcludes)
1757 self.invalidate(clearfilecache=True)
1757 self.invalidate(clearfilecache=True)
1758
1758
1759 @unfilteredpropertycache
1759 @unfilteredpropertycache
1760 def _quick_access_changeid_null(self):
1760 def _quick_access_changeid_null(self):
1761 return {
1761 return {
1762 b'null': (nullrev, self.nodeconstants.nullid),
1762 b'null': (nullrev, self.nodeconstants.nullid),
1763 nullrev: (nullrev, self.nodeconstants.nullid),
1763 nullrev: (nullrev, self.nodeconstants.nullid),
1764 self.nullid: (nullrev, self.nullid),
1764 self.nullid: (nullrev, self.nullid),
1765 }
1765 }
1766
1766
1767 @unfilteredpropertycache
1767 @unfilteredpropertycache
1768 def _quick_access_changeid_wc(self):
1768 def _quick_access_changeid_wc(self):
1769 # also fast path access to the working copy parents
1769 # also fast path access to the working copy parents
1770 # however, only do it for filter that ensure wc is visible.
1770 # however, only do it for filter that ensure wc is visible.
1771 quick = self._quick_access_changeid_null.copy()
1771 quick = self._quick_access_changeid_null.copy()
1772 cl = self.unfiltered().changelog
1772 cl = self.unfiltered().changelog
1773 for node in self.dirstate.parents():
1773 for node in self.dirstate.parents():
1774 if node == self.nullid:
1774 if node == self.nullid:
1775 continue
1775 continue
1776 rev = cl.index.get_rev(node)
1776 rev = cl.index.get_rev(node)
1777 if rev is None:
1777 if rev is None:
1778 # unknown working copy parent case:
1778 # unknown working copy parent case:
1779 #
1779 #
1780 # skip the fast path and let higher code deal with it
1780 # skip the fast path and let higher code deal with it
1781 continue
1781 continue
1782 pair = (rev, node)
1782 pair = (rev, node)
1783 quick[rev] = pair
1783 quick[rev] = pair
1784 quick[node] = pair
1784 quick[node] = pair
1785 # also add the parents of the parents
1785 # also add the parents of the parents
1786 for r in cl.parentrevs(rev):
1786 for r in cl.parentrevs(rev):
1787 if r == nullrev:
1787 if r == nullrev:
1788 continue
1788 continue
1789 n = cl.node(r)
1789 n = cl.node(r)
1790 pair = (r, n)
1790 pair = (r, n)
1791 quick[r] = pair
1791 quick[r] = pair
1792 quick[n] = pair
1792 quick[n] = pair
1793 p1node = self.dirstate.p1()
1793 p1node = self.dirstate.p1()
1794 if p1node != self.nullid:
1794 if p1node != self.nullid:
1795 quick[b'.'] = quick[p1node]
1795 quick[b'.'] = quick[p1node]
1796 return quick
1796 return quick
1797
1797
1798 @unfilteredmethod
1798 @unfilteredmethod
1799 def _quick_access_changeid_invalidate(self):
1799 def _quick_access_changeid_invalidate(self):
1800 if '_quick_access_changeid_wc' in vars(self):
1800 if '_quick_access_changeid_wc' in vars(self):
1801 del self.__dict__['_quick_access_changeid_wc']
1801 del self.__dict__['_quick_access_changeid_wc']
1802
1802
1803 @property
1803 @property
1804 def _quick_access_changeid(self):
1804 def _quick_access_changeid(self):
1805 """an helper dictionnary for __getitem__ calls
1805 """an helper dictionnary for __getitem__ calls
1806
1806
1807 This contains a list of symbol we can recognise right away without
1807 This contains a list of symbol we can recognise right away without
1808 further processing.
1808 further processing.
1809 """
1809 """
1810 if self.filtername in repoview.filter_has_wc:
1810 if self.filtername in repoview.filter_has_wc:
1811 return self._quick_access_changeid_wc
1811 return self._quick_access_changeid_wc
1812 return self._quick_access_changeid_null
1812 return self._quick_access_changeid_null
1813
1813
1814 def __getitem__(self, changeid):
1814 def __getitem__(self, changeid):
1815 # dealing with special cases
1815 # dealing with special cases
1816 if changeid is None:
1816 if changeid is None:
1817 return context.workingctx(self)
1817 return context.workingctx(self)
1818 if isinstance(changeid, context.basectx):
1818 if isinstance(changeid, context.basectx):
1819 return changeid
1819 return changeid
1820
1820
1821 # dealing with multiple revisions
1821 # dealing with multiple revisions
1822 if isinstance(changeid, slice):
1822 if isinstance(changeid, slice):
1823 # wdirrev isn't contiguous so the slice shouldn't include it
1823 # wdirrev isn't contiguous so the slice shouldn't include it
1824 return [
1824 return [
1825 self[i]
1825 self[i]
1826 for i in pycompat.xrange(*changeid.indices(len(self)))
1826 for i in pycompat.xrange(*changeid.indices(len(self)))
1827 if i not in self.changelog.filteredrevs
1827 if i not in self.changelog.filteredrevs
1828 ]
1828 ]
1829
1829
1830 # dealing with some special values
1830 # dealing with some special values
1831 quick_access = self._quick_access_changeid.get(changeid)
1831 quick_access = self._quick_access_changeid.get(changeid)
1832 if quick_access is not None:
1832 if quick_access is not None:
1833 rev, node = quick_access
1833 rev, node = quick_access
1834 return context.changectx(self, rev, node, maybe_filtered=False)
1834 return context.changectx(self, rev, node, maybe_filtered=False)
1835 if changeid == b'tip':
1835 if changeid == b'tip':
1836 node = self.changelog.tip()
1836 node = self.changelog.tip()
1837 rev = self.changelog.rev(node)
1837 rev = self.changelog.rev(node)
1838 return context.changectx(self, rev, node)
1838 return context.changectx(self, rev, node)
1839
1839
1840 # dealing with arbitrary values
1840 # dealing with arbitrary values
1841 try:
1841 try:
1842 if isinstance(changeid, int):
1842 if isinstance(changeid, int):
1843 node = self.changelog.node(changeid)
1843 node = self.changelog.node(changeid)
1844 rev = changeid
1844 rev = changeid
1845 elif changeid == b'.':
1845 elif changeid == b'.':
1846 # this is a hack to delay/avoid loading obsmarkers
1846 # this is a hack to delay/avoid loading obsmarkers
1847 # when we know that '.' won't be hidden
1847 # when we know that '.' won't be hidden
1848 node = self.dirstate.p1()
1848 node = self.dirstate.p1()
1849 rev = self.unfiltered().changelog.rev(node)
1849 rev = self.unfiltered().changelog.rev(node)
1850 elif len(changeid) == self.nodeconstants.nodelen:
1850 elif len(changeid) == self.nodeconstants.nodelen:
1851 try:
1851 try:
1852 node = changeid
1852 node = changeid
1853 rev = self.changelog.rev(changeid)
1853 rev = self.changelog.rev(changeid)
1854 except error.FilteredLookupError:
1854 except error.FilteredLookupError:
1855 changeid = hex(changeid) # for the error message
1855 changeid = hex(changeid) # for the error message
1856 raise
1856 raise
1857 except LookupError:
1857 except LookupError:
1858 # check if it might have come from damaged dirstate
1858 # check if it might have come from damaged dirstate
1859 #
1859 #
1860 # XXX we could avoid the unfiltered if we had a recognizable
1860 # XXX we could avoid the unfiltered if we had a recognizable
1861 # exception for filtered changeset access
1861 # exception for filtered changeset access
1862 if (
1862 if (
1863 self.local()
1863 self.local()
1864 and changeid in self.unfiltered().dirstate.parents()
1864 and changeid in self.unfiltered().dirstate.parents()
1865 ):
1865 ):
1866 msg = _(b"working directory has unknown parent '%s'!")
1866 msg = _(b"working directory has unknown parent '%s'!")
1867 raise error.Abort(msg % short(changeid))
1867 raise error.Abort(msg % short(changeid))
1868 changeid = hex(changeid) # for the error message
1868 changeid = hex(changeid) # for the error message
1869 raise
1869 raise
1870
1870
1871 elif len(changeid) == 2 * self.nodeconstants.nodelen:
1871 elif len(changeid) == 2 * self.nodeconstants.nodelen:
1872 node = bin(changeid)
1872 node = bin(changeid)
1873 rev = self.changelog.rev(node)
1873 rev = self.changelog.rev(node)
1874 else:
1874 else:
1875 raise error.ProgrammingError(
1875 raise error.ProgrammingError(
1876 b"unsupported changeid '%s' of type %s"
1876 b"unsupported changeid '%s' of type %s"
1877 % (changeid, pycompat.bytestr(type(changeid)))
1877 % (changeid, pycompat.bytestr(type(changeid)))
1878 )
1878 )
1879
1879
1880 return context.changectx(self, rev, node)
1880 return context.changectx(self, rev, node)
1881
1881
1882 except (error.FilteredIndexError, error.FilteredLookupError):
1882 except (error.FilteredIndexError, error.FilteredLookupError):
1883 raise error.FilteredRepoLookupError(
1883 raise error.FilteredRepoLookupError(
1884 _(b"filtered revision '%s'") % pycompat.bytestr(changeid)
1884 _(b"filtered revision '%s'") % pycompat.bytestr(changeid)
1885 )
1885 )
1886 except (IndexError, LookupError):
1886 except (IndexError, LookupError):
1887 raise error.RepoLookupError(
1887 raise error.RepoLookupError(
1888 _(b"unknown revision '%s'") % pycompat.bytestr(changeid)
1888 _(b"unknown revision '%s'") % pycompat.bytestr(changeid)
1889 )
1889 )
1890 except error.WdirUnsupported:
1890 except error.WdirUnsupported:
1891 return context.workingctx(self)
1891 return context.workingctx(self)
1892
1892
1893 def __contains__(self, changeid):
1893 def __contains__(self, changeid):
1894 """True if the given changeid exists"""
1894 """True if the given changeid exists"""
1895 try:
1895 try:
1896 self[changeid]
1896 self[changeid]
1897 return True
1897 return True
1898 except error.RepoLookupError:
1898 except error.RepoLookupError:
1899 return False
1899 return False
1900
1900
1901 def __nonzero__(self):
1901 def __nonzero__(self):
1902 return True
1902 return True
1903
1903
1904 __bool__ = __nonzero__
1904 __bool__ = __nonzero__
1905
1905
1906 def __len__(self):
1906 def __len__(self):
1907 # no need to pay the cost of repoview.changelog
1907 # no need to pay the cost of repoview.changelog
1908 unfi = self.unfiltered()
1908 unfi = self.unfiltered()
1909 return len(unfi.changelog)
1909 return len(unfi.changelog)
1910
1910
1911 def __iter__(self):
1911 def __iter__(self):
1912 return iter(self.changelog)
1912 return iter(self.changelog)
1913
1913
1914 def revs(self, expr, *args):
1914 def revs(self, expr, *args):
1915 """Find revisions matching a revset.
1915 """Find revisions matching a revset.
1916
1916
1917 The revset is specified as a string ``expr`` that may contain
1917 The revset is specified as a string ``expr`` that may contain
1918 %-formatting to escape certain types. See ``revsetlang.formatspec``.
1918 %-formatting to escape certain types. See ``revsetlang.formatspec``.
1919
1919
1920 Revset aliases from the configuration are not expanded. To expand
1920 Revset aliases from the configuration are not expanded. To expand
1921 user aliases, consider calling ``scmutil.revrange()`` or
1921 user aliases, consider calling ``scmutil.revrange()`` or
1922 ``repo.anyrevs([expr], user=True)``.
1922 ``repo.anyrevs([expr], user=True)``.
1923
1923
1924 Returns a smartset.abstractsmartset, which is a list-like interface
1924 Returns a smartset.abstractsmartset, which is a list-like interface
1925 that contains integer revisions.
1925 that contains integer revisions.
1926 """
1926 """
1927 tree = revsetlang.spectree(expr, *args)
1927 tree = revsetlang.spectree(expr, *args)
1928 return revset.makematcher(tree)(self)
1928 return revset.makematcher(tree)(self)
1929
1929
1930 def set(self, expr, *args):
1930 def set(self, expr, *args):
1931 """Find revisions matching a revset and emit changectx instances.
1931 """Find revisions matching a revset and emit changectx instances.
1932
1932
1933 This is a convenience wrapper around ``revs()`` that iterates the
1933 This is a convenience wrapper around ``revs()`` that iterates the
1934 result and is a generator of changectx instances.
1934 result and is a generator of changectx instances.
1935
1935
1936 Revset aliases from the configuration are not expanded. To expand
1936 Revset aliases from the configuration are not expanded. To expand
1937 user aliases, consider calling ``scmutil.revrange()``.
1937 user aliases, consider calling ``scmutil.revrange()``.
1938 """
1938 """
1939 for r in self.revs(expr, *args):
1939 for r in self.revs(expr, *args):
1940 yield self[r]
1940 yield self[r]
1941
1941
1942 def anyrevs(self, specs, user=False, localalias=None):
1942 def anyrevs(self, specs, user=False, localalias=None):
1943 """Find revisions matching one of the given revsets.
1943 """Find revisions matching one of the given revsets.
1944
1944
1945 Revset aliases from the configuration are not expanded by default. To
1945 Revset aliases from the configuration are not expanded by default. To
1946 expand user aliases, specify ``user=True``. To provide some local
1946 expand user aliases, specify ``user=True``. To provide some local
1947 definitions overriding user aliases, set ``localalias`` to
1947 definitions overriding user aliases, set ``localalias`` to
1948 ``{name: definitionstring}``.
1948 ``{name: definitionstring}``.
1949 """
1949 """
1950 if specs == [b'null']:
1950 if specs == [b'null']:
1951 return revset.baseset([nullrev])
1951 return revset.baseset([nullrev])
1952 if specs == [b'.']:
1952 if specs == [b'.']:
1953 quick_data = self._quick_access_changeid.get(b'.')
1953 quick_data = self._quick_access_changeid.get(b'.')
1954 if quick_data is not None:
1954 if quick_data is not None:
1955 return revset.baseset([quick_data[0]])
1955 return revset.baseset([quick_data[0]])
1956 if user:
1956 if user:
1957 m = revset.matchany(
1957 m = revset.matchany(
1958 self.ui,
1958 self.ui,
1959 specs,
1959 specs,
1960 lookup=revset.lookupfn(self),
1960 lookup=revset.lookupfn(self),
1961 localalias=localalias,
1961 localalias=localalias,
1962 )
1962 )
1963 else:
1963 else:
1964 m = revset.matchany(None, specs, localalias=localalias)
1964 m = revset.matchany(None, specs, localalias=localalias)
1965 return m(self)
1965 return m(self)
1966
1966
1967 def url(self):
1967 def url(self):
1968 return b'file:' + self.root
1968 return b'file:' + self.root
1969
1969
1970 def hook(self, name, throw=False, **args):
1970 def hook(self, name, throw=False, **args):
1971 """Call a hook, passing this repo instance.
1971 """Call a hook, passing this repo instance.
1972
1972
1973 This a convenience method to aid invoking hooks. Extensions likely
1973 This a convenience method to aid invoking hooks. Extensions likely
1974 won't call this unless they have registered a custom hook or are
1974 won't call this unless they have registered a custom hook or are
1975 replacing code that is expected to call a hook.
1975 replacing code that is expected to call a hook.
1976 """
1976 """
1977 return hook.hook(self.ui, self, name, throw, **args)
1977 return hook.hook(self.ui, self, name, throw, **args)
1978
1978
1979 @filteredpropertycache
1979 @filteredpropertycache
1980 def _tagscache(self):
1980 def _tagscache(self):
1981 """Returns a tagscache object that contains various tags related
1981 """Returns a tagscache object that contains various tags related
1982 caches."""
1982 caches."""
1983
1983
1984 # This simplifies its cache management by having one decorated
1984 # This simplifies its cache management by having one decorated
1985 # function (this one) and the rest simply fetch things from it.
1985 # function (this one) and the rest simply fetch things from it.
1986 class tagscache(object):
1986 class tagscache(object):
1987 def __init__(self):
1987 def __init__(self):
1988 # These two define the set of tags for this repository. tags
1988 # These two define the set of tags for this repository. tags
1989 # maps tag name to node; tagtypes maps tag name to 'global' or
1989 # maps tag name to node; tagtypes maps tag name to 'global' or
1990 # 'local'. (Global tags are defined by .hgtags across all
1990 # 'local'. (Global tags are defined by .hgtags across all
1991 # heads, and local tags are defined in .hg/localtags.)
1991 # heads, and local tags are defined in .hg/localtags.)
1992 # They constitute the in-memory cache of tags.
1992 # They constitute the in-memory cache of tags.
1993 self.tags = self.tagtypes = None
1993 self.tags = self.tagtypes = None
1994
1994
1995 self.nodetagscache = self.tagslist = None
1995 self.nodetagscache = self.tagslist = None
1996
1996
1997 cache = tagscache()
1997 cache = tagscache()
1998 cache.tags, cache.tagtypes = self._findtags()
1998 cache.tags, cache.tagtypes = self._findtags()
1999
1999
2000 return cache
2000 return cache
2001
2001
2002 def tags(self):
2002 def tags(self):
2003 '''return a mapping of tag to node'''
2003 '''return a mapping of tag to node'''
2004 t = {}
2004 t = {}
2005 if self.changelog.filteredrevs:
2005 if self.changelog.filteredrevs:
2006 tags, tt = self._findtags()
2006 tags, tt = self._findtags()
2007 else:
2007 else:
2008 tags = self._tagscache.tags
2008 tags = self._tagscache.tags
2009 rev = self.changelog.rev
2009 rev = self.changelog.rev
2010 for k, v in pycompat.iteritems(tags):
2010 for k, v in pycompat.iteritems(tags):
2011 try:
2011 try:
2012 # ignore tags to unknown nodes
2012 # ignore tags to unknown nodes
2013 rev(v)
2013 rev(v)
2014 t[k] = v
2014 t[k] = v
2015 except (error.LookupError, ValueError):
2015 except (error.LookupError, ValueError):
2016 pass
2016 pass
2017 return t
2017 return t
2018
2018
2019 def _findtags(self):
2019 def _findtags(self):
2020 """Do the hard work of finding tags. Return a pair of dicts
2020 """Do the hard work of finding tags. Return a pair of dicts
2021 (tags, tagtypes) where tags maps tag name to node, and tagtypes
2021 (tags, tagtypes) where tags maps tag name to node, and tagtypes
2022 maps tag name to a string like \'global\' or \'local\'.
2022 maps tag name to a string like \'global\' or \'local\'.
2023 Subclasses or extensions are free to add their own tags, but
2023 Subclasses or extensions are free to add their own tags, but
2024 should be aware that the returned dicts will be retained for the
2024 should be aware that the returned dicts will be retained for the
2025 duration of the localrepo object."""
2025 duration of the localrepo object."""
2026
2026
2027 # XXX what tagtype should subclasses/extensions use? Currently
2027 # XXX what tagtype should subclasses/extensions use? Currently
2028 # mq and bookmarks add tags, but do not set the tagtype at all.
2028 # mq and bookmarks add tags, but do not set the tagtype at all.
2029 # Should each extension invent its own tag type? Should there
2029 # Should each extension invent its own tag type? Should there
2030 # be one tagtype for all such "virtual" tags? Or is the status
2030 # be one tagtype for all such "virtual" tags? Or is the status
2031 # quo fine?
2031 # quo fine?
2032
2032
2033 # map tag name to (node, hist)
2033 # map tag name to (node, hist)
2034 alltags = tagsmod.findglobaltags(self.ui, self)
2034 alltags = tagsmod.findglobaltags(self.ui, self)
2035 # map tag name to tag type
2035 # map tag name to tag type
2036 tagtypes = {tag: b'global' for tag in alltags}
2036 tagtypes = {tag: b'global' for tag in alltags}
2037
2037
2038 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
2038 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
2039
2039
2040 # Build the return dicts. Have to re-encode tag names because
2040 # Build the return dicts. Have to re-encode tag names because
2041 # the tags module always uses UTF-8 (in order not to lose info
2041 # the tags module always uses UTF-8 (in order not to lose info
2042 # writing to the cache), but the rest of Mercurial wants them in
2042 # writing to the cache), but the rest of Mercurial wants them in
2043 # local encoding.
2043 # local encoding.
2044 tags = {}
2044 tags = {}
2045 for (name, (node, hist)) in pycompat.iteritems(alltags):
2045 for (name, (node, hist)) in pycompat.iteritems(alltags):
2046 if node != self.nullid:
2046 if node != self.nullid:
2047 tags[encoding.tolocal(name)] = node
2047 tags[encoding.tolocal(name)] = node
2048 tags[b'tip'] = self.changelog.tip()
2048 tags[b'tip'] = self.changelog.tip()
2049 tagtypes = {
2049 tagtypes = {
2050 encoding.tolocal(name): value
2050 encoding.tolocal(name): value
2051 for (name, value) in pycompat.iteritems(tagtypes)
2051 for (name, value) in pycompat.iteritems(tagtypes)
2052 }
2052 }
2053 return (tags, tagtypes)
2053 return (tags, tagtypes)
2054
2054
2055 def tagtype(self, tagname):
2055 def tagtype(self, tagname):
2056 """
2056 """
2057 return the type of the given tag. result can be:
2057 return the type of the given tag. result can be:
2058
2058
2059 'local' : a local tag
2059 'local' : a local tag
2060 'global' : a global tag
2060 'global' : a global tag
2061 None : tag does not exist
2061 None : tag does not exist
2062 """
2062 """
2063
2063
2064 return self._tagscache.tagtypes.get(tagname)
2064 return self._tagscache.tagtypes.get(tagname)
2065
2065
2066 def tagslist(self):
2066 def tagslist(self):
2067 '''return a list of tags ordered by revision'''
2067 '''return a list of tags ordered by revision'''
2068 if not self._tagscache.tagslist:
2068 if not self._tagscache.tagslist:
2069 l = []
2069 l = []
2070 for t, n in pycompat.iteritems(self.tags()):
2070 for t, n in pycompat.iteritems(self.tags()):
2071 l.append((self.changelog.rev(n), t, n))
2071 l.append((self.changelog.rev(n), t, n))
2072 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
2072 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
2073
2073
2074 return self._tagscache.tagslist
2074 return self._tagscache.tagslist
2075
2075
2076 def nodetags(self, node):
2076 def nodetags(self, node):
2077 '''return the tags associated with a node'''
2077 '''return the tags associated with a node'''
2078 if not self._tagscache.nodetagscache:
2078 if not self._tagscache.nodetagscache:
2079 nodetagscache = {}
2079 nodetagscache = {}
2080 for t, n in pycompat.iteritems(self._tagscache.tags):
2080 for t, n in pycompat.iteritems(self._tagscache.tags):
2081 nodetagscache.setdefault(n, []).append(t)
2081 nodetagscache.setdefault(n, []).append(t)
2082 for tags in pycompat.itervalues(nodetagscache):
2082 for tags in pycompat.itervalues(nodetagscache):
2083 tags.sort()
2083 tags.sort()
2084 self._tagscache.nodetagscache = nodetagscache
2084 self._tagscache.nodetagscache = nodetagscache
2085 return self._tagscache.nodetagscache.get(node, [])
2085 return self._tagscache.nodetagscache.get(node, [])
2086
2086
2087 def nodebookmarks(self, node):
2087 def nodebookmarks(self, node):
2088 """return the list of bookmarks pointing to the specified node"""
2088 """return the list of bookmarks pointing to the specified node"""
2089 return self._bookmarks.names(node)
2089 return self._bookmarks.names(node)
2090
2090
2091 def branchmap(self):
2091 def branchmap(self):
2092 """returns a dictionary {branch: [branchheads]} with branchheads
2092 """returns a dictionary {branch: [branchheads]} with branchheads
2093 ordered by increasing revision number"""
2093 ordered by increasing revision number"""
2094 return self._branchcaches[self]
2094 return self._branchcaches[self]
2095
2095
2096 @unfilteredmethod
2096 @unfilteredmethod
2097 def revbranchcache(self):
2097 def revbranchcache(self):
2098 if not self._revbranchcache:
2098 if not self._revbranchcache:
2099 self._revbranchcache = branchmap.revbranchcache(self.unfiltered())
2099 self._revbranchcache = branchmap.revbranchcache(self.unfiltered())
2100 return self._revbranchcache
2100 return self._revbranchcache
2101
2101
2102 def register_changeset(self, rev, changelogrevision):
2102 def register_changeset(self, rev, changelogrevision):
2103 self.revbranchcache().setdata(rev, changelogrevision)
2103 self.revbranchcache().setdata(rev, changelogrevision)
2104
2104
2105 def branchtip(self, branch, ignoremissing=False):
2105 def branchtip(self, branch, ignoremissing=False):
2106 """return the tip node for a given branch
2106 """return the tip node for a given branch
2107
2107
2108 If ignoremissing is True, then this method will not raise an error.
2108 If ignoremissing is True, then this method will not raise an error.
2109 This is helpful for callers that only expect None for a missing branch
2109 This is helpful for callers that only expect None for a missing branch
2110 (e.g. namespace).
2110 (e.g. namespace).
2111
2111
2112 """
2112 """
2113 try:
2113 try:
2114 return self.branchmap().branchtip(branch)
2114 return self.branchmap().branchtip(branch)
2115 except KeyError:
2115 except KeyError:
2116 if not ignoremissing:
2116 if not ignoremissing:
2117 raise error.RepoLookupError(_(b"unknown branch '%s'") % branch)
2117 raise error.RepoLookupError(_(b"unknown branch '%s'") % branch)
2118 else:
2118 else:
2119 pass
2119 pass
2120
2120
2121 def lookup(self, key):
2121 def lookup(self, key):
2122 node = scmutil.revsymbol(self, key).node()
2122 node = scmutil.revsymbol(self, key).node()
2123 if node is None:
2123 if node is None:
2124 raise error.RepoLookupError(_(b"unknown revision '%s'") % key)
2124 raise error.RepoLookupError(_(b"unknown revision '%s'") % key)
2125 return node
2125 return node
2126
2126
2127 def lookupbranch(self, key):
2127 def lookupbranch(self, key):
2128 if self.branchmap().hasbranch(key):
2128 if self.branchmap().hasbranch(key):
2129 return key
2129 return key
2130
2130
2131 return scmutil.revsymbol(self, key).branch()
2131 return scmutil.revsymbol(self, key).branch()
2132
2132
2133 def known(self, nodes):
2133 def known(self, nodes):
2134 cl = self.changelog
2134 cl = self.changelog
2135 get_rev = cl.index.get_rev
2135 get_rev = cl.index.get_rev
2136 filtered = cl.filteredrevs
2136 filtered = cl.filteredrevs
2137 result = []
2137 result = []
2138 for n in nodes:
2138 for n in nodes:
2139 r = get_rev(n)
2139 r = get_rev(n)
2140 resp = not (r is None or r in filtered)
2140 resp = not (r is None or r in filtered)
2141 result.append(resp)
2141 result.append(resp)
2142 return result
2142 return result
2143
2143
2144 def local(self):
2144 def local(self):
2145 return self
2145 return self
2146
2146
2147 def publishing(self):
2147 def publishing(self):
2148 # it's safe (and desirable) to trust the publish flag unconditionally
2148 # it's safe (and desirable) to trust the publish flag unconditionally
2149 # so that we don't finalize changes shared between users via ssh or nfs
2149 # so that we don't finalize changes shared between users via ssh or nfs
2150 return self.ui.configbool(b'phases', b'publish', untrusted=True)
2150 return self.ui.configbool(b'phases', b'publish', untrusted=True)
2151
2151
2152 def cancopy(self):
2152 def cancopy(self):
2153 # so statichttprepo's override of local() works
2153 # so statichttprepo's override of local() works
2154 if not self.local():
2154 if not self.local():
2155 return False
2155 return False
2156 if not self.publishing():
2156 if not self.publishing():
2157 return True
2157 return True
2158 # if publishing we can't copy if there is filtered content
2158 # if publishing we can't copy if there is filtered content
2159 return not self.filtered(b'visible').changelog.filteredrevs
2159 return not self.filtered(b'visible').changelog.filteredrevs
2160
2160
2161 def shared(self):
2161 def shared(self):
2162 '''the type of shared repository (None if not shared)'''
2162 '''the type of shared repository (None if not shared)'''
2163 if self.sharedpath != self.path:
2163 if self.sharedpath != self.path:
2164 return b'store'
2164 return b'store'
2165 return None
2165 return None
2166
2166
2167 def wjoin(self, f, *insidef):
2167 def wjoin(self, f, *insidef):
2168 return self.vfs.reljoin(self.root, f, *insidef)
2168 return self.vfs.reljoin(self.root, f, *insidef)
2169
2169
2170 def setparents(self, p1, p2=None):
2170 def setparents(self, p1, p2=None):
2171 if p2 is None:
2171 if p2 is None:
2172 p2 = self.nullid
2172 p2 = self.nullid
2173 self[None].setparents(p1, p2)
2173 self[None].setparents(p1, p2)
2174 self._quick_access_changeid_invalidate()
2174 self._quick_access_changeid_invalidate()
2175
2175
2176 def filectx(self, path, changeid=None, fileid=None, changectx=None):
2176 def filectx(self, path, changeid=None, fileid=None, changectx=None):
2177 """changeid must be a changeset revision, if specified.
2177 """changeid must be a changeset revision, if specified.
2178 fileid can be a file revision or node."""
2178 fileid can be a file revision or node."""
2179 return context.filectx(
2179 return context.filectx(
2180 self, path, changeid, fileid, changectx=changectx
2180 self, path, changeid, fileid, changectx=changectx
2181 )
2181 )
2182
2182
2183 def getcwd(self):
2183 def getcwd(self):
2184 return self.dirstate.getcwd()
2184 return self.dirstate.getcwd()
2185
2185
2186 def pathto(self, f, cwd=None):
2186 def pathto(self, f, cwd=None):
2187 return self.dirstate.pathto(f, cwd)
2187 return self.dirstate.pathto(f, cwd)
2188
2188
2189 def _loadfilter(self, filter):
2189 def _loadfilter(self, filter):
2190 if filter not in self._filterpats:
2190 if filter not in self._filterpats:
2191 l = []
2191 l = []
2192 for pat, cmd in self.ui.configitems(filter):
2192 for pat, cmd in self.ui.configitems(filter):
2193 if cmd == b'!':
2193 if cmd == b'!':
2194 continue
2194 continue
2195 mf = matchmod.match(self.root, b'', [pat])
2195 mf = matchmod.match(self.root, b'', [pat])
2196 fn = None
2196 fn = None
2197 params = cmd
2197 params = cmd
2198 for name, filterfn in pycompat.iteritems(self._datafilters):
2198 for name, filterfn in pycompat.iteritems(self._datafilters):
2199 if cmd.startswith(name):
2199 if cmd.startswith(name):
2200 fn = filterfn
2200 fn = filterfn
2201 params = cmd[len(name) :].lstrip()
2201 params = cmd[len(name) :].lstrip()
2202 break
2202 break
2203 if not fn:
2203 if not fn:
2204 fn = lambda s, c, **kwargs: procutil.filter(s, c)
2204 fn = lambda s, c, **kwargs: procutil.filter(s, c)
2205 fn.__name__ = 'commandfilter'
2205 fn.__name__ = 'commandfilter'
2206 # Wrap old filters not supporting keyword arguments
2206 # Wrap old filters not supporting keyword arguments
2207 if not pycompat.getargspec(fn)[2]:
2207 if not pycompat.getargspec(fn)[2]:
2208 oldfn = fn
2208 oldfn = fn
2209 fn = lambda s, c, oldfn=oldfn, **kwargs: oldfn(s, c)
2209 fn = lambda s, c, oldfn=oldfn, **kwargs: oldfn(s, c)
2210 fn.__name__ = 'compat-' + oldfn.__name__
2210 fn.__name__ = 'compat-' + oldfn.__name__
2211 l.append((mf, fn, params))
2211 l.append((mf, fn, params))
2212 self._filterpats[filter] = l
2212 self._filterpats[filter] = l
2213 return self._filterpats[filter]
2213 return self._filterpats[filter]
2214
2214
2215 def _filter(self, filterpats, filename, data):
2215 def _filter(self, filterpats, filename, data):
2216 for mf, fn, cmd in filterpats:
2216 for mf, fn, cmd in filterpats:
2217 if mf(filename):
2217 if mf(filename):
2218 self.ui.debug(
2218 self.ui.debug(
2219 b"filtering %s through %s\n"
2219 b"filtering %s through %s\n"
2220 % (filename, cmd or pycompat.sysbytes(fn.__name__))
2220 % (filename, cmd or pycompat.sysbytes(fn.__name__))
2221 )
2221 )
2222 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
2222 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
2223 break
2223 break
2224
2224
2225 return data
2225 return data
2226
2226
2227 @unfilteredpropertycache
2227 @unfilteredpropertycache
2228 def _encodefilterpats(self):
2228 def _encodefilterpats(self):
2229 return self._loadfilter(b'encode')
2229 return self._loadfilter(b'encode')
2230
2230
2231 @unfilteredpropertycache
2231 @unfilteredpropertycache
2232 def _decodefilterpats(self):
2232 def _decodefilterpats(self):
2233 return self._loadfilter(b'decode')
2233 return self._loadfilter(b'decode')
2234
2234
2235 def adddatafilter(self, name, filter):
2235 def adddatafilter(self, name, filter):
2236 self._datafilters[name] = filter
2236 self._datafilters[name] = filter
2237
2237
2238 def wread(self, filename):
2238 def wread(self, filename):
2239 if self.wvfs.islink(filename):
2239 if self.wvfs.islink(filename):
2240 data = self.wvfs.readlink(filename)
2240 data = self.wvfs.readlink(filename)
2241 else:
2241 else:
2242 data = self.wvfs.read(filename)
2242 data = self.wvfs.read(filename)
2243 return self._filter(self._encodefilterpats, filename, data)
2243 return self._filter(self._encodefilterpats, filename, data)
2244
2244
2245 def wwrite(self, filename, data, flags, backgroundclose=False, **kwargs):
2245 def wwrite(self, filename, data, flags, backgroundclose=False, **kwargs):
2246 """write ``data`` into ``filename`` in the working directory
2246 """write ``data`` into ``filename`` in the working directory
2247
2247
2248 This returns length of written (maybe decoded) data.
2248 This returns length of written (maybe decoded) data.
2249 """
2249 """
2250 data = self._filter(self._decodefilterpats, filename, data)
2250 data = self._filter(self._decodefilterpats, filename, data)
2251 if b'l' in flags:
2251 if b'l' in flags:
2252 self.wvfs.symlink(data, filename)
2252 self.wvfs.symlink(data, filename)
2253 else:
2253 else:
2254 self.wvfs.write(
2254 self.wvfs.write(
2255 filename, data, backgroundclose=backgroundclose, **kwargs
2255 filename, data, backgroundclose=backgroundclose, **kwargs
2256 )
2256 )
2257 if b'x' in flags:
2257 if b'x' in flags:
2258 self.wvfs.setflags(filename, False, True)
2258 self.wvfs.setflags(filename, False, True)
2259 else:
2259 else:
2260 self.wvfs.setflags(filename, False, False)
2260 self.wvfs.setflags(filename, False, False)
2261 return len(data)
2261 return len(data)
2262
2262
2263 def wwritedata(self, filename, data):
2263 def wwritedata(self, filename, data):
2264 return self._filter(self._decodefilterpats, filename, data)
2264 return self._filter(self._decodefilterpats, filename, data)
2265
2265
2266 def currenttransaction(self):
2266 def currenttransaction(self):
2267 """return the current transaction or None if non exists"""
2267 """return the current transaction or None if non exists"""
2268 if self._transref:
2268 if self._transref:
2269 tr = self._transref()
2269 tr = self._transref()
2270 else:
2270 else:
2271 tr = None
2271 tr = None
2272
2272
2273 if tr and tr.running():
2273 if tr and tr.running():
2274 return tr
2274 return tr
2275 return None
2275 return None
2276
2276
2277 def transaction(self, desc, report=None):
2277 def transaction(self, desc, report=None):
2278 if self.ui.configbool(b'devel', b'all-warnings') or self.ui.configbool(
2278 if self.ui.configbool(b'devel', b'all-warnings') or self.ui.configbool(
2279 b'devel', b'check-locks'
2279 b'devel', b'check-locks'
2280 ):
2280 ):
2281 if self._currentlock(self._lockref) is None:
2281 if self._currentlock(self._lockref) is None:
2282 raise error.ProgrammingError(b'transaction requires locking')
2282 raise error.ProgrammingError(b'transaction requires locking')
2283 tr = self.currenttransaction()
2283 tr = self.currenttransaction()
2284 if tr is not None:
2284 if tr is not None:
2285 return tr.nest(name=desc)
2285 return tr.nest(name=desc)
2286
2286
2287 # abort here if the journal already exists
2287 # abort here if the journal already exists
2288 if self.svfs.exists(b"journal"):
2288 if self.svfs.exists(b"journal"):
2289 raise error.RepoError(
2289 raise error.RepoError(
2290 _(b"abandoned transaction found"),
2290 _(b"abandoned transaction found"),
2291 hint=_(b"run 'hg recover' to clean up transaction"),
2291 hint=_(b"run 'hg recover' to clean up transaction"),
2292 )
2292 )
2293
2293
2294 idbase = b"%.40f#%f" % (random.random(), time.time())
2294 idbase = b"%.40f#%f" % (random.random(), time.time())
2295 ha = hex(hashutil.sha1(idbase).digest())
2295 ha = hex(hashutil.sha1(idbase).digest())
2296 txnid = b'TXN:' + ha
2296 txnid = b'TXN:' + ha
2297 self.hook(b'pretxnopen', throw=True, txnname=desc, txnid=txnid)
2297 self.hook(b'pretxnopen', throw=True, txnname=desc, txnid=txnid)
2298
2298
2299 self._writejournal(desc)
2299 self._writejournal(desc)
2300 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
2300 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
2301 if report:
2301 if report:
2302 rp = report
2302 rp = report
2303 else:
2303 else:
2304 rp = self.ui.warn
2304 rp = self.ui.warn
2305 vfsmap = {b'plain': self.vfs, b'store': self.svfs} # root of .hg/
2305 vfsmap = {b'plain': self.vfs, b'store': self.svfs} # root of .hg/
2306 # we must avoid cyclic reference between repo and transaction.
2306 # we must avoid cyclic reference between repo and transaction.
2307 reporef = weakref.ref(self)
2307 reporef = weakref.ref(self)
2308 # Code to track tag movement
2308 # Code to track tag movement
2309 #
2309 #
2310 # Since tags are all handled as file content, it is actually quite hard
2310 # Since tags are all handled as file content, it is actually quite hard
2311 # to track these movement from a code perspective. So we fallback to a
2311 # to track these movement from a code perspective. So we fallback to a
2312 # tracking at the repository level. One could envision to track changes
2312 # tracking at the repository level. One could envision to track changes
2313 # to the '.hgtags' file through changegroup apply but that fails to
2313 # to the '.hgtags' file through changegroup apply but that fails to
2314 # cope with case where transaction expose new heads without changegroup
2314 # cope with case where transaction expose new heads without changegroup
2315 # being involved (eg: phase movement).
2315 # being involved (eg: phase movement).
2316 #
2316 #
2317 # For now, We gate the feature behind a flag since this likely comes
2317 # For now, We gate the feature behind a flag since this likely comes
2318 # with performance impacts. The current code run more often than needed
2318 # with performance impacts. The current code run more often than needed
2319 # and do not use caches as much as it could. The current focus is on
2319 # and do not use caches as much as it could. The current focus is on
2320 # the behavior of the feature so we disable it by default. The flag
2320 # the behavior of the feature so we disable it by default. The flag
2321 # will be removed when we are happy with the performance impact.
2321 # will be removed when we are happy with the performance impact.
2322 #
2322 #
2323 # Once this feature is no longer experimental move the following
2323 # Once this feature is no longer experimental move the following
2324 # documentation to the appropriate help section:
2324 # documentation to the appropriate help section:
2325 #
2325 #
2326 # The ``HG_TAG_MOVED`` variable will be set if the transaction touched
2326 # The ``HG_TAG_MOVED`` variable will be set if the transaction touched
2327 # tags (new or changed or deleted tags). In addition the details of
2327 # tags (new or changed or deleted tags). In addition the details of
2328 # these changes are made available in a file at:
2328 # these changes are made available in a file at:
2329 # ``REPOROOT/.hg/changes/tags.changes``.
2329 # ``REPOROOT/.hg/changes/tags.changes``.
2330 # Make sure you check for HG_TAG_MOVED before reading that file as it
2330 # Make sure you check for HG_TAG_MOVED before reading that file as it
2331 # might exist from a previous transaction even if no tag were touched
2331 # might exist from a previous transaction even if no tag were touched
2332 # in this one. Changes are recorded in a line base format::
2332 # in this one. Changes are recorded in a line base format::
2333 #
2333 #
2334 # <action> <hex-node> <tag-name>\n
2334 # <action> <hex-node> <tag-name>\n
2335 #
2335 #
2336 # Actions are defined as follow:
2336 # Actions are defined as follow:
2337 # "-R": tag is removed,
2337 # "-R": tag is removed,
2338 # "+A": tag is added,
2338 # "+A": tag is added,
2339 # "-M": tag is moved (old value),
2339 # "-M": tag is moved (old value),
2340 # "+M": tag is moved (new value),
2340 # "+M": tag is moved (new value),
2341 tracktags = lambda x: None
2341 tracktags = lambda x: None
2342 # experimental config: experimental.hook-track-tags
2342 # experimental config: experimental.hook-track-tags
2343 shouldtracktags = self.ui.configbool(
2343 shouldtracktags = self.ui.configbool(
2344 b'experimental', b'hook-track-tags'
2344 b'experimental', b'hook-track-tags'
2345 )
2345 )
2346 if desc != b'strip' and shouldtracktags:
2346 if desc != b'strip' and shouldtracktags:
2347 oldheads = self.changelog.headrevs()
2347 oldheads = self.changelog.headrevs()
2348
2348
2349 def tracktags(tr2):
2349 def tracktags(tr2):
2350 repo = reporef()
2350 repo = reporef()
2351 assert repo is not None # help pytype
2351 assert repo is not None # help pytype
2352 oldfnodes = tagsmod.fnoderevs(repo.ui, repo, oldheads)
2352 oldfnodes = tagsmod.fnoderevs(repo.ui, repo, oldheads)
2353 newheads = repo.changelog.headrevs()
2353 newheads = repo.changelog.headrevs()
2354 newfnodes = tagsmod.fnoderevs(repo.ui, repo, newheads)
2354 newfnodes = tagsmod.fnoderevs(repo.ui, repo, newheads)
2355 # notes: we compare lists here.
2355 # notes: we compare lists here.
2356 # As we do it only once buiding set would not be cheaper
2356 # As we do it only once buiding set would not be cheaper
2357 changes = tagsmod.difftags(repo.ui, repo, oldfnodes, newfnodes)
2357 changes = tagsmod.difftags(repo.ui, repo, oldfnodes, newfnodes)
2358 if changes:
2358 if changes:
2359 tr2.hookargs[b'tag_moved'] = b'1'
2359 tr2.hookargs[b'tag_moved'] = b'1'
2360 with repo.vfs(
2360 with repo.vfs(
2361 b'changes/tags.changes', b'w', atomictemp=True
2361 b'changes/tags.changes', b'w', atomictemp=True
2362 ) as changesfile:
2362 ) as changesfile:
2363 # note: we do not register the file to the transaction
2363 # note: we do not register the file to the transaction
2364 # because we needs it to still exist on the transaction
2364 # because we needs it to still exist on the transaction
2365 # is close (for txnclose hooks)
2365 # is close (for txnclose hooks)
2366 tagsmod.writediff(changesfile, changes)
2366 tagsmod.writediff(changesfile, changes)
2367
2367
2368 def validate(tr2):
2368 def validate(tr2):
2369 """will run pre-closing hooks"""
2369 """will run pre-closing hooks"""
2370 # XXX the transaction API is a bit lacking here so we take a hacky
2370 # XXX the transaction API is a bit lacking here so we take a hacky
2371 # path for now
2371 # path for now
2372 #
2372 #
2373 # We cannot add this as a "pending" hooks since the 'tr.hookargs'
2373 # We cannot add this as a "pending" hooks since the 'tr.hookargs'
2374 # dict is copied before these run. In addition we needs the data
2374 # dict is copied before these run. In addition we needs the data
2375 # available to in memory hooks too.
2375 # available to in memory hooks too.
2376 #
2376 #
2377 # Moreover, we also need to make sure this runs before txnclose
2377 # Moreover, we also need to make sure this runs before txnclose
2378 # hooks and there is no "pending" mechanism that would execute
2378 # hooks and there is no "pending" mechanism that would execute
2379 # logic only if hooks are about to run.
2379 # logic only if hooks are about to run.
2380 #
2380 #
2381 # Fixing this limitation of the transaction is also needed to track
2381 # Fixing this limitation of the transaction is also needed to track
2382 # other families of changes (bookmarks, phases, obsolescence).
2382 # other families of changes (bookmarks, phases, obsolescence).
2383 #
2383 #
2384 # This will have to be fixed before we remove the experimental
2384 # This will have to be fixed before we remove the experimental
2385 # gating.
2385 # gating.
2386 tracktags(tr2)
2386 tracktags(tr2)
2387 repo = reporef()
2387 repo = reporef()
2388 assert repo is not None # help pytype
2388 assert repo is not None # help pytype
2389
2389
2390 singleheadopt = (b'experimental', b'single-head-per-branch')
2390 singleheadopt = (b'experimental', b'single-head-per-branch')
2391 singlehead = repo.ui.configbool(*singleheadopt)
2391 singlehead = repo.ui.configbool(*singleheadopt)
2392 if singlehead:
2392 if singlehead:
2393 singleheadsub = repo.ui.configsuboptions(*singleheadopt)[1]
2393 singleheadsub = repo.ui.configsuboptions(*singleheadopt)[1]
2394 accountclosed = singleheadsub.get(
2394 accountclosed = singleheadsub.get(
2395 b"account-closed-heads", False
2395 b"account-closed-heads", False
2396 )
2396 )
2397 if singleheadsub.get(b"public-changes-only", False):
2397 if singleheadsub.get(b"public-changes-only", False):
2398 filtername = b"immutable"
2398 filtername = b"immutable"
2399 else:
2399 else:
2400 filtername = b"visible"
2400 filtername = b"visible"
2401 scmutil.enforcesinglehead(
2401 scmutil.enforcesinglehead(
2402 repo, tr2, desc, accountclosed, filtername
2402 repo, tr2, desc, accountclosed, filtername
2403 )
2403 )
2404 if hook.hashook(repo.ui, b'pretxnclose-bookmark'):
2404 if hook.hashook(repo.ui, b'pretxnclose-bookmark'):
2405 for name, (old, new) in sorted(
2405 for name, (old, new) in sorted(
2406 tr.changes[b'bookmarks'].items()
2406 tr.changes[b'bookmarks'].items()
2407 ):
2407 ):
2408 args = tr.hookargs.copy()
2408 args = tr.hookargs.copy()
2409 args.update(bookmarks.preparehookargs(name, old, new))
2409 args.update(bookmarks.preparehookargs(name, old, new))
2410 repo.hook(
2410 repo.hook(
2411 b'pretxnclose-bookmark',
2411 b'pretxnclose-bookmark',
2412 throw=True,
2412 throw=True,
2413 **pycompat.strkwargs(args)
2413 **pycompat.strkwargs(args)
2414 )
2414 )
2415 if hook.hashook(repo.ui, b'pretxnclose-phase'):
2415 if hook.hashook(repo.ui, b'pretxnclose-phase'):
2416 cl = repo.unfiltered().changelog
2416 cl = repo.unfiltered().changelog
2417 for revs, (old, new) in tr.changes[b'phases']:
2417 for revs, (old, new) in tr.changes[b'phases']:
2418 for rev in revs:
2418 for rev in revs:
2419 args = tr.hookargs.copy()
2419 args = tr.hookargs.copy()
2420 node = hex(cl.node(rev))
2420 node = hex(cl.node(rev))
2421 args.update(phases.preparehookargs(node, old, new))
2421 args.update(phases.preparehookargs(node, old, new))
2422 repo.hook(
2422 repo.hook(
2423 b'pretxnclose-phase',
2423 b'pretxnclose-phase',
2424 throw=True,
2424 throw=True,
2425 **pycompat.strkwargs(args)
2425 **pycompat.strkwargs(args)
2426 )
2426 )
2427
2427
2428 repo.hook(
2428 repo.hook(
2429 b'pretxnclose', throw=True, **pycompat.strkwargs(tr.hookargs)
2429 b'pretxnclose', throw=True, **pycompat.strkwargs(tr.hookargs)
2430 )
2430 )
2431
2431
2432 def releasefn(tr, success):
2432 def releasefn(tr, success):
2433 repo = reporef()
2433 repo = reporef()
2434 if repo is None:
2434 if repo is None:
2435 # If the repo has been GC'd (and this release function is being
2435 # If the repo has been GC'd (and this release function is being
2436 # called from transaction.__del__), there's not much we can do,
2436 # called from transaction.__del__), there's not much we can do,
2437 # so just leave the unfinished transaction there and let the
2437 # so just leave the unfinished transaction there and let the
2438 # user run `hg recover`.
2438 # user run `hg recover`.
2439 return
2439 return
2440 if success:
2440 if success:
2441 # this should be explicitly invoked here, because
2441 # this should be explicitly invoked here, because
2442 # in-memory changes aren't written out at closing
2442 # in-memory changes aren't written out at closing
2443 # transaction, if tr.addfilegenerator (via
2443 # transaction, if tr.addfilegenerator (via
2444 # dirstate.write or so) isn't invoked while
2444 # dirstate.write or so) isn't invoked while
2445 # transaction running
2445 # transaction running
2446 repo.dirstate.write(None)
2446 repo.dirstate.write(None)
2447 else:
2447 else:
2448 # discard all changes (including ones already written
2448 # discard all changes (including ones already written
2449 # out) in this transaction
2449 # out) in this transaction
2450 narrowspec.restorebackup(self, b'journal.narrowspec')
2450 narrowspec.restorebackup(self, b'journal.narrowspec')
2451 narrowspec.restorewcbackup(self, b'journal.narrowspec.dirstate')
2451 narrowspec.restorewcbackup(self, b'journal.narrowspec.dirstate')
2452 repo.dirstate.restorebackup(None, b'journal.dirstate')
2452 repo.dirstate.restorebackup(None, b'journal.dirstate')
2453
2453
2454 repo.invalidate(clearfilecache=True)
2454 repo.invalidate(clearfilecache=True)
2455
2455
2456 tr = transaction.transaction(
2456 tr = transaction.transaction(
2457 rp,
2457 rp,
2458 self.svfs,
2458 self.svfs,
2459 vfsmap,
2459 vfsmap,
2460 b"journal",
2460 b"journal",
2461 b"undo",
2461 b"undo",
2462 aftertrans(renames),
2462 aftertrans(renames),
2463 self.store.createmode,
2463 self.store.createmode,
2464 validator=validate,
2464 validator=validate,
2465 releasefn=releasefn,
2465 releasefn=releasefn,
2466 checkambigfiles=_cachedfiles,
2466 checkambigfiles=_cachedfiles,
2467 name=desc,
2467 name=desc,
2468 )
2468 )
2469 tr.changes[b'origrepolen'] = len(self)
2469 tr.changes[b'origrepolen'] = len(self)
2470 tr.changes[b'obsmarkers'] = set()
2470 tr.changes[b'obsmarkers'] = set()
2471 tr.changes[b'phases'] = []
2471 tr.changes[b'phases'] = []
2472 tr.changes[b'bookmarks'] = {}
2472 tr.changes[b'bookmarks'] = {}
2473
2473
2474 tr.hookargs[b'txnid'] = txnid
2474 tr.hookargs[b'txnid'] = txnid
2475 tr.hookargs[b'txnname'] = desc
2475 tr.hookargs[b'txnname'] = desc
2476 tr.hookargs[b'changes'] = tr.changes
2476 tr.hookargs[b'changes'] = tr.changes
2477 # note: writing the fncache only during finalize mean that the file is
2477 # note: writing the fncache only during finalize mean that the file is
2478 # outdated when running hooks. As fncache is used for streaming clone,
2478 # outdated when running hooks. As fncache is used for streaming clone,
2479 # this is not expected to break anything that happen during the hooks.
2479 # this is not expected to break anything that happen during the hooks.
2480 tr.addfinalize(b'flush-fncache', self.store.write)
2480 tr.addfinalize(b'flush-fncache', self.store.write)
2481
2481
2482 def txnclosehook(tr2):
2482 def txnclosehook(tr2):
2483 """To be run if transaction is successful, will schedule a hook run"""
2483 """To be run if transaction is successful, will schedule a hook run"""
2484 # Don't reference tr2 in hook() so we don't hold a reference.
2484 # Don't reference tr2 in hook() so we don't hold a reference.
2485 # This reduces memory consumption when there are multiple
2485 # This reduces memory consumption when there are multiple
2486 # transactions per lock. This can likely go away if issue5045
2486 # transactions per lock. This can likely go away if issue5045
2487 # fixes the function accumulation.
2487 # fixes the function accumulation.
2488 hookargs = tr2.hookargs
2488 hookargs = tr2.hookargs
2489
2489
2490 def hookfunc(unused_success):
2490 def hookfunc(unused_success):
2491 repo = reporef()
2491 repo = reporef()
2492 assert repo is not None # help pytype
2492 assert repo is not None # help pytype
2493
2493
2494 if hook.hashook(repo.ui, b'txnclose-bookmark'):
2494 if hook.hashook(repo.ui, b'txnclose-bookmark'):
2495 bmchanges = sorted(tr.changes[b'bookmarks'].items())
2495 bmchanges = sorted(tr.changes[b'bookmarks'].items())
2496 for name, (old, new) in bmchanges:
2496 for name, (old, new) in bmchanges:
2497 args = tr.hookargs.copy()
2497 args = tr.hookargs.copy()
2498 args.update(bookmarks.preparehookargs(name, old, new))
2498 args.update(bookmarks.preparehookargs(name, old, new))
2499 repo.hook(
2499 repo.hook(
2500 b'txnclose-bookmark',
2500 b'txnclose-bookmark',
2501 throw=False,
2501 throw=False,
2502 **pycompat.strkwargs(args)
2502 **pycompat.strkwargs(args)
2503 )
2503 )
2504
2504
2505 if hook.hashook(repo.ui, b'txnclose-phase'):
2505 if hook.hashook(repo.ui, b'txnclose-phase'):
2506 cl = repo.unfiltered().changelog
2506 cl = repo.unfiltered().changelog
2507 phasemv = sorted(
2507 phasemv = sorted(
2508 tr.changes[b'phases'], key=lambda r: r[0][0]
2508 tr.changes[b'phases'], key=lambda r: r[0][0]
2509 )
2509 )
2510 for revs, (old, new) in phasemv:
2510 for revs, (old, new) in phasemv:
2511 for rev in revs:
2511 for rev in revs:
2512 args = tr.hookargs.copy()
2512 args = tr.hookargs.copy()
2513 node = hex(cl.node(rev))
2513 node = hex(cl.node(rev))
2514 args.update(phases.preparehookargs(node, old, new))
2514 args.update(phases.preparehookargs(node, old, new))
2515 repo.hook(
2515 repo.hook(
2516 b'txnclose-phase',
2516 b'txnclose-phase',
2517 throw=False,
2517 throw=False,
2518 **pycompat.strkwargs(args)
2518 **pycompat.strkwargs(args)
2519 )
2519 )
2520
2520
2521 repo.hook(
2521 repo.hook(
2522 b'txnclose', throw=False, **pycompat.strkwargs(hookargs)
2522 b'txnclose', throw=False, **pycompat.strkwargs(hookargs)
2523 )
2523 )
2524
2524
2525 repo = reporef()
2525 repo = reporef()
2526 assert repo is not None # help pytype
2526 assert repo is not None # help pytype
2527 repo._afterlock(hookfunc)
2527 repo._afterlock(hookfunc)
2528
2528
2529 tr.addfinalize(b'txnclose-hook', txnclosehook)
2529 tr.addfinalize(b'txnclose-hook', txnclosehook)
2530 # Include a leading "-" to make it happen before the transaction summary
2530 # Include a leading "-" to make it happen before the transaction summary
2531 # reports registered via scmutil.registersummarycallback() whose names
2531 # reports registered via scmutil.registersummarycallback() whose names
2532 # are 00-txnreport etc. That way, the caches will be warm when the
2532 # are 00-txnreport etc. That way, the caches will be warm when the
2533 # callbacks run.
2533 # callbacks run.
2534 tr.addpostclose(b'-warm-cache', self._buildcacheupdater(tr))
2534 tr.addpostclose(b'-warm-cache', self._buildcacheupdater(tr))
2535
2535
2536 def txnaborthook(tr2):
2536 def txnaborthook(tr2):
2537 """To be run if transaction is aborted"""
2537 """To be run if transaction is aborted"""
2538 repo = reporef()
2538 repo = reporef()
2539 assert repo is not None # help pytype
2539 assert repo is not None # help pytype
2540 repo.hook(
2540 repo.hook(
2541 b'txnabort', throw=False, **pycompat.strkwargs(tr2.hookargs)
2541 b'txnabort', throw=False, **pycompat.strkwargs(tr2.hookargs)
2542 )
2542 )
2543
2543
2544 tr.addabort(b'txnabort-hook', txnaborthook)
2544 tr.addabort(b'txnabort-hook', txnaborthook)
2545 # avoid eager cache invalidation. in-memory data should be identical
2545 # avoid eager cache invalidation. in-memory data should be identical
2546 # to stored data if transaction has no error.
2546 # to stored data if transaction has no error.
2547 tr.addpostclose(b'refresh-filecachestats', self._refreshfilecachestats)
2547 tr.addpostclose(b'refresh-filecachestats', self._refreshfilecachestats)
2548 self._transref = weakref.ref(tr)
2548 self._transref = weakref.ref(tr)
2549 scmutil.registersummarycallback(self, tr, desc)
2549 scmutil.registersummarycallback(self, tr, desc)
2550 return tr
2550 return tr
2551
2551
2552 def _journalfiles(self):
2552 def _journalfiles(self):
2553 return (
2553 return (
2554 (self.svfs, b'journal'),
2554 (self.svfs, b'journal'),
2555 (self.svfs, b'journal.narrowspec'),
2555 (self.svfs, b'journal.narrowspec'),
2556 (self.vfs, b'journal.narrowspec.dirstate'),
2556 (self.vfs, b'journal.narrowspec.dirstate'),
2557 (self.vfs, b'journal.dirstate'),
2557 (self.vfs, b'journal.dirstate'),
2558 (self.vfs, b'journal.branch'),
2558 (self.vfs, b'journal.branch'),
2559 (self.vfs, b'journal.desc'),
2559 (self.vfs, b'journal.desc'),
2560 (bookmarks.bookmarksvfs(self), b'journal.bookmarks'),
2560 (bookmarks.bookmarksvfs(self), b'journal.bookmarks'),
2561 (self.svfs, b'journal.phaseroots'),
2561 (self.svfs, b'journal.phaseroots'),
2562 )
2562 )
2563
2563
2564 def undofiles(self):
2564 def undofiles(self):
2565 return [(vfs, undoname(x)) for vfs, x in self._journalfiles()]
2565 return [(vfs, undoname(x)) for vfs, x in self._journalfiles()]
2566
2566
2567 @unfilteredmethod
2567 @unfilteredmethod
2568 def _writejournal(self, desc):
2568 def _writejournal(self, desc):
2569 self.dirstate.savebackup(None, b'journal.dirstate')
2569 self.dirstate.savebackup(None, b'journal.dirstate')
2570 narrowspec.savewcbackup(self, b'journal.narrowspec.dirstate')
2570 narrowspec.savewcbackup(self, b'journal.narrowspec.dirstate')
2571 narrowspec.savebackup(self, b'journal.narrowspec')
2571 narrowspec.savebackup(self, b'journal.narrowspec')
2572 self.vfs.write(
2572 self.vfs.write(
2573 b"journal.branch", encoding.fromlocal(self.dirstate.branch())
2573 b"journal.branch", encoding.fromlocal(self.dirstate.branch())
2574 )
2574 )
2575 self.vfs.write(b"journal.desc", b"%d\n%s\n" % (len(self), desc))
2575 self.vfs.write(b"journal.desc", b"%d\n%s\n" % (len(self), desc))
2576 bookmarksvfs = bookmarks.bookmarksvfs(self)
2576 bookmarksvfs = bookmarks.bookmarksvfs(self)
2577 bookmarksvfs.write(
2577 bookmarksvfs.write(
2578 b"journal.bookmarks", bookmarksvfs.tryread(b"bookmarks")
2578 b"journal.bookmarks", bookmarksvfs.tryread(b"bookmarks")
2579 )
2579 )
2580 self.svfs.write(b"journal.phaseroots", self.svfs.tryread(b"phaseroots"))
2580 self.svfs.write(b"journal.phaseroots", self.svfs.tryread(b"phaseroots"))
2581
2581
2582 def recover(self):
2582 def recover(self):
2583 with self.lock():
2583 with self.lock():
2584 if self.svfs.exists(b"journal"):
2584 if self.svfs.exists(b"journal"):
2585 self.ui.status(_(b"rolling back interrupted transaction\n"))
2585 self.ui.status(_(b"rolling back interrupted transaction\n"))
2586 vfsmap = {
2586 vfsmap = {
2587 b'': self.svfs,
2587 b'': self.svfs,
2588 b'plain': self.vfs,
2588 b'plain': self.vfs,
2589 }
2589 }
2590 transaction.rollback(
2590 transaction.rollback(
2591 self.svfs,
2591 self.svfs,
2592 vfsmap,
2592 vfsmap,
2593 b"journal",
2593 b"journal",
2594 self.ui.warn,
2594 self.ui.warn,
2595 checkambigfiles=_cachedfiles,
2595 checkambigfiles=_cachedfiles,
2596 )
2596 )
2597 self.invalidate()
2597 self.invalidate()
2598 return True
2598 return True
2599 else:
2599 else:
2600 self.ui.warn(_(b"no interrupted transaction available\n"))
2600 self.ui.warn(_(b"no interrupted transaction available\n"))
2601 return False
2601 return False
2602
2602
2603 def rollback(self, dryrun=False, force=False):
2603 def rollback(self, dryrun=False, force=False):
2604 wlock = lock = dsguard = None
2604 wlock = lock = dsguard = None
2605 try:
2605 try:
2606 wlock = self.wlock()
2606 wlock = self.wlock()
2607 lock = self.lock()
2607 lock = self.lock()
2608 if self.svfs.exists(b"undo"):
2608 if self.svfs.exists(b"undo"):
2609 dsguard = dirstateguard.dirstateguard(self, b'rollback')
2609 dsguard = dirstateguard.dirstateguard(self, b'rollback')
2610
2610
2611 return self._rollback(dryrun, force, dsguard)
2611 return self._rollback(dryrun, force, dsguard)
2612 else:
2612 else:
2613 self.ui.warn(_(b"no rollback information available\n"))
2613 self.ui.warn(_(b"no rollback information available\n"))
2614 return 1
2614 return 1
2615 finally:
2615 finally:
2616 release(dsguard, lock, wlock)
2616 release(dsguard, lock, wlock)
2617
2617
2618 @unfilteredmethod # Until we get smarter cache management
2618 @unfilteredmethod # Until we get smarter cache management
2619 def _rollback(self, dryrun, force, dsguard):
2619 def _rollback(self, dryrun, force, dsguard):
2620 ui = self.ui
2620 ui = self.ui
2621 try:
2621 try:
2622 args = self.vfs.read(b'undo.desc').splitlines()
2622 args = self.vfs.read(b'undo.desc').splitlines()
2623 (oldlen, desc, detail) = (int(args[0]), args[1], None)
2623 (oldlen, desc, detail) = (int(args[0]), args[1], None)
2624 if len(args) >= 3:
2624 if len(args) >= 3:
2625 detail = args[2]
2625 detail = args[2]
2626 oldtip = oldlen - 1
2626 oldtip = oldlen - 1
2627
2627
2628 if detail and ui.verbose:
2628 if detail and ui.verbose:
2629 msg = _(
2629 msg = _(
2630 b'repository tip rolled back to revision %d'
2630 b'repository tip rolled back to revision %d'
2631 b' (undo %s: %s)\n'
2631 b' (undo %s: %s)\n'
2632 ) % (oldtip, desc, detail)
2632 ) % (oldtip, desc, detail)
2633 else:
2633 else:
2634 msg = _(
2634 msg = _(
2635 b'repository tip rolled back to revision %d (undo %s)\n'
2635 b'repository tip rolled back to revision %d (undo %s)\n'
2636 ) % (oldtip, desc)
2636 ) % (oldtip, desc)
2637 except IOError:
2637 except IOError:
2638 msg = _(b'rolling back unknown transaction\n')
2638 msg = _(b'rolling back unknown transaction\n')
2639 desc = None
2639 desc = None
2640
2640
2641 if not force and self[b'.'] != self[b'tip'] and desc == b'commit':
2641 if not force and self[b'.'] != self[b'tip'] and desc == b'commit':
2642 raise error.Abort(
2642 raise error.Abort(
2643 _(
2643 _(
2644 b'rollback of last commit while not checked out '
2644 b'rollback of last commit while not checked out '
2645 b'may lose data'
2645 b'may lose data'
2646 ),
2646 ),
2647 hint=_(b'use -f to force'),
2647 hint=_(b'use -f to force'),
2648 )
2648 )
2649
2649
2650 ui.status(msg)
2650 ui.status(msg)
2651 if dryrun:
2651 if dryrun:
2652 return 0
2652 return 0
2653
2653
2654 parents = self.dirstate.parents()
2654 parents = self.dirstate.parents()
2655 self.destroying()
2655 self.destroying()
2656 vfsmap = {b'plain': self.vfs, b'': self.svfs}
2656 vfsmap = {b'plain': self.vfs, b'': self.svfs}
2657 transaction.rollback(
2657 transaction.rollback(
2658 self.svfs, vfsmap, b'undo', ui.warn, checkambigfiles=_cachedfiles
2658 self.svfs, vfsmap, b'undo', ui.warn, checkambigfiles=_cachedfiles
2659 )
2659 )
2660 bookmarksvfs = bookmarks.bookmarksvfs(self)
2660 bookmarksvfs = bookmarks.bookmarksvfs(self)
2661 if bookmarksvfs.exists(b'undo.bookmarks'):
2661 if bookmarksvfs.exists(b'undo.bookmarks'):
2662 bookmarksvfs.rename(
2662 bookmarksvfs.rename(
2663 b'undo.bookmarks', b'bookmarks', checkambig=True
2663 b'undo.bookmarks', b'bookmarks', checkambig=True
2664 )
2664 )
2665 if self.svfs.exists(b'undo.phaseroots'):
2665 if self.svfs.exists(b'undo.phaseroots'):
2666 self.svfs.rename(b'undo.phaseroots', b'phaseroots', checkambig=True)
2666 self.svfs.rename(b'undo.phaseroots', b'phaseroots', checkambig=True)
2667 self.invalidate()
2667 self.invalidate()
2668
2668
2669 has_node = self.changelog.index.has_node
2669 has_node = self.changelog.index.has_node
2670 parentgone = any(not has_node(p) for p in parents)
2670 parentgone = any(not has_node(p) for p in parents)
2671 if parentgone:
2671 if parentgone:
2672 # prevent dirstateguard from overwriting already restored one
2672 # prevent dirstateguard from overwriting already restored one
2673 dsguard.close()
2673 dsguard.close()
2674
2674
2675 narrowspec.restorebackup(self, b'undo.narrowspec')
2675 narrowspec.restorebackup(self, b'undo.narrowspec')
2676 narrowspec.restorewcbackup(self, b'undo.narrowspec.dirstate')
2676 narrowspec.restorewcbackup(self, b'undo.narrowspec.dirstate')
2677 self.dirstate.restorebackup(None, b'undo.dirstate')
2677 self.dirstate.restorebackup(None, b'undo.dirstate')
2678 try:
2678 try:
2679 branch = self.vfs.read(b'undo.branch')
2679 branch = self.vfs.read(b'undo.branch')
2680 self.dirstate.setbranch(encoding.tolocal(branch))
2680 self.dirstate.setbranch(encoding.tolocal(branch))
2681 except IOError:
2681 except IOError:
2682 ui.warn(
2682 ui.warn(
2683 _(
2683 _(
2684 b'named branch could not be reset: '
2684 b'named branch could not be reset: '
2685 b'current branch is still \'%s\'\n'
2685 b'current branch is still \'%s\'\n'
2686 )
2686 )
2687 % self.dirstate.branch()
2687 % self.dirstate.branch()
2688 )
2688 )
2689
2689
2690 parents = tuple([p.rev() for p in self[None].parents()])
2690 parents = tuple([p.rev() for p in self[None].parents()])
2691 if len(parents) > 1:
2691 if len(parents) > 1:
2692 ui.status(
2692 ui.status(
2693 _(
2693 _(
2694 b'working directory now based on '
2694 b'working directory now based on '
2695 b'revisions %d and %d\n'
2695 b'revisions %d and %d\n'
2696 )
2696 )
2697 % parents
2697 % parents
2698 )
2698 )
2699 else:
2699 else:
2700 ui.status(
2700 ui.status(
2701 _(b'working directory now based on revision %d\n') % parents
2701 _(b'working directory now based on revision %d\n') % parents
2702 )
2702 )
2703 mergestatemod.mergestate.clean(self)
2703 mergestatemod.mergestate.clean(self)
2704
2704
2705 # TODO: if we know which new heads may result from this rollback, pass
2705 # TODO: if we know which new heads may result from this rollback, pass
2706 # them to destroy(), which will prevent the branchhead cache from being
2706 # them to destroy(), which will prevent the branchhead cache from being
2707 # invalidated.
2707 # invalidated.
2708 self.destroyed()
2708 self.destroyed()
2709 return 0
2709 return 0
2710
2710
2711 def _buildcacheupdater(self, newtransaction):
2711 def _buildcacheupdater(self, newtransaction):
2712 """called during transaction to build the callback updating cache
2712 """called during transaction to build the callback updating cache
2713
2713
2714 Lives on the repository to help extension who might want to augment
2714 Lives on the repository to help extension who might want to augment
2715 this logic. For this purpose, the created transaction is passed to the
2715 this logic. For this purpose, the created transaction is passed to the
2716 method.
2716 method.
2717 """
2717 """
2718 # we must avoid cyclic reference between repo and transaction.
2718 # we must avoid cyclic reference between repo and transaction.
2719 reporef = weakref.ref(self)
2719 reporef = weakref.ref(self)
2720
2720
2721 def updater(tr):
2721 def updater(tr):
2722 repo = reporef()
2722 repo = reporef()
2723 assert repo is not None # help pytype
2723 assert repo is not None # help pytype
2724 repo.updatecaches(tr)
2724 repo.updatecaches(tr)
2725
2725
2726 return updater
2726 return updater
2727
2727
2728 @unfilteredmethod
2728 @unfilteredmethod
2729 def updatecaches(self, tr=None, full=False):
2729 def updatecaches(self, tr=None, full=False):
2730 """warm appropriate caches
2730 """warm appropriate caches
2731
2731
2732 If this function is called after a transaction closed. The transaction
2732 If this function is called after a transaction closed. The transaction
2733 will be available in the 'tr' argument. This can be used to selectively
2733 will be available in the 'tr' argument. This can be used to selectively
2734 update caches relevant to the changes in that transaction.
2734 update caches relevant to the changes in that transaction.
2735
2735
2736 If 'full' is set, make sure all caches the function knows about have
2736 If 'full' is set, make sure all caches the function knows about have
2737 up-to-date data. Even the ones usually loaded more lazily.
2737 up-to-date data. Even the ones usually loaded more lazily.
2738
2738
2739 The `full` argument can take a special "post-clone" value. In this case
2739 The `full` argument can take a special "post-clone" value. In this case
2740 the cache warming is made after a clone and of the slower cache might
2740 the cache warming is made after a clone and of the slower cache might
2741 be skipped, namely the `.fnodetags` one. This argument is 5.8 specific
2741 be skipped, namely the `.fnodetags` one. This argument is 5.8 specific
2742 as we plan for a cleaner way to deal with this for 5.9.
2742 as we plan for a cleaner way to deal with this for 5.9.
2743 """
2743 """
2744 if tr is not None and tr.hookargs.get(b'source') == b'strip':
2744 if tr is not None and tr.hookargs.get(b'source') == b'strip':
2745 # During strip, many caches are invalid but
2745 # During strip, many caches are invalid but
2746 # later call to `destroyed` will refresh them.
2746 # later call to `destroyed` will refresh them.
2747 return
2747 return
2748
2748
2749 if tr is None or tr.changes[b'origrepolen'] < len(self):
2749 if tr is None or tr.changes[b'origrepolen'] < len(self):
2750 # accessing the 'served' branchmap should refresh all the others,
2750 # accessing the 'served' branchmap should refresh all the others,
2751 self.ui.debug(b'updating the branch cache\n')
2751 self.ui.debug(b'updating the branch cache\n')
2752 self.filtered(b'served').branchmap()
2752 self.filtered(b'served').branchmap()
2753 self.filtered(b'served.hidden').branchmap()
2753 self.filtered(b'served.hidden').branchmap()
2754
2754
2755 if full:
2755 if full:
2756 unfi = self.unfiltered()
2756 unfi = self.unfiltered()
2757
2757
2758 self.changelog.update_caches(transaction=tr)
2758 self.changelog.update_caches(transaction=tr)
2759 self.manifestlog.update_caches(transaction=tr)
2759 self.manifestlog.update_caches(transaction=tr)
2760
2760
2761 rbc = unfi.revbranchcache()
2761 rbc = unfi.revbranchcache()
2762 for r in unfi.changelog:
2762 for r in unfi.changelog:
2763 rbc.branchinfo(r)
2763 rbc.branchinfo(r)
2764 rbc.write()
2764 rbc.write()
2765
2765
2766 # ensure the working copy parents are in the manifestfulltextcache
2766 # ensure the working copy parents are in the manifestfulltextcache
2767 for ctx in self[b'.'].parents():
2767 for ctx in self[b'.'].parents():
2768 ctx.manifest() # accessing the manifest is enough
2768 ctx.manifest() # accessing the manifest is enough
2769
2769
2770 if not full == b"post-clone":
2770 if not full == b"post-clone":
2771 # accessing fnode cache warms the cache
2771 # accessing fnode cache warms the cache
2772 tagsmod.fnoderevs(self.ui, unfi, unfi.changelog.revs())
2772 tagsmod.fnoderevs(self.ui, unfi, unfi.changelog.revs())
2773 # accessing tags warm the cache
2773 # accessing tags warm the cache
2774 self.tags()
2774 self.tags()
2775 self.filtered(b'served').tags()
2775 self.filtered(b'served').tags()
2776
2776
2777 # The `full` arg is documented as updating even the lazily-loaded
2777 # The `full` arg is documented as updating even the lazily-loaded
2778 # caches immediately, so we're forcing a write to cause these caches
2778 # caches immediately, so we're forcing a write to cause these caches
2779 # to be warmed up even if they haven't explicitly been requested
2779 # to be warmed up even if they haven't explicitly been requested
2780 # yet (if they've never been used by hg, they won't ever have been
2780 # yet (if they've never been used by hg, they won't ever have been
2781 # written, even if they're a subset of another kind of cache that
2781 # written, even if they're a subset of another kind of cache that
2782 # *has* been used).
2782 # *has* been used).
2783 for filt in repoview.filtertable.keys():
2783 for filt in repoview.filtertable.keys():
2784 filtered = self.filtered(filt)
2784 filtered = self.filtered(filt)
2785 filtered.branchmap().write(filtered)
2785 filtered.branchmap().write(filtered)
2786
2786
2787 def invalidatecaches(self):
2787 def invalidatecaches(self):
2788
2788
2789 if '_tagscache' in vars(self):
2789 if '_tagscache' in vars(self):
2790 # can't use delattr on proxy
2790 # can't use delattr on proxy
2791 del self.__dict__['_tagscache']
2791 del self.__dict__['_tagscache']
2792
2792
2793 self._branchcaches.clear()
2793 self._branchcaches.clear()
2794 self.invalidatevolatilesets()
2794 self.invalidatevolatilesets()
2795 self._sparsesignaturecache.clear()
2795 self._sparsesignaturecache.clear()
2796
2796
2797 def invalidatevolatilesets(self):
2797 def invalidatevolatilesets(self):
2798 self.filteredrevcache.clear()
2798 self.filteredrevcache.clear()
2799 obsolete.clearobscaches(self)
2799 obsolete.clearobscaches(self)
2800 self._quick_access_changeid_invalidate()
2800 self._quick_access_changeid_invalidate()
2801
2801
2802 def invalidatedirstate(self):
2802 def invalidatedirstate(self):
2803 """Invalidates the dirstate, causing the next call to dirstate
2803 """Invalidates the dirstate, causing the next call to dirstate
2804 to check if it was modified since the last time it was read,
2804 to check if it was modified since the last time it was read,
2805 rereading it if it has.
2805 rereading it if it has.
2806
2806
2807 This is different to dirstate.invalidate() that it doesn't always
2807 This is different to dirstate.invalidate() that it doesn't always
2808 rereads the dirstate. Use dirstate.invalidate() if you want to
2808 rereads the dirstate. Use dirstate.invalidate() if you want to
2809 explicitly read the dirstate again (i.e. restoring it to a previous
2809 explicitly read the dirstate again (i.e. restoring it to a previous
2810 known good state)."""
2810 known good state)."""
2811 if hasunfilteredcache(self, 'dirstate'):
2811 if hasunfilteredcache(self, 'dirstate'):
2812 for k in self.dirstate._filecache:
2812 for k in self.dirstate._filecache:
2813 try:
2813 try:
2814 delattr(self.dirstate, k)
2814 delattr(self.dirstate, k)
2815 except AttributeError:
2815 except AttributeError:
2816 pass
2816 pass
2817 delattr(self.unfiltered(), 'dirstate')
2817 delattr(self.unfiltered(), 'dirstate')
2818
2818
2819 def invalidate(self, clearfilecache=False):
2819 def invalidate(self, clearfilecache=False):
2820 """Invalidates both store and non-store parts other than dirstate
2820 """Invalidates both store and non-store parts other than dirstate
2821
2821
2822 If a transaction is running, invalidation of store is omitted,
2822 If a transaction is running, invalidation of store is omitted,
2823 because discarding in-memory changes might cause inconsistency
2823 because discarding in-memory changes might cause inconsistency
2824 (e.g. incomplete fncache causes unintentional failure, but
2824 (e.g. incomplete fncache causes unintentional failure, but
2825 redundant one doesn't).
2825 redundant one doesn't).
2826 """
2826 """
2827 unfiltered = self.unfiltered() # all file caches are stored unfiltered
2827 unfiltered = self.unfiltered() # all file caches are stored unfiltered
2828 for k in list(self._filecache.keys()):
2828 for k in list(self._filecache.keys()):
2829 # dirstate is invalidated separately in invalidatedirstate()
2829 # dirstate is invalidated separately in invalidatedirstate()
2830 if k == b'dirstate':
2830 if k == b'dirstate':
2831 continue
2831 continue
2832 if (
2832 if (
2833 k == b'changelog'
2833 k == b'changelog'
2834 and self.currenttransaction()
2834 and self.currenttransaction()
2835 and self.changelog._delayed
2835 and self.changelog._delayed
2836 ):
2836 ):
2837 # The changelog object may store unwritten revisions. We don't
2837 # The changelog object may store unwritten revisions. We don't
2838 # want to lose them.
2838 # want to lose them.
2839 # TODO: Solve the problem instead of working around it.
2839 # TODO: Solve the problem instead of working around it.
2840 continue
2840 continue
2841
2841
2842 if clearfilecache:
2842 if clearfilecache:
2843 del self._filecache[k]
2843 del self._filecache[k]
2844 try:
2844 try:
2845 delattr(unfiltered, k)
2845 delattr(unfiltered, k)
2846 except AttributeError:
2846 except AttributeError:
2847 pass
2847 pass
2848 self.invalidatecaches()
2848 self.invalidatecaches()
2849 if not self.currenttransaction():
2849 if not self.currenttransaction():
2850 # TODO: Changing contents of store outside transaction
2850 # TODO: Changing contents of store outside transaction
2851 # causes inconsistency. We should make in-memory store
2851 # causes inconsistency. We should make in-memory store
2852 # changes detectable, and abort if changed.
2852 # changes detectable, and abort if changed.
2853 self.store.invalidatecaches()
2853 self.store.invalidatecaches()
2854
2854
2855 def invalidateall(self):
2855 def invalidateall(self):
2856 """Fully invalidates both store and non-store parts, causing the
2856 """Fully invalidates both store and non-store parts, causing the
2857 subsequent operation to reread any outside changes."""
2857 subsequent operation to reread any outside changes."""
2858 # extension should hook this to invalidate its caches
2858 # extension should hook this to invalidate its caches
2859 self.invalidate()
2859 self.invalidate()
2860 self.invalidatedirstate()
2860 self.invalidatedirstate()
2861
2861
2862 @unfilteredmethod
2862 @unfilteredmethod
2863 def _refreshfilecachestats(self, tr):
2863 def _refreshfilecachestats(self, tr):
2864 """Reload stats of cached files so that they are flagged as valid"""
2864 """Reload stats of cached files so that they are flagged as valid"""
2865 for k, ce in self._filecache.items():
2865 for k, ce in self._filecache.items():
2866 k = pycompat.sysstr(k)
2866 k = pycompat.sysstr(k)
2867 if k == 'dirstate' or k not in self.__dict__:
2867 if k == 'dirstate' or k not in self.__dict__:
2868 continue
2868 continue
2869 ce.refresh()
2869 ce.refresh()
2870
2870
2871 def _lock(
2871 def _lock(
2872 self,
2872 self,
2873 vfs,
2873 vfs,
2874 lockname,
2874 lockname,
2875 wait,
2875 wait,
2876 releasefn,
2876 releasefn,
2877 acquirefn,
2877 acquirefn,
2878 desc,
2878 desc,
2879 ):
2879 ):
2880 timeout = 0
2880 timeout = 0
2881 warntimeout = 0
2881 warntimeout = 0
2882 if wait:
2882 if wait:
2883 timeout = self.ui.configint(b"ui", b"timeout")
2883 timeout = self.ui.configint(b"ui", b"timeout")
2884 warntimeout = self.ui.configint(b"ui", b"timeout.warn")
2884 warntimeout = self.ui.configint(b"ui", b"timeout.warn")
2885 # internal config: ui.signal-safe-lock
2885 # internal config: ui.signal-safe-lock
2886 signalsafe = self.ui.configbool(b'ui', b'signal-safe-lock')
2886 signalsafe = self.ui.configbool(b'ui', b'signal-safe-lock')
2887
2887
2888 l = lockmod.trylock(
2888 l = lockmod.trylock(
2889 self.ui,
2889 self.ui,
2890 vfs,
2890 vfs,
2891 lockname,
2891 lockname,
2892 timeout,
2892 timeout,
2893 warntimeout,
2893 warntimeout,
2894 releasefn=releasefn,
2894 releasefn=releasefn,
2895 acquirefn=acquirefn,
2895 acquirefn=acquirefn,
2896 desc=desc,
2896 desc=desc,
2897 signalsafe=signalsafe,
2897 signalsafe=signalsafe,
2898 )
2898 )
2899 return l
2899 return l
2900
2900
2901 def _afterlock(self, callback):
2901 def _afterlock(self, callback):
2902 """add a callback to be run when the repository is fully unlocked
2902 """add a callback to be run when the repository is fully unlocked
2903
2903
2904 The callback will be executed when the outermost lock is released
2904 The callback will be executed when the outermost lock is released
2905 (with wlock being higher level than 'lock')."""
2905 (with wlock being higher level than 'lock')."""
2906 for ref in (self._wlockref, self._lockref):
2906 for ref in (self._wlockref, self._lockref):
2907 l = ref and ref()
2907 l = ref and ref()
2908 if l and l.held:
2908 if l and l.held:
2909 l.postrelease.append(callback)
2909 l.postrelease.append(callback)
2910 break
2910 break
2911 else: # no lock have been found.
2911 else: # no lock have been found.
2912 callback(True)
2912 callback(True)
2913
2913
2914 def lock(self, wait=True):
2914 def lock(self, wait=True):
2915 """Lock the repository store (.hg/store) and return a weak reference
2915 """Lock the repository store (.hg/store) and return a weak reference
2916 to the lock. Use this before modifying the store (e.g. committing or
2916 to the lock. Use this before modifying the store (e.g. committing or
2917 stripping). If you are opening a transaction, get a lock as well.)
2917 stripping). If you are opening a transaction, get a lock as well.)
2918
2918
2919 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
2919 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
2920 'wlock' first to avoid a dead-lock hazard."""
2920 'wlock' first to avoid a dead-lock hazard."""
2921 l = self._currentlock(self._lockref)
2921 l = self._currentlock(self._lockref)
2922 if l is not None:
2922 if l is not None:
2923 l.lock()
2923 l.lock()
2924 return l
2924 return l
2925
2925
2926 l = self._lock(
2926 l = self._lock(
2927 vfs=self.svfs,
2927 vfs=self.svfs,
2928 lockname=b"lock",
2928 lockname=b"lock",
2929 wait=wait,
2929 wait=wait,
2930 releasefn=None,
2930 releasefn=None,
2931 acquirefn=self.invalidate,
2931 acquirefn=self.invalidate,
2932 desc=_(b'repository %s') % self.origroot,
2932 desc=_(b'repository %s') % self.origroot,
2933 )
2933 )
2934 self._lockref = weakref.ref(l)
2934 self._lockref = weakref.ref(l)
2935 return l
2935 return l
2936
2936
2937 def wlock(self, wait=True):
2937 def wlock(self, wait=True):
2938 """Lock the non-store parts of the repository (everything under
2938 """Lock the non-store parts of the repository (everything under
2939 .hg except .hg/store) and return a weak reference to the lock.
2939 .hg except .hg/store) and return a weak reference to the lock.
2940
2940
2941 Use this before modifying files in .hg.
2941 Use this before modifying files in .hg.
2942
2942
2943 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
2943 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
2944 'wlock' first to avoid a dead-lock hazard."""
2944 'wlock' first to avoid a dead-lock hazard."""
2945 l = self._wlockref() if self._wlockref else None
2945 l = self._wlockref() if self._wlockref else None
2946 if l is not None and l.held:
2946 if l is not None and l.held:
2947 l.lock()
2947 l.lock()
2948 return l
2948 return l
2949
2949
2950 # We do not need to check for non-waiting lock acquisition. Such
2950 # We do not need to check for non-waiting lock acquisition. Such
2951 # acquisition would not cause dead-lock as they would just fail.
2951 # acquisition would not cause dead-lock as they would just fail.
2952 if wait and (
2952 if wait and (
2953 self.ui.configbool(b'devel', b'all-warnings')
2953 self.ui.configbool(b'devel', b'all-warnings')
2954 or self.ui.configbool(b'devel', b'check-locks')
2954 or self.ui.configbool(b'devel', b'check-locks')
2955 ):
2955 ):
2956 if self._currentlock(self._lockref) is not None:
2956 if self._currentlock(self._lockref) is not None:
2957 self.ui.develwarn(b'"wlock" acquired after "lock"')
2957 self.ui.develwarn(b'"wlock" acquired after "lock"')
2958
2958
2959 def unlock():
2959 def unlock():
2960 if self.dirstate.pendingparentchange():
2960 if self.dirstate.pendingparentchange():
2961 self.dirstate.invalidate()
2961 self.dirstate.invalidate()
2962 else:
2962 else:
2963 self.dirstate.write(None)
2963 self.dirstate.write(None)
2964
2964
2965 self._filecache[b'dirstate'].refresh()
2965 self._filecache[b'dirstate'].refresh()
2966
2966
2967 l = self._lock(
2967 l = self._lock(
2968 self.vfs,
2968 self.vfs,
2969 b"wlock",
2969 b"wlock",
2970 wait,
2970 wait,
2971 unlock,
2971 unlock,
2972 self.invalidatedirstate,
2972 self.invalidatedirstate,
2973 _(b'working directory of %s') % self.origroot,
2973 _(b'working directory of %s') % self.origroot,
2974 )
2974 )
2975 self._wlockref = weakref.ref(l)
2975 self._wlockref = weakref.ref(l)
2976 return l
2976 return l
2977
2977
2978 def _currentlock(self, lockref):
2978 def _currentlock(self, lockref):
2979 """Returns the lock if it's held, or None if it's not."""
2979 """Returns the lock if it's held, or None if it's not."""
2980 if lockref is None:
2980 if lockref is None:
2981 return None
2981 return None
2982 l = lockref()
2982 l = lockref()
2983 if l is None or not l.held:
2983 if l is None or not l.held:
2984 return None
2984 return None
2985 return l
2985 return l
2986
2986
2987 def currentwlock(self):
2987 def currentwlock(self):
2988 """Returns the wlock if it's held, or None if it's not."""
2988 """Returns the wlock if it's held, or None if it's not."""
2989 return self._currentlock(self._wlockref)
2989 return self._currentlock(self._wlockref)
2990
2990
2991 def checkcommitpatterns(self, wctx, match, status, fail):
2991 def checkcommitpatterns(self, wctx, match, status, fail):
2992 """check for commit arguments that aren't committable"""
2992 """check for commit arguments that aren't committable"""
2993 if match.isexact() or match.prefix():
2993 if match.isexact() or match.prefix():
2994 matched = set(status.modified + status.added + status.removed)
2994 matched = set(status.modified + status.added + status.removed)
2995
2995
2996 for f in match.files():
2996 for f in match.files():
2997 f = self.dirstate.normalize(f)
2997 f = self.dirstate.normalize(f)
2998 if f == b'.' or f in matched or f in wctx.substate:
2998 if f == b'.' or f in matched or f in wctx.substate:
2999 continue
2999 continue
3000 if f in status.deleted:
3000 if f in status.deleted:
3001 fail(f, _(b'file not found!'))
3001 fail(f, _(b'file not found!'))
3002 # Is it a directory that exists or used to exist?
3002 # Is it a directory that exists or used to exist?
3003 if self.wvfs.isdir(f) or wctx.p1().hasdir(f):
3003 if self.wvfs.isdir(f) or wctx.p1().hasdir(f):
3004 d = f + b'/'
3004 d = f + b'/'
3005 for mf in matched:
3005 for mf in matched:
3006 if mf.startswith(d):
3006 if mf.startswith(d):
3007 break
3007 break
3008 else:
3008 else:
3009 fail(f, _(b"no match under directory!"))
3009 fail(f, _(b"no match under directory!"))
3010 elif f not in self.dirstate:
3010 elif f not in self.dirstate:
3011 fail(f, _(b"file not tracked!"))
3011 fail(f, _(b"file not tracked!"))
3012
3012
3013 @unfilteredmethod
3013 @unfilteredmethod
3014 def commit(
3014 def commit(
3015 self,
3015 self,
3016 text=b"",
3016 text=b"",
3017 user=None,
3017 user=None,
3018 date=None,
3018 date=None,
3019 match=None,
3019 match=None,
3020 force=False,
3020 force=False,
3021 editor=None,
3021 editor=None,
3022 extra=None,
3022 extra=None,
3023 ):
3023 ):
3024 """Add a new revision to current repository.
3024 """Add a new revision to current repository.
3025
3025
3026 Revision information is gathered from the working directory,
3026 Revision information is gathered from the working directory,
3027 match can be used to filter the committed files. If editor is
3027 match can be used to filter the committed files. If editor is
3028 supplied, it is called to get a commit message.
3028 supplied, it is called to get a commit message.
3029 """
3029 """
3030 if extra is None:
3030 if extra is None:
3031 extra = {}
3031 extra = {}
3032
3032
3033 def fail(f, msg):
3033 def fail(f, msg):
3034 raise error.InputError(b'%s: %s' % (f, msg))
3034 raise error.InputError(b'%s: %s' % (f, msg))
3035
3035
3036 if not match:
3036 if not match:
3037 match = matchmod.always()
3037 match = matchmod.always()
3038
3038
3039 if not force:
3039 if not force:
3040 match.bad = fail
3040 match.bad = fail
3041
3041
3042 # lock() for recent changelog (see issue4368)
3042 # lock() for recent changelog (see issue4368)
3043 with self.wlock(), self.lock():
3043 with self.wlock(), self.lock():
3044 wctx = self[None]
3044 wctx = self[None]
3045 merge = len(wctx.parents()) > 1
3045 merge = len(wctx.parents()) > 1
3046
3046
3047 if not force and merge and not match.always():
3047 if not force and merge and not match.always():
3048 raise error.Abort(
3048 raise error.Abort(
3049 _(
3049 _(
3050 b'cannot partially commit a merge '
3050 b'cannot partially commit a merge '
3051 b'(do not specify files or patterns)'
3051 b'(do not specify files or patterns)'
3052 )
3052 )
3053 )
3053 )
3054
3054
3055 status = self.status(match=match, clean=force)
3055 status = self.status(match=match, clean=force)
3056 if force:
3056 if force:
3057 status.modified.extend(
3057 status.modified.extend(
3058 status.clean
3058 status.clean
3059 ) # mq may commit clean files
3059 ) # mq may commit clean files
3060
3060
3061 # check subrepos
3061 # check subrepos
3062 subs, commitsubs, newstate = subrepoutil.precommit(
3062 subs, commitsubs, newstate = subrepoutil.precommit(
3063 self.ui, wctx, status, match, force=force
3063 self.ui, wctx, status, match, force=force
3064 )
3064 )
3065
3065
3066 # make sure all explicit patterns are matched
3066 # make sure all explicit patterns are matched
3067 if not force:
3067 if not force:
3068 self.checkcommitpatterns(wctx, match, status, fail)
3068 self.checkcommitpatterns(wctx, match, status, fail)
3069
3069
3070 cctx = context.workingcommitctx(
3070 cctx = context.workingcommitctx(
3071 self, status, text, user, date, extra
3071 self, status, text, user, date, extra
3072 )
3072 )
3073
3073
3074 ms = mergestatemod.mergestate.read(self)
3074 ms = mergestatemod.mergestate.read(self)
3075 mergeutil.checkunresolved(ms)
3075 mergeutil.checkunresolved(ms)
3076
3076
3077 # internal config: ui.allowemptycommit
3077 # internal config: ui.allowemptycommit
3078 if cctx.isempty() and not self.ui.configbool(
3078 if cctx.isempty() and not self.ui.configbool(
3079 b'ui', b'allowemptycommit'
3079 b'ui', b'allowemptycommit'
3080 ):
3080 ):
3081 self.ui.debug(b'nothing to commit, clearing merge state\n')
3081 self.ui.debug(b'nothing to commit, clearing merge state\n')
3082 ms.reset()
3082 ms.reset()
3083 return None
3083 return None
3084
3084
3085 if merge and cctx.deleted():
3085 if merge and cctx.deleted():
3086 raise error.Abort(_(b"cannot commit merge with missing files"))
3086 raise error.Abort(_(b"cannot commit merge with missing files"))
3087
3087
3088 if editor:
3088 if editor:
3089 cctx._text = editor(self, cctx, subs)
3089 cctx._text = editor(self, cctx, subs)
3090 edited = text != cctx._text
3090 edited = text != cctx._text
3091
3091
3092 # Save commit message in case this transaction gets rolled back
3092 # Save commit message in case this transaction gets rolled back
3093 # (e.g. by a pretxncommit hook). Leave the content alone on
3093 # (e.g. by a pretxncommit hook). Leave the content alone on
3094 # the assumption that the user will use the same editor again.
3094 # the assumption that the user will use the same editor again.
3095 msgfn = self.savecommitmessage(cctx._text)
3095 msgfn = self.savecommitmessage(cctx._text)
3096
3096
3097 # commit subs and write new state
3097 # commit subs and write new state
3098 if subs:
3098 if subs:
3099 uipathfn = scmutil.getuipathfn(self)
3099 uipathfn = scmutil.getuipathfn(self)
3100 for s in sorted(commitsubs):
3100 for s in sorted(commitsubs):
3101 sub = wctx.sub(s)
3101 sub = wctx.sub(s)
3102 self.ui.status(
3102 self.ui.status(
3103 _(b'committing subrepository %s\n')
3103 _(b'committing subrepository %s\n')
3104 % uipathfn(subrepoutil.subrelpath(sub))
3104 % uipathfn(subrepoutil.subrelpath(sub))
3105 )
3105 )
3106 sr = sub.commit(cctx._text, user, date)
3106 sr = sub.commit(cctx._text, user, date)
3107 newstate[s] = (newstate[s][0], sr)
3107 newstate[s] = (newstate[s][0], sr)
3108 subrepoutil.writestate(self, newstate)
3108 subrepoutil.writestate(self, newstate)
3109
3109
3110 p1, p2 = self.dirstate.parents()
3110 p1, p2 = self.dirstate.parents()
3111 hookp1, hookp2 = hex(p1), (p2 != self.nullid and hex(p2) or b'')
3111 hookp1, hookp2 = hex(p1), (p2 != self.nullid and hex(p2) or b'')
3112 try:
3112 try:
3113 self.hook(
3113 self.hook(
3114 b"precommit", throw=True, parent1=hookp1, parent2=hookp2
3114 b"precommit", throw=True, parent1=hookp1, parent2=hookp2
3115 )
3115 )
3116 with self.transaction(b'commit'):
3116 with self.transaction(b'commit'):
3117 ret = self.commitctx(cctx, True)
3117 ret = self.commitctx(cctx, True)
3118 # update bookmarks, dirstate and mergestate
3118 # update bookmarks, dirstate and mergestate
3119 bookmarks.update(self, [p1, p2], ret)
3119 bookmarks.update(self, [p1, p2], ret)
3120 cctx.markcommitted(ret)
3120 cctx.markcommitted(ret)
3121 ms.reset()
3121 ms.reset()
3122 except: # re-raises
3122 except: # re-raises
3123 if edited:
3123 if edited:
3124 self.ui.write(
3124 self.ui.write(
3125 _(b'note: commit message saved in %s\n') % msgfn
3125 _(b'note: commit message saved in %s\n') % msgfn
3126 )
3126 )
3127 self.ui.write(
3127 self.ui.write(
3128 _(
3128 _(
3129 b"note: use 'hg commit --logfile "
3129 b"note: use 'hg commit --logfile "
3130 b".hg/last-message.txt --edit' to reuse it\n"
3130 b".hg/last-message.txt --edit' to reuse it\n"
3131 )
3131 )
3132 )
3132 )
3133 raise
3133 raise
3134
3134
3135 def commithook(unused_success):
3135 def commithook(unused_success):
3136 # hack for command that use a temporary commit (eg: histedit)
3136 # hack for command that use a temporary commit (eg: histedit)
3137 # temporary commit got stripped before hook release
3137 # temporary commit got stripped before hook release
3138 if self.changelog.hasnode(ret):
3138 if self.changelog.hasnode(ret):
3139 self.hook(
3139 self.hook(
3140 b"commit", node=hex(ret), parent1=hookp1, parent2=hookp2
3140 b"commit", node=hex(ret), parent1=hookp1, parent2=hookp2
3141 )
3141 )
3142
3142
3143 self._afterlock(commithook)
3143 self._afterlock(commithook)
3144 return ret
3144 return ret
3145
3145
3146 @unfilteredmethod
3146 @unfilteredmethod
3147 def commitctx(self, ctx, error=False, origctx=None):
3147 def commitctx(self, ctx, error=False, origctx=None):
3148 return commit.commitctx(self, ctx, error=error, origctx=origctx)
3148 return commit.commitctx(self, ctx, error=error, origctx=origctx)
3149
3149
3150 @unfilteredmethod
3150 @unfilteredmethod
3151 def destroying(self):
3151 def destroying(self):
3152 """Inform the repository that nodes are about to be destroyed.
3152 """Inform the repository that nodes are about to be destroyed.
3153 Intended for use by strip and rollback, so there's a common
3153 Intended for use by strip and rollback, so there's a common
3154 place for anything that has to be done before destroying history.
3154 place for anything that has to be done before destroying history.
3155
3155
3156 This is mostly useful for saving state that is in memory and waiting
3156 This is mostly useful for saving state that is in memory and waiting
3157 to be flushed when the current lock is released. Because a call to
3157 to be flushed when the current lock is released. Because a call to
3158 destroyed is imminent, the repo will be invalidated causing those
3158 destroyed is imminent, the repo will be invalidated causing those
3159 changes to stay in memory (waiting for the next unlock), or vanish
3159 changes to stay in memory (waiting for the next unlock), or vanish
3160 completely.
3160 completely.
3161 """
3161 """
3162 # When using the same lock to commit and strip, the phasecache is left
3162 # When using the same lock to commit and strip, the phasecache is left
3163 # dirty after committing. Then when we strip, the repo is invalidated,
3163 # dirty after committing. Then when we strip, the repo is invalidated,
3164 # causing those changes to disappear.
3164 # causing those changes to disappear.
3165 if '_phasecache' in vars(self):
3165 if '_phasecache' in vars(self):
3166 self._phasecache.write()
3166 self._phasecache.write()
3167
3167
3168 @unfilteredmethod
3168 @unfilteredmethod
3169 def destroyed(self):
3169 def destroyed(self):
3170 """Inform the repository that nodes have been destroyed.
3170 """Inform the repository that nodes have been destroyed.
3171 Intended for use by strip and rollback, so there's a common
3171 Intended for use by strip and rollback, so there's a common
3172 place for anything that has to be done after destroying history.
3172 place for anything that has to be done after destroying history.
3173 """
3173 """
3174 # When one tries to:
3174 # When one tries to:
3175 # 1) destroy nodes thus calling this method (e.g. strip)
3175 # 1) destroy nodes thus calling this method (e.g. strip)
3176 # 2) use phasecache somewhere (e.g. commit)
3176 # 2) use phasecache somewhere (e.g. commit)
3177 #
3177 #
3178 # then 2) will fail because the phasecache contains nodes that were
3178 # then 2) will fail because the phasecache contains nodes that were
3179 # removed. We can either remove phasecache from the filecache,
3179 # removed. We can either remove phasecache from the filecache,
3180 # causing it to reload next time it is accessed, or simply filter
3180 # causing it to reload next time it is accessed, or simply filter
3181 # the removed nodes now and write the updated cache.
3181 # the removed nodes now and write the updated cache.
3182 self._phasecache.filterunknown(self)
3182 self._phasecache.filterunknown(self)
3183 self._phasecache.write()
3183 self._phasecache.write()
3184
3184
3185 # refresh all repository caches
3185 # refresh all repository caches
3186 self.updatecaches()
3186 self.updatecaches()
3187
3187
3188 # Ensure the persistent tag cache is updated. Doing it now
3188 # Ensure the persistent tag cache is updated. Doing it now
3189 # means that the tag cache only has to worry about destroyed
3189 # means that the tag cache only has to worry about destroyed
3190 # heads immediately after a strip/rollback. That in turn
3190 # heads immediately after a strip/rollback. That in turn
3191 # guarantees that "cachetip == currenttip" (comparing both rev
3191 # guarantees that "cachetip == currenttip" (comparing both rev
3192 # and node) always means no nodes have been added or destroyed.
3192 # and node) always means no nodes have been added or destroyed.
3193
3193
3194 # XXX this is suboptimal when qrefresh'ing: we strip the current
3194 # XXX this is suboptimal when qrefresh'ing: we strip the current
3195 # head, refresh the tag cache, then immediately add a new head.
3195 # head, refresh the tag cache, then immediately add a new head.
3196 # But I think doing it this way is necessary for the "instant
3196 # But I think doing it this way is necessary for the "instant
3197 # tag cache retrieval" case to work.
3197 # tag cache retrieval" case to work.
3198 self.invalidate()
3198 self.invalidate()
3199
3199
3200 def status(
3200 def status(
3201 self,
3201 self,
3202 node1=b'.',
3202 node1=b'.',
3203 node2=None,
3203 node2=None,
3204 match=None,
3204 match=None,
3205 ignored=False,
3205 ignored=False,
3206 clean=False,
3206 clean=False,
3207 unknown=False,
3207 unknown=False,
3208 listsubrepos=False,
3208 listsubrepos=False,
3209 ):
3209 ):
3210 '''a convenience method that calls node1.status(node2)'''
3210 '''a convenience method that calls node1.status(node2)'''
3211 return self[node1].status(
3211 return self[node1].status(
3212 node2, match, ignored, clean, unknown, listsubrepos
3212 node2, match, ignored, clean, unknown, listsubrepos
3213 )
3213 )
3214
3214
3215 def addpostdsstatus(self, ps):
3215 def addpostdsstatus(self, ps):
3216 """Add a callback to run within the wlock, at the point at which status
3216 """Add a callback to run within the wlock, at the point at which status
3217 fixups happen.
3217 fixups happen.
3218
3218
3219 On status completion, callback(wctx, status) will be called with the
3219 On status completion, callback(wctx, status) will be called with the
3220 wlock held, unless the dirstate has changed from underneath or the wlock
3220 wlock held, unless the dirstate has changed from underneath or the wlock
3221 couldn't be grabbed.
3221 couldn't be grabbed.
3222
3222
3223 Callbacks should not capture and use a cached copy of the dirstate --
3223 Callbacks should not capture and use a cached copy of the dirstate --
3224 it might change in the meanwhile. Instead, they should access the
3224 it might change in the meanwhile. Instead, they should access the
3225 dirstate via wctx.repo().dirstate.
3225 dirstate via wctx.repo().dirstate.
3226
3226
3227 This list is emptied out after each status run -- extensions should
3227 This list is emptied out after each status run -- extensions should
3228 make sure it adds to this list each time dirstate.status is called.
3228 make sure it adds to this list each time dirstate.status is called.
3229 Extensions should also make sure they don't call this for statuses
3229 Extensions should also make sure they don't call this for statuses
3230 that don't involve the dirstate.
3230 that don't involve the dirstate.
3231 """
3231 """
3232
3232
3233 # The list is located here for uniqueness reasons -- it is actually
3233 # The list is located here for uniqueness reasons -- it is actually
3234 # managed by the workingctx, but that isn't unique per-repo.
3234 # managed by the workingctx, but that isn't unique per-repo.
3235 self._postdsstatus.append(ps)
3235 self._postdsstatus.append(ps)
3236
3236
3237 def postdsstatus(self):
3237 def postdsstatus(self):
3238 """Used by workingctx to get the list of post-dirstate-status hooks."""
3238 """Used by workingctx to get the list of post-dirstate-status hooks."""
3239 return self._postdsstatus
3239 return self._postdsstatus
3240
3240
3241 def clearpostdsstatus(self):
3241 def clearpostdsstatus(self):
3242 """Used by workingctx to clear post-dirstate-status hooks."""
3242 """Used by workingctx to clear post-dirstate-status hooks."""
3243 del self._postdsstatus[:]
3243 del self._postdsstatus[:]
3244
3244
3245 def heads(self, start=None):
3245 def heads(self, start=None):
3246 if start is None:
3246 if start is None:
3247 cl = self.changelog
3247 cl = self.changelog
3248 headrevs = reversed(cl.headrevs())
3248 headrevs = reversed(cl.headrevs())
3249 return [cl.node(rev) for rev in headrevs]
3249 return [cl.node(rev) for rev in headrevs]
3250
3250
3251 heads = self.changelog.heads(start)
3251 heads = self.changelog.heads(start)
3252 # sort the output in rev descending order
3252 # sort the output in rev descending order
3253 return sorted(heads, key=self.changelog.rev, reverse=True)
3253 return sorted(heads, key=self.changelog.rev, reverse=True)
3254
3254
3255 def branchheads(self, branch=None, start=None, closed=False):
3255 def branchheads(self, branch=None, start=None, closed=False):
3256 """return a (possibly filtered) list of heads for the given branch
3256 """return a (possibly filtered) list of heads for the given branch
3257
3257
3258 Heads are returned in topological order, from newest to oldest.
3258 Heads are returned in topological order, from newest to oldest.
3259 If branch is None, use the dirstate branch.
3259 If branch is None, use the dirstate branch.
3260 If start is not None, return only heads reachable from start.
3260 If start is not None, return only heads reachable from start.
3261 If closed is True, return heads that are marked as closed as well.
3261 If closed is True, return heads that are marked as closed as well.
3262 """
3262 """
3263 if branch is None:
3263 if branch is None:
3264 branch = self[None].branch()
3264 branch = self[None].branch()
3265 branches = self.branchmap()
3265 branches = self.branchmap()
3266 if not branches.hasbranch(branch):
3266 if not branches.hasbranch(branch):
3267 return []
3267 return []
3268 # the cache returns heads ordered lowest to highest
3268 # the cache returns heads ordered lowest to highest
3269 bheads = list(reversed(branches.branchheads(branch, closed=closed)))
3269 bheads = list(reversed(branches.branchheads(branch, closed=closed)))
3270 if start is not None:
3270 if start is not None:
3271 # filter out the heads that cannot be reached from startrev
3271 # filter out the heads that cannot be reached from startrev
3272 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
3272 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
3273 bheads = [h for h in bheads if h in fbheads]
3273 bheads = [h for h in bheads if h in fbheads]
3274 return bheads
3274 return bheads
3275
3275
3276 def branches(self, nodes):
3276 def branches(self, nodes):
3277 if not nodes:
3277 if not nodes:
3278 nodes = [self.changelog.tip()]
3278 nodes = [self.changelog.tip()]
3279 b = []
3279 b = []
3280 for n in nodes:
3280 for n in nodes:
3281 t = n
3281 t = n
3282 while True:
3282 while True:
3283 p = self.changelog.parents(n)
3283 p = self.changelog.parents(n)
3284 if p[1] != self.nullid or p[0] == self.nullid:
3284 if p[1] != self.nullid or p[0] == self.nullid:
3285 b.append((t, n, p[0], p[1]))
3285 b.append((t, n, p[0], p[1]))
3286 break
3286 break
3287 n = p[0]
3287 n = p[0]
3288 return b
3288 return b
3289
3289
3290 def between(self, pairs):
3290 def between(self, pairs):
3291 r = []
3291 r = []
3292
3292
3293 for top, bottom in pairs:
3293 for top, bottom in pairs:
3294 n, l, i = top, [], 0
3294 n, l, i = top, [], 0
3295 f = 1
3295 f = 1
3296
3296
3297 while n != bottom and n != self.nullid:
3297 while n != bottom and n != self.nullid:
3298 p = self.changelog.parents(n)[0]
3298 p = self.changelog.parents(n)[0]
3299 if i == f:
3299 if i == f:
3300 l.append(n)
3300 l.append(n)
3301 f = f * 2
3301 f = f * 2
3302 n = p
3302 n = p
3303 i += 1
3303 i += 1
3304
3304
3305 r.append(l)
3305 r.append(l)
3306
3306
3307 return r
3307 return r
3308
3308
3309 def checkpush(self, pushop):
3309 def checkpush(self, pushop):
3310 """Extensions can override this function if additional checks have
3310 """Extensions can override this function if additional checks have
3311 to be performed before pushing, or call it if they override push
3311 to be performed before pushing, or call it if they override push
3312 command.
3312 command.
3313 """
3313 """
3314
3314
3315 @unfilteredpropertycache
3315 @unfilteredpropertycache
3316 def prepushoutgoinghooks(self):
3316 def prepushoutgoinghooks(self):
3317 """Return util.hooks consists of a pushop with repo, remote, outgoing
3317 """Return util.hooks consists of a pushop with repo, remote, outgoing
3318 methods, which are called before pushing changesets.
3318 methods, which are called before pushing changesets.
3319 """
3319 """
3320 return util.hooks()
3320 return util.hooks()
3321
3321
3322 def pushkey(self, namespace, key, old, new):
3322 def pushkey(self, namespace, key, old, new):
3323 try:
3323 try:
3324 tr = self.currenttransaction()
3324 tr = self.currenttransaction()
3325 hookargs = {}
3325 hookargs = {}
3326 if tr is not None:
3326 if tr is not None:
3327 hookargs.update(tr.hookargs)
3327 hookargs.update(tr.hookargs)
3328 hookargs = pycompat.strkwargs(hookargs)
3328 hookargs = pycompat.strkwargs(hookargs)
3329 hookargs['namespace'] = namespace
3329 hookargs['namespace'] = namespace
3330 hookargs['key'] = key
3330 hookargs['key'] = key
3331 hookargs['old'] = old
3331 hookargs['old'] = old
3332 hookargs['new'] = new
3332 hookargs['new'] = new
3333 self.hook(b'prepushkey', throw=True, **hookargs)
3333 self.hook(b'prepushkey', throw=True, **hookargs)
3334 except error.HookAbort as exc:
3334 except error.HookAbort as exc:
3335 self.ui.write_err(_(b"pushkey-abort: %s\n") % exc)
3335 self.ui.write_err(_(b"pushkey-abort: %s\n") % exc)
3336 if exc.hint:
3336 if exc.hint:
3337 self.ui.write_err(_(b"(%s)\n") % exc.hint)
3337 self.ui.write_err(_(b"(%s)\n") % exc.hint)
3338 return False
3338 return False
3339 self.ui.debug(b'pushing key for "%s:%s"\n' % (namespace, key))
3339 self.ui.debug(b'pushing key for "%s:%s"\n' % (namespace, key))
3340 ret = pushkey.push(self, namespace, key, old, new)
3340 ret = pushkey.push(self, namespace, key, old, new)
3341
3341
3342 def runhook(unused_success):
3342 def runhook(unused_success):
3343 self.hook(
3343 self.hook(
3344 b'pushkey',
3344 b'pushkey',
3345 namespace=namespace,
3345 namespace=namespace,
3346 key=key,
3346 key=key,
3347 old=old,
3347 old=old,
3348 new=new,
3348 new=new,
3349 ret=ret,
3349 ret=ret,
3350 )
3350 )
3351
3351
3352 self._afterlock(runhook)
3352 self._afterlock(runhook)
3353 return ret
3353 return ret
3354
3354
3355 def listkeys(self, namespace):
3355 def listkeys(self, namespace):
3356 self.hook(b'prelistkeys', throw=True, namespace=namespace)
3356 self.hook(b'prelistkeys', throw=True, namespace=namespace)
3357 self.ui.debug(b'listing keys for "%s"\n' % namespace)
3357 self.ui.debug(b'listing keys for "%s"\n' % namespace)
3358 values = pushkey.list(self, namespace)
3358 values = pushkey.list(self, namespace)
3359 self.hook(b'listkeys', namespace=namespace, values=values)
3359 self.hook(b'listkeys', namespace=namespace, values=values)
3360 return values
3360 return values
3361
3361
3362 def debugwireargs(self, one, two, three=None, four=None, five=None):
3362 def debugwireargs(self, one, two, three=None, four=None, five=None):
3363 '''used to test argument passing over the wire'''
3363 '''used to test argument passing over the wire'''
3364 return b"%s %s %s %s %s" % (
3364 return b"%s %s %s %s %s" % (
3365 one,
3365 one,
3366 two,
3366 two,
3367 pycompat.bytestr(three),
3367 pycompat.bytestr(three),
3368 pycompat.bytestr(four),
3368 pycompat.bytestr(four),
3369 pycompat.bytestr(five),
3369 pycompat.bytestr(five),
3370 )
3370 )
3371
3371
3372 def savecommitmessage(self, text):
3372 def savecommitmessage(self, text):
3373 fp = self.vfs(b'last-message.txt', b'wb')
3373 fp = self.vfs(b'last-message.txt', b'wb')
3374 try:
3374 try:
3375 fp.write(text)
3375 fp.write(text)
3376 finally:
3376 finally:
3377 fp.close()
3377 fp.close()
3378 return self.pathto(fp.name[len(self.root) + 1 :])
3378 return self.pathto(fp.name[len(self.root) + 1 :])
3379
3379
3380 def register_wanted_sidedata(self, category):
3380 def register_wanted_sidedata(self, category):
3381 if repository.REPO_FEATURE_SIDE_DATA not in self.features:
3381 if repository.REPO_FEATURE_SIDE_DATA not in self.features:
3382 # Only revlogv2 repos can want sidedata.
3382 # Only revlogv2 repos can want sidedata.
3383 return
3383 return
3384 self._wanted_sidedata.add(pycompat.bytestr(category))
3384 self._wanted_sidedata.add(pycompat.bytestr(category))
3385
3385
3386 def register_sidedata_computer(
3386 def register_sidedata_computer(
3387 self, kind, category, keys, computer, flags, replace=False
3387 self, kind, category, keys, computer, flags, replace=False
3388 ):
3388 ):
3389 if kind not in revlogconst.ALL_KINDS:
3389 if kind not in revlogconst.ALL_KINDS:
3390 msg = _(b"unexpected revlog kind '%s'.")
3390 msg = _(b"unexpected revlog kind '%s'.")
3391 raise error.ProgrammingError(msg % kind)
3391 raise error.ProgrammingError(msg % kind)
3392 category = pycompat.bytestr(category)
3392 category = pycompat.bytestr(category)
3393 already_registered = category in self._sidedata_computers.get(kind, [])
3393 already_registered = category in self._sidedata_computers.get(kind, [])
3394 if already_registered and not replace:
3394 if already_registered and not replace:
3395 msg = _(
3395 msg = _(
3396 b"cannot register a sidedata computer twice for category '%s'."
3396 b"cannot register a sidedata computer twice for category '%s'."
3397 )
3397 )
3398 raise error.ProgrammingError(msg % category)
3398 raise error.ProgrammingError(msg % category)
3399 if replace and not already_registered:
3399 if replace and not already_registered:
3400 msg = _(
3400 msg = _(
3401 b"cannot replace a sidedata computer that isn't registered "
3401 b"cannot replace a sidedata computer that isn't registered "
3402 b"for category '%s'."
3402 b"for category '%s'."
3403 )
3403 )
3404 raise error.ProgrammingError(msg % category)
3404 raise error.ProgrammingError(msg % category)
3405 self._sidedata_computers.setdefault(kind, {})
3405 self._sidedata_computers.setdefault(kind, {})
3406 self._sidedata_computers[kind][category] = (keys, computer, flags)
3406 self._sidedata_computers[kind][category] = (keys, computer, flags)
3407
3407
3408
3408
3409 # used to avoid circular references so destructors work
3409 # used to avoid circular references so destructors work
3410 def aftertrans(files):
3410 def aftertrans(files):
3411 renamefiles = [tuple(t) for t in files]
3411 renamefiles = [tuple(t) for t in files]
3412
3412
3413 def a():
3413 def a():
3414 for vfs, src, dest in renamefiles:
3414 for vfs, src, dest in renamefiles:
3415 # if src and dest refer to a same file, vfs.rename is a no-op,
3415 # if src and dest refer to a same file, vfs.rename is a no-op,
3416 # leaving both src and dest on disk. delete dest to make sure
3416 # leaving both src and dest on disk. delete dest to make sure
3417 # the rename couldn't be such a no-op.
3417 # the rename couldn't be such a no-op.
3418 vfs.tryunlink(dest)
3418 vfs.tryunlink(dest)
3419 try:
3419 try:
3420 vfs.rename(src, dest)
3420 vfs.rename(src, dest)
3421 except OSError: # journal file does not yet exist
3421 except OSError: # journal file does not yet exist
3422 pass
3422 pass
3423
3423
3424 return a
3424 return a
3425
3425
3426
3426
3427 def undoname(fn):
3427 def undoname(fn):
3428 base, name = os.path.split(fn)
3428 base, name = os.path.split(fn)
3429 assert name.startswith(b'journal')
3429 assert name.startswith(b'journal')
3430 return os.path.join(base, name.replace(b'journal', b'undo', 1))
3430 return os.path.join(base, name.replace(b'journal', b'undo', 1))
3431
3431
3432
3432
3433 def instance(ui, path, create, intents=None, createopts=None):
3433 def instance(ui, path, create, intents=None, createopts=None):
3434 localpath = urlutil.urllocalpath(path)
3434 localpath = urlutil.urllocalpath(path)
3435 if create:
3435 if create:
3436 createrepository(ui, localpath, createopts=createopts)
3436 createrepository(ui, localpath, createopts=createopts)
3437
3437
3438 return makelocalrepository(ui, localpath, intents=intents)
3438 return makelocalrepository(ui, localpath, intents=intents)
3439
3439
3440
3440
3441 def islocal(path):
3441 def islocal(path):
3442 return True
3442 return True
3443
3443
3444
3444
3445 def defaultcreateopts(ui, createopts=None):
3445 def defaultcreateopts(ui, createopts=None):
3446 """Populate the default creation options for a repository.
3446 """Populate the default creation options for a repository.
3447
3447
3448 A dictionary of explicitly requested creation options can be passed
3448 A dictionary of explicitly requested creation options can be passed
3449 in. Missing keys will be populated.
3449 in. Missing keys will be populated.
3450 """
3450 """
3451 createopts = dict(createopts or {})
3451 createopts = dict(createopts or {})
3452
3452
3453 if b'backend' not in createopts:
3453 if b'backend' not in createopts:
3454 # experimental config: storage.new-repo-backend
3454 # experimental config: storage.new-repo-backend
3455 createopts[b'backend'] = ui.config(b'storage', b'new-repo-backend')
3455 createopts[b'backend'] = ui.config(b'storage', b'new-repo-backend')
3456
3456
3457 return createopts
3457 return createopts
3458
3458
3459
3459
3460 def newreporequirements(ui, createopts):
3460 def newreporequirements(ui, createopts):
3461 """Determine the set of requirements for a new local repository.
3461 """Determine the set of requirements for a new local repository.
3462
3462
3463 Extensions can wrap this function to specify custom requirements for
3463 Extensions can wrap this function to specify custom requirements for
3464 new repositories.
3464 new repositories.
3465 """
3465 """
3466 # If the repo is being created from a shared repository, we copy
3466 # If the repo is being created from a shared repository, we copy
3467 # its requirements.
3467 # its requirements.
3468 if b'sharedrepo' in createopts:
3468 if b'sharedrepo' in createopts:
3469 requirements = set(createopts[b'sharedrepo'].requirements)
3469 requirements = set(createopts[b'sharedrepo'].requirements)
3470 if createopts.get(b'sharedrelative'):
3470 if createopts.get(b'sharedrelative'):
3471 requirements.add(requirementsmod.RELATIVE_SHARED_REQUIREMENT)
3471 requirements.add(requirementsmod.RELATIVE_SHARED_REQUIREMENT)
3472 else:
3472 else:
3473 requirements.add(requirementsmod.SHARED_REQUIREMENT)
3473 requirements.add(requirementsmod.SHARED_REQUIREMENT)
3474
3474
3475 return requirements
3475 return requirements
3476
3476
3477 if b'backend' not in createopts:
3477 if b'backend' not in createopts:
3478 raise error.ProgrammingError(
3478 raise error.ProgrammingError(
3479 b'backend key not present in createopts; '
3479 b'backend key not present in createopts; '
3480 b'was defaultcreateopts() called?'
3480 b'was defaultcreateopts() called?'
3481 )
3481 )
3482
3482
3483 if createopts[b'backend'] != b'revlogv1':
3483 if createopts[b'backend'] != b'revlogv1':
3484 raise error.Abort(
3484 raise error.Abort(
3485 _(
3485 _(
3486 b'unable to determine repository requirements for '
3486 b'unable to determine repository requirements for '
3487 b'storage backend: %s'
3487 b'storage backend: %s'
3488 )
3488 )
3489 % createopts[b'backend']
3489 % createopts[b'backend']
3490 )
3490 )
3491
3491
3492 requirements = {requirementsmod.REVLOGV1_REQUIREMENT}
3492 requirements = {requirementsmod.REVLOGV1_REQUIREMENT}
3493 if ui.configbool(b'format', b'usestore'):
3493 if ui.configbool(b'format', b'usestore'):
3494 requirements.add(requirementsmod.STORE_REQUIREMENT)
3494 requirements.add(requirementsmod.STORE_REQUIREMENT)
3495 if ui.configbool(b'format', b'usefncache'):
3495 if ui.configbool(b'format', b'usefncache'):
3496 requirements.add(requirementsmod.FNCACHE_REQUIREMENT)
3496 requirements.add(requirementsmod.FNCACHE_REQUIREMENT)
3497 if ui.configbool(b'format', b'dotencode'):
3497 if ui.configbool(b'format', b'dotencode'):
3498 requirements.add(requirementsmod.DOTENCODE_REQUIREMENT)
3498 requirements.add(requirementsmod.DOTENCODE_REQUIREMENT)
3499
3499
3500 compengines = ui.configlist(b'format', b'revlog-compression')
3500 compengines = ui.configlist(b'format', b'revlog-compression')
3501 for compengine in compengines:
3501 for compengine in compengines:
3502 if compengine in util.compengines:
3502 if compengine in util.compengines:
3503 engine = util.compengines[compengine]
3503 engine = util.compengines[compengine]
3504 if engine.available() and engine.revlogheader():
3504 if engine.available() and engine.revlogheader():
3505 break
3505 break
3506 else:
3506 else:
3507 raise error.Abort(
3507 raise error.Abort(
3508 _(
3508 _(
3509 b'compression engines %s defined by '
3509 b'compression engines %s defined by '
3510 b'format.revlog-compression not available'
3510 b'format.revlog-compression not available'
3511 )
3511 )
3512 % b', '.join(b'"%s"' % e for e in compengines),
3512 % b', '.join(b'"%s"' % e for e in compengines),
3513 hint=_(
3513 hint=_(
3514 b'run "hg debuginstall" to list available '
3514 b'run "hg debuginstall" to list available '
3515 b'compression engines'
3515 b'compression engines'
3516 ),
3516 ),
3517 )
3517 )
3518
3518
3519 # zlib is the historical default and doesn't need an explicit requirement.
3519 # zlib is the historical default and doesn't need an explicit requirement.
3520 if compengine == b'zstd':
3520 if compengine == b'zstd':
3521 requirements.add(b'revlog-compression-zstd')
3521 requirements.add(b'revlog-compression-zstd')
3522 elif compengine != b'zlib':
3522 elif compengine != b'zlib':
3523 requirements.add(b'exp-compression-%s' % compengine)
3523 requirements.add(b'exp-compression-%s' % compengine)
3524
3524
3525 if scmutil.gdinitconfig(ui):
3525 if scmutil.gdinitconfig(ui):
3526 requirements.add(requirementsmod.GENERALDELTA_REQUIREMENT)
3526 requirements.add(requirementsmod.GENERALDELTA_REQUIREMENT)
3527 if ui.configbool(b'format', b'sparse-revlog'):
3527 if ui.configbool(b'format', b'sparse-revlog'):
3528 requirements.add(requirementsmod.SPARSEREVLOG_REQUIREMENT)
3528 requirements.add(requirementsmod.SPARSEREVLOG_REQUIREMENT)
3529
3529
3530 # experimental config: format.exp-use-copies-side-data-changeset
3530 # experimental config: format.exp-use-copies-side-data-changeset
3531 if ui.configbool(b'format', b'exp-use-copies-side-data-changeset'):
3531 if ui.configbool(b'format', b'exp-use-copies-side-data-changeset'):
3532 requirements.discard(requirementsmod.REVLOGV1_REQUIREMENT)
3532 requirements.add(requirementsmod.CHANGELOGV2_REQUIREMENT)
3533 requirements.add(requirementsmod.REVLOGV2_REQUIREMENT)
3534 requirements.add(requirementsmod.COPIESSDC_REQUIREMENT)
3533 requirements.add(requirementsmod.COPIESSDC_REQUIREMENT)
3535 if ui.configbool(b'experimental', b'treemanifest'):
3534 if ui.configbool(b'experimental', b'treemanifest'):
3536 requirements.add(requirementsmod.TREEMANIFEST_REQUIREMENT)
3535 requirements.add(requirementsmod.TREEMANIFEST_REQUIREMENT)
3537
3536
3538 changelogv2 = ui.config(b'format', b'exp-use-changelog-v2')
3537 changelogv2 = ui.config(b'format', b'exp-use-changelog-v2')
3539 if changelogv2 == b'enable-unstable-format-and-corrupt-my-data':
3538 if changelogv2 == b'enable-unstable-format-and-corrupt-my-data':
3540 requirements.add(requirementsmod.CHANGELOGV2_REQUIREMENT)
3539 requirements.add(requirementsmod.CHANGELOGV2_REQUIREMENT)
3541
3540
3542 revlogv2 = ui.config(b'experimental', b'revlogv2')
3541 revlogv2 = ui.config(b'experimental', b'revlogv2')
3543 if revlogv2 == b'enable-unstable-format-and-corrupt-my-data':
3542 if revlogv2 == b'enable-unstable-format-and-corrupt-my-data':
3544 requirements.discard(requirementsmod.REVLOGV1_REQUIREMENT)
3543 requirements.discard(requirementsmod.REVLOGV1_REQUIREMENT)
3545 requirements.add(requirementsmod.REVLOGV2_REQUIREMENT)
3544 requirements.add(requirementsmod.REVLOGV2_REQUIREMENT)
3546 # experimental config: format.internal-phase
3545 # experimental config: format.internal-phase
3547 if ui.configbool(b'format', b'internal-phase'):
3546 if ui.configbool(b'format', b'internal-phase'):
3548 requirements.add(requirementsmod.INTERNAL_PHASE_REQUIREMENT)
3547 requirements.add(requirementsmod.INTERNAL_PHASE_REQUIREMENT)
3549
3548
3550 if createopts.get(b'narrowfiles'):
3549 if createopts.get(b'narrowfiles'):
3551 requirements.add(requirementsmod.NARROW_REQUIREMENT)
3550 requirements.add(requirementsmod.NARROW_REQUIREMENT)
3552
3551
3553 if createopts.get(b'lfs'):
3552 if createopts.get(b'lfs'):
3554 requirements.add(b'lfs')
3553 requirements.add(b'lfs')
3555
3554
3556 if ui.configbool(b'format', b'bookmarks-in-store'):
3555 if ui.configbool(b'format', b'bookmarks-in-store'):
3557 requirements.add(bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT)
3556 requirements.add(bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT)
3558
3557
3559 if ui.configbool(b'format', b'use-persistent-nodemap'):
3558 if ui.configbool(b'format', b'use-persistent-nodemap'):
3560 requirements.add(requirementsmod.NODEMAP_REQUIREMENT)
3559 requirements.add(requirementsmod.NODEMAP_REQUIREMENT)
3561
3560
3562 # if share-safe is enabled, let's create the new repository with the new
3561 # if share-safe is enabled, let's create the new repository with the new
3563 # requirement
3562 # requirement
3564 if ui.configbool(b'format', b'use-share-safe'):
3563 if ui.configbool(b'format', b'use-share-safe'):
3565 requirements.add(requirementsmod.SHARESAFE_REQUIREMENT)
3564 requirements.add(requirementsmod.SHARESAFE_REQUIREMENT)
3566
3565
3567 return requirements
3566 return requirements
3568
3567
3569
3568
3570 def checkrequirementscompat(ui, requirements):
3569 def checkrequirementscompat(ui, requirements):
3571 """Checks compatibility of repository requirements enabled and disabled.
3570 """Checks compatibility of repository requirements enabled and disabled.
3572
3571
3573 Returns a set of requirements which needs to be dropped because dependend
3572 Returns a set of requirements which needs to be dropped because dependend
3574 requirements are not enabled. Also warns users about it"""
3573 requirements are not enabled. Also warns users about it"""
3575
3574
3576 dropped = set()
3575 dropped = set()
3577
3576
3578 if requirementsmod.STORE_REQUIREMENT not in requirements:
3577 if requirementsmod.STORE_REQUIREMENT not in requirements:
3579 if bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT in requirements:
3578 if bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT in requirements:
3580 ui.warn(
3579 ui.warn(
3581 _(
3580 _(
3582 b'ignoring enabled \'format.bookmarks-in-store\' config '
3581 b'ignoring enabled \'format.bookmarks-in-store\' config '
3583 b'beacuse it is incompatible with disabled '
3582 b'beacuse it is incompatible with disabled '
3584 b'\'format.usestore\' config\n'
3583 b'\'format.usestore\' config\n'
3585 )
3584 )
3586 )
3585 )
3587 dropped.add(bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT)
3586 dropped.add(bookmarks.BOOKMARKS_IN_STORE_REQUIREMENT)
3588
3587
3589 if (
3588 if (
3590 requirementsmod.SHARED_REQUIREMENT in requirements
3589 requirementsmod.SHARED_REQUIREMENT in requirements
3591 or requirementsmod.RELATIVE_SHARED_REQUIREMENT in requirements
3590 or requirementsmod.RELATIVE_SHARED_REQUIREMENT in requirements
3592 ):
3591 ):
3593 raise error.Abort(
3592 raise error.Abort(
3594 _(
3593 _(
3595 b"cannot create shared repository as source was created"
3594 b"cannot create shared repository as source was created"
3596 b" with 'format.usestore' config disabled"
3595 b" with 'format.usestore' config disabled"
3597 )
3596 )
3598 )
3597 )
3599
3598
3600 if requirementsmod.SHARESAFE_REQUIREMENT in requirements:
3599 if requirementsmod.SHARESAFE_REQUIREMENT in requirements:
3601 ui.warn(
3600 ui.warn(
3602 _(
3601 _(
3603 b"ignoring enabled 'format.use-share-safe' config because "
3602 b"ignoring enabled 'format.use-share-safe' config because "
3604 b"it is incompatible with disabled 'format.usestore'"
3603 b"it is incompatible with disabled 'format.usestore'"
3605 b" config\n"
3604 b" config\n"
3606 )
3605 )
3607 )
3606 )
3608 dropped.add(requirementsmod.SHARESAFE_REQUIREMENT)
3607 dropped.add(requirementsmod.SHARESAFE_REQUIREMENT)
3609
3608
3610 return dropped
3609 return dropped
3611
3610
3612
3611
3613 def filterknowncreateopts(ui, createopts):
3612 def filterknowncreateopts(ui, createopts):
3614 """Filters a dict of repo creation options against options that are known.
3613 """Filters a dict of repo creation options against options that are known.
3615
3614
3616 Receives a dict of repo creation options and returns a dict of those
3615 Receives a dict of repo creation options and returns a dict of those
3617 options that we don't know how to handle.
3616 options that we don't know how to handle.
3618
3617
3619 This function is called as part of repository creation. If the
3618 This function is called as part of repository creation. If the
3620 returned dict contains any items, repository creation will not
3619 returned dict contains any items, repository creation will not
3621 be allowed, as it means there was a request to create a repository
3620 be allowed, as it means there was a request to create a repository
3622 with options not recognized by loaded code.
3621 with options not recognized by loaded code.
3623
3622
3624 Extensions can wrap this function to filter out creation options
3623 Extensions can wrap this function to filter out creation options
3625 they know how to handle.
3624 they know how to handle.
3626 """
3625 """
3627 known = {
3626 known = {
3628 b'backend',
3627 b'backend',
3629 b'lfs',
3628 b'lfs',
3630 b'narrowfiles',
3629 b'narrowfiles',
3631 b'sharedrepo',
3630 b'sharedrepo',
3632 b'sharedrelative',
3631 b'sharedrelative',
3633 b'shareditems',
3632 b'shareditems',
3634 b'shallowfilestore',
3633 b'shallowfilestore',
3635 }
3634 }
3636
3635
3637 return {k: v for k, v in createopts.items() if k not in known}
3636 return {k: v for k, v in createopts.items() if k not in known}
3638
3637
3639
3638
3640 def createrepository(ui, path, createopts=None):
3639 def createrepository(ui, path, createopts=None):
3641 """Create a new repository in a vfs.
3640 """Create a new repository in a vfs.
3642
3641
3643 ``path`` path to the new repo's working directory.
3642 ``path`` path to the new repo's working directory.
3644 ``createopts`` options for the new repository.
3643 ``createopts`` options for the new repository.
3645
3644
3646 The following keys for ``createopts`` are recognized:
3645 The following keys for ``createopts`` are recognized:
3647
3646
3648 backend
3647 backend
3649 The storage backend to use.
3648 The storage backend to use.
3650 lfs
3649 lfs
3651 Repository will be created with ``lfs`` requirement. The lfs extension
3650 Repository will be created with ``lfs`` requirement. The lfs extension
3652 will automatically be loaded when the repository is accessed.
3651 will automatically be loaded when the repository is accessed.
3653 narrowfiles
3652 narrowfiles
3654 Set up repository to support narrow file storage.
3653 Set up repository to support narrow file storage.
3655 sharedrepo
3654 sharedrepo
3656 Repository object from which storage should be shared.
3655 Repository object from which storage should be shared.
3657 sharedrelative
3656 sharedrelative
3658 Boolean indicating if the path to the shared repo should be
3657 Boolean indicating if the path to the shared repo should be
3659 stored as relative. By default, the pointer to the "parent" repo
3658 stored as relative. By default, the pointer to the "parent" repo
3660 is stored as an absolute path.
3659 is stored as an absolute path.
3661 shareditems
3660 shareditems
3662 Set of items to share to the new repository (in addition to storage).
3661 Set of items to share to the new repository (in addition to storage).
3663 shallowfilestore
3662 shallowfilestore
3664 Indicates that storage for files should be shallow (not all ancestor
3663 Indicates that storage for files should be shallow (not all ancestor
3665 revisions are known).
3664 revisions are known).
3666 """
3665 """
3667 createopts = defaultcreateopts(ui, createopts=createopts)
3666 createopts = defaultcreateopts(ui, createopts=createopts)
3668
3667
3669 unknownopts = filterknowncreateopts(ui, createopts)
3668 unknownopts = filterknowncreateopts(ui, createopts)
3670
3669
3671 if not isinstance(unknownopts, dict):
3670 if not isinstance(unknownopts, dict):
3672 raise error.ProgrammingError(
3671 raise error.ProgrammingError(
3673 b'filterknowncreateopts() did not return a dict'
3672 b'filterknowncreateopts() did not return a dict'
3674 )
3673 )
3675
3674
3676 if unknownopts:
3675 if unknownopts:
3677 raise error.Abort(
3676 raise error.Abort(
3678 _(
3677 _(
3679 b'unable to create repository because of unknown '
3678 b'unable to create repository because of unknown '
3680 b'creation option: %s'
3679 b'creation option: %s'
3681 )
3680 )
3682 % b', '.join(sorted(unknownopts)),
3681 % b', '.join(sorted(unknownopts)),
3683 hint=_(b'is a required extension not loaded?'),
3682 hint=_(b'is a required extension not loaded?'),
3684 )
3683 )
3685
3684
3686 requirements = newreporequirements(ui, createopts=createopts)
3685 requirements = newreporequirements(ui, createopts=createopts)
3687 requirements -= checkrequirementscompat(ui, requirements)
3686 requirements -= checkrequirementscompat(ui, requirements)
3688
3687
3689 wdirvfs = vfsmod.vfs(path, expandpath=True, realpath=True)
3688 wdirvfs = vfsmod.vfs(path, expandpath=True, realpath=True)
3690
3689
3691 hgvfs = vfsmod.vfs(wdirvfs.join(b'.hg'))
3690 hgvfs = vfsmod.vfs(wdirvfs.join(b'.hg'))
3692 if hgvfs.exists():
3691 if hgvfs.exists():
3693 raise error.RepoError(_(b'repository %s already exists') % path)
3692 raise error.RepoError(_(b'repository %s already exists') % path)
3694
3693
3695 if b'sharedrepo' in createopts:
3694 if b'sharedrepo' in createopts:
3696 sharedpath = createopts[b'sharedrepo'].sharedpath
3695 sharedpath = createopts[b'sharedrepo'].sharedpath
3697
3696
3698 if createopts.get(b'sharedrelative'):
3697 if createopts.get(b'sharedrelative'):
3699 try:
3698 try:
3700 sharedpath = os.path.relpath(sharedpath, hgvfs.base)
3699 sharedpath = os.path.relpath(sharedpath, hgvfs.base)
3701 sharedpath = util.pconvert(sharedpath)
3700 sharedpath = util.pconvert(sharedpath)
3702 except (IOError, ValueError) as e:
3701 except (IOError, ValueError) as e:
3703 # ValueError is raised on Windows if the drive letters differ
3702 # ValueError is raised on Windows if the drive letters differ
3704 # on each path.
3703 # on each path.
3705 raise error.Abort(
3704 raise error.Abort(
3706 _(b'cannot calculate relative path'),
3705 _(b'cannot calculate relative path'),
3707 hint=stringutil.forcebytestr(e),
3706 hint=stringutil.forcebytestr(e),
3708 )
3707 )
3709
3708
3710 if not wdirvfs.exists():
3709 if not wdirvfs.exists():
3711 wdirvfs.makedirs()
3710 wdirvfs.makedirs()
3712
3711
3713 hgvfs.makedir(notindexed=True)
3712 hgvfs.makedir(notindexed=True)
3714 if b'sharedrepo' not in createopts:
3713 if b'sharedrepo' not in createopts:
3715 hgvfs.mkdir(b'cache')
3714 hgvfs.mkdir(b'cache')
3716 hgvfs.mkdir(b'wcache')
3715 hgvfs.mkdir(b'wcache')
3717
3716
3718 has_store = requirementsmod.STORE_REQUIREMENT in requirements
3717 has_store = requirementsmod.STORE_REQUIREMENT in requirements
3719 if has_store and b'sharedrepo' not in createopts:
3718 if has_store and b'sharedrepo' not in createopts:
3720 hgvfs.mkdir(b'store')
3719 hgvfs.mkdir(b'store')
3721
3720
3722 # We create an invalid changelog outside the store so very old
3721 # We create an invalid changelog outside the store so very old
3723 # Mercurial versions (which didn't know about the requirements
3722 # Mercurial versions (which didn't know about the requirements
3724 # file) encounter an error on reading the changelog. This
3723 # file) encounter an error on reading the changelog. This
3725 # effectively locks out old clients and prevents them from
3724 # effectively locks out old clients and prevents them from
3726 # mucking with a repo in an unknown format.
3725 # mucking with a repo in an unknown format.
3727 #
3726 #
3728 # The revlog header has version 65535, which won't be recognized by
3727 # The revlog header has version 65535, which won't be recognized by
3729 # such old clients.
3728 # such old clients.
3730 hgvfs.append(
3729 hgvfs.append(
3731 b'00changelog.i',
3730 b'00changelog.i',
3732 b'\0\0\xFF\xFF dummy changelog to prevent using the old repo '
3731 b'\0\0\xFF\xFF dummy changelog to prevent using the old repo '
3733 b'layout',
3732 b'layout',
3734 )
3733 )
3735
3734
3736 # Filter the requirements into working copy and store ones
3735 # Filter the requirements into working copy and store ones
3737 wcreq, storereq = scmutil.filterrequirements(requirements)
3736 wcreq, storereq = scmutil.filterrequirements(requirements)
3738 # write working copy ones
3737 # write working copy ones
3739 scmutil.writerequires(hgvfs, wcreq)
3738 scmutil.writerequires(hgvfs, wcreq)
3740 # If there are store requirements and the current repository
3739 # If there are store requirements and the current repository
3741 # is not a shared one, write stored requirements
3740 # is not a shared one, write stored requirements
3742 # For new shared repository, we don't need to write the store
3741 # For new shared repository, we don't need to write the store
3743 # requirements as they are already present in store requires
3742 # requirements as they are already present in store requires
3744 if storereq and b'sharedrepo' not in createopts:
3743 if storereq and b'sharedrepo' not in createopts:
3745 storevfs = vfsmod.vfs(hgvfs.join(b'store'), cacheaudited=True)
3744 storevfs = vfsmod.vfs(hgvfs.join(b'store'), cacheaudited=True)
3746 scmutil.writerequires(storevfs, storereq)
3745 scmutil.writerequires(storevfs, storereq)
3747
3746
3748 # Write out file telling readers where to find the shared store.
3747 # Write out file telling readers where to find the shared store.
3749 if b'sharedrepo' in createopts:
3748 if b'sharedrepo' in createopts:
3750 hgvfs.write(b'sharedpath', sharedpath)
3749 hgvfs.write(b'sharedpath', sharedpath)
3751
3750
3752 if createopts.get(b'shareditems'):
3751 if createopts.get(b'shareditems'):
3753 shared = b'\n'.join(sorted(createopts[b'shareditems'])) + b'\n'
3752 shared = b'\n'.join(sorted(createopts[b'shareditems'])) + b'\n'
3754 hgvfs.write(b'shared', shared)
3753 hgvfs.write(b'shared', shared)
3755
3754
3756
3755
3757 def poisonrepository(repo):
3756 def poisonrepository(repo):
3758 """Poison a repository instance so it can no longer be used."""
3757 """Poison a repository instance so it can no longer be used."""
3759 # Perform any cleanup on the instance.
3758 # Perform any cleanup on the instance.
3760 repo.close()
3759 repo.close()
3761
3760
3762 # Our strategy is to replace the type of the object with one that
3761 # Our strategy is to replace the type of the object with one that
3763 # has all attribute lookups result in error.
3762 # has all attribute lookups result in error.
3764 #
3763 #
3765 # But we have to allow the close() method because some constructors
3764 # But we have to allow the close() method because some constructors
3766 # of repos call close() on repo references.
3765 # of repos call close() on repo references.
3767 class poisonedrepository(object):
3766 class poisonedrepository(object):
3768 def __getattribute__(self, item):
3767 def __getattribute__(self, item):
3769 if item == 'close':
3768 if item == 'close':
3770 return object.__getattribute__(self, item)
3769 return object.__getattribute__(self, item)
3771
3770
3772 raise error.ProgrammingError(
3771 raise error.ProgrammingError(
3773 b'repo instances should not be used after unshare'
3772 b'repo instances should not be used after unshare'
3774 )
3773 )
3775
3774
3776 def close(self):
3775 def close(self):
3777 pass
3776 pass
3778
3777
3779 # We may have a repoview, which intercepts __setattr__. So be sure
3778 # We may have a repoview, which intercepts __setattr__. So be sure
3780 # we operate at the lowest level possible.
3779 # we operate at the lowest level possible.
3781 object.__setattr__(repo, '__class__', poisonedrepository)
3780 object.__setattr__(repo, '__class__', poisonedrepository)
@@ -1,3874 +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 no no
1663 changelog-v2: no no no
1663 changelog-v2: no yes no
1664 plain-cl-delta: yes yes yes
1664 plain-cl-delta: yes yes yes
1665 compression: * (glob)
1665 compression: * (glob)
1666 compression-level: default default default
1666 compression-level: default default default
1667 $ hg debugupgraderepo --run --quiet
1667 $ hg debugupgraderepo --run --quiet
1668 upgrade will perform the following actions:
1668 upgrade will perform the following actions:
1669
1669
1670 requirements
1670 requirements
1671 preserved: * (glob)
1671 preserved: * (glob)
1672 removed: revlogv1
1672 added: exp-changelog-v2, exp-copies-sidedata-changeset
1673 added: exp-copies-sidedata-changeset, exp-revlogv2.2
1674
1673
1675 processed revlogs:
1674 processed revlogs:
1676 - all-filelogs
1675 - all-filelogs
1677 - changelog
1676 - changelog
1678 - manifest
1677 - manifest
1679
1678
1680 #endif
1679 #endif
1681
1680
1682 #if upgraded-parallel
1681 #if upgraded-parallel
1683 $ cat >> $HGRCPATH << EOF
1682 $ cat >> $HGRCPATH << EOF
1684 > [format]
1683 > [format]
1685 > exp-use-copies-side-data-changeset = yes
1684 > exp-use-copies-side-data-changeset = yes
1686 > [experimental]
1685 > [experimental]
1687 > worker.repository-upgrade=yes
1686 > worker.repository-upgrade=yes
1688 > [worker]
1687 > [worker]
1689 > enabled=yes
1688 > enabled=yes
1690 > numcpus=8
1689 > numcpus=8
1691 > EOF
1690 > EOF
1692 $ hg debugformat -v
1691 $ hg debugformat -v
1693 format-variant repo config default
1692 format-variant repo config default
1694 fncache: yes yes yes
1693 fncache: yes yes yes
1695 dotencode: yes yes yes
1694 dotencode: yes yes yes
1696 generaldelta: yes yes yes
1695 generaldelta: yes yes yes
1697 share-safe: no no no
1696 share-safe: no no no
1698 sparserevlog: yes yes yes
1697 sparserevlog: yes yes yes
1699 persistent-nodemap: no no no (no-rust !)
1698 persistent-nodemap: no no no (no-rust !)
1700 persistent-nodemap: yes yes no (rust !)
1699 persistent-nodemap: yes yes no (rust !)
1701 copies-sdc: no yes no
1700 copies-sdc: no yes no
1702 revlog-v2: no yes no
1701 revlog-v2: no no no
1703 changelog-v2: no no no
1702 changelog-v2: no yes no
1704 plain-cl-delta: yes yes yes
1703 plain-cl-delta: yes yes yes
1705 compression: * (glob)
1704 compression: * (glob)
1706 compression-level: default default default
1705 compression-level: default default default
1707 $ hg debugupgraderepo --run --quiet
1706 $ hg debugupgraderepo --run --quiet
1708 upgrade will perform the following actions:
1707 upgrade will perform the following actions:
1709
1708
1710 requirements
1709 requirements
1711 preserved: * (glob)
1710 preserved: * (glob)
1712 removed: revlogv1
1711 added: exp-changelog-v2, exp-copies-sidedata-changeset
1713 added: exp-copies-sidedata-changeset, exp-revlogv2.2
1714
1712
1715 processed revlogs:
1713 processed revlogs:
1716 - all-filelogs
1714 - all-filelogs
1717 - changelog
1715 - changelog
1718 - manifest
1716 - manifest
1719
1717
1720 #endif
1718 #endif
1721
1719
1722 #if pull
1720 #if pull
1723 $ cd ..
1721 $ cd ..
1724 $ mv repo-chain repo-source
1722 $ mv repo-chain repo-source
1725 $ hg init repo-chain
1723 $ hg init repo-chain
1726 $ cd repo-chain
1724 $ cd repo-chain
1727 $ hg pull ../repo-source
1725 $ hg pull ../repo-source
1728 pulling from ../repo-source
1726 pulling from ../repo-source
1729 requesting all changes
1727 requesting all changes
1730 adding changesets
1728 adding changesets
1731 adding manifests
1729 adding manifests
1732 adding file changes
1730 adding file changes
1733 added 80 changesets with 44 changes to 25 files (+39 heads)
1731 added 80 changesets with 44 changes to 25 files (+39 heads)
1734 new changesets a3a31bbefea6:908ce9259ffa
1732 new changesets a3a31bbefea6:908ce9259ffa
1735 (run 'hg heads' to see heads, 'hg merge' to merge)
1733 (run 'hg heads' to see heads, 'hg merge' to merge)
1736 #endif
1734 #endif
1737
1735
1738 #if pull-upgrade
1736 #if pull-upgrade
1739 $ cat >> $HGRCPATH << EOF
1737 $ cat >> $HGRCPATH << EOF
1740 > [format]
1738 > [format]
1741 > exp-use-copies-side-data-changeset = yes
1739 > exp-use-copies-side-data-changeset = yes
1742 > [experimental]
1740 > [experimental]
1743 > changegroup4 = yes
1741 > changegroup4 = yes
1744 > EOF
1742 > EOF
1745 $ cd ..
1743 $ cd ..
1746 $ mv repo-chain repo-source
1744 $ mv repo-chain repo-source
1747 $ hg init repo-chain
1745 $ hg init repo-chain
1748 $ cd repo-chain
1746 $ cd repo-chain
1749 $ hg pull ../repo-source
1747 $ hg pull ../repo-source
1750 pulling from ../repo-source
1748 pulling from ../repo-source
1751 requesting all changes
1749 requesting all changes
1752 adding changesets
1750 adding changesets
1753 adding manifests
1751 adding manifests
1754 adding file changes
1752 adding file changes
1755 added 80 changesets with 44 changes to 25 files (+39 heads)
1753 added 80 changesets with 44 changes to 25 files (+39 heads)
1756 new changesets a3a31bbefea6:908ce9259ffa
1754 new changesets a3a31bbefea6:908ce9259ffa
1757 (run 'hg heads' to see heads, 'hg merge' to merge)
1755 (run 'hg heads' to see heads, 'hg merge' to merge)
1758 #endif
1756 #endif
1759
1757
1760 #if push
1758 #if push
1761 $ cd ..
1759 $ cd ..
1762 $ mv repo-chain repo-source
1760 $ mv repo-chain repo-source
1763 $ hg init repo-chain
1761 $ hg init repo-chain
1764 $ cd repo-source
1762 $ cd repo-source
1765 $ hg push ../repo-chain
1763 $ hg push ../repo-chain
1766 pushing to ../repo-chain
1764 pushing to ../repo-chain
1767 searching for changes
1765 searching for changes
1768 adding changesets
1766 adding changesets
1769 adding manifests
1767 adding manifests
1770 adding file changes
1768 adding file changes
1771 added 80 changesets with 44 changes to 25 files (+39 heads)
1769 added 80 changesets with 44 changes to 25 files (+39 heads)
1772 $ cd ../repo-chain
1770 $ cd ../repo-chain
1773 #endif
1771 #endif
1774
1772
1775 #if push-upgrade
1773 #if push-upgrade
1776 $ cat >> $HGRCPATH << EOF
1774 $ cat >> $HGRCPATH << EOF
1777 > [format]
1775 > [format]
1778 > exp-use-copies-side-data-changeset = yes
1776 > exp-use-copies-side-data-changeset = yes
1779 > [experimental]
1777 > [experimental]
1780 > changegroup4 = yes
1778 > changegroup4 = yes
1781 > EOF
1779 > EOF
1782 $ cd ..
1780 $ cd ..
1783 $ mv repo-chain repo-source
1781 $ mv repo-chain repo-source
1784 $ hg init repo-chain
1782 $ hg init repo-chain
1785 $ cd repo-source
1783 $ cd repo-source
1786 $ hg push ../repo-chain
1784 $ hg push ../repo-chain
1787 pushing to ../repo-chain
1785 pushing to ../repo-chain
1788 searching for changes
1786 searching for changes
1789 adding changesets
1787 adding changesets
1790 adding manifests
1788 adding manifests
1791 adding file changes
1789 adding file changes
1792 added 80 changesets with 44 changes to 25 files (+39 heads)
1790 added 80 changesets with 44 changes to 25 files (+39 heads)
1793 $ cd ../repo-chain
1791 $ cd ../repo-chain
1794 #endif
1792 #endif
1795
1793
1796 #if no-compatibility no-filelog no-changeset
1794 #if no-compatibility no-filelog no-changeset
1797
1795
1798 $ hg debugchangedfiles --compute 0
1796 $ hg debugchangedfiles --compute 0
1799 added : a, ;
1797 added : a, ;
1800 added : b, ;
1798 added : b, ;
1801 added : h, ;
1799 added : h, ;
1802 added : p, ;
1800 added : p, ;
1803 added : q, ;
1801 added : q, ;
1804 added : r, ;
1802 added : r, ;
1805
1803
1806 $ for rev in `hg log --rev 'all()' -T '{rev}\n'`; do
1804 $ for rev in `hg log --rev 'all()' -T '{rev}\n'`; do
1807 > case_id=`hg log -r $rev -T '{word(0, desc, ":")}\n'`
1805 > case_id=`hg log -r $rev -T '{word(0, desc, ":")}\n'`
1808 > echo "##### revision \"$case_id\" #####"
1806 > echo "##### revision \"$case_id\" #####"
1809 > hg debugsidedata -c -v -- $rev
1807 > hg debugsidedata -c -v -- $rev
1810 > hg debugchangedfiles $rev
1808 > hg debugchangedfiles $rev
1811 > done
1809 > done
1812 ##### revision "i-0 initial commit" #####
1810 ##### revision "i-0 initial commit" #####
1813 1 sidedata entries
1811 1 sidedata entries
1814 entry-0014 size 64
1812 entry-0014 size 64
1815 '\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'
1816 added : a, ;
1814 added : a, ;
1817 added : b, ;
1815 added : b, ;
1818 added : h, ;
1816 added : h, ;
1819 added : p, ;
1817 added : p, ;
1820 added : q, ;
1818 added : q, ;
1821 added : r, ;
1819 added : r, ;
1822 ##### revision "i-1" #####
1820 ##### revision "i-1" #####
1823 1 sidedata entries
1821 1 sidedata entries
1824 entry-0014 size 44
1822 entry-0014 size 44
1825 '\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'
1826 removed : a, ;
1824 removed : a, ;
1827 added p1: c, a;
1825 added p1: c, a;
1828 removed : p, ;
1826 removed : p, ;
1829 added p1: s, p;
1827 added p1: s, p;
1830 ##### revision "i-2" #####
1828 ##### revision "i-2" #####
1831 1 sidedata entries
1829 1 sidedata entries
1832 entry-0014 size 44
1830 entry-0014 size 44
1833 '\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'
1834 removed : c, ;
1832 removed : c, ;
1835 added p1: d, c;
1833 added p1: d, c;
1836 removed : s, ;
1834 removed : s, ;
1837 added p1: t, s;
1835 added p1: t, s;
1838 ##### revision "a-1" #####
1836 ##### revision "a-1" #####
1839 1 sidedata entries
1837 1 sidedata entries
1840 entry-0014 size 24
1838 entry-0014 size 24
1841 '\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'
1842 removed : d, ;
1840 removed : d, ;
1843 added p1: e, d;
1841 added p1: e, d;
1844 ##### revision "a-2" #####
1842 ##### revision "a-2" #####
1845 1 sidedata entries
1843 1 sidedata entries
1846 entry-0014 size 24
1844 entry-0014 size 24
1847 '\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'
1848 removed : e, ;
1846 removed : e, ;
1849 added p1: f, e;
1847 added p1: f, e;
1850 ##### revision "b-1" #####
1848 ##### revision "b-1" #####
1851 1 sidedata entries
1849 1 sidedata entries
1852 entry-0014 size 14
1850 entry-0014 size 14
1853 '\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'
1854 touched : b, ;
1852 touched : b, ;
1855 ##### revision "c-1 delete d" #####
1853 ##### revision "c-1 delete d" #####
1856 1 sidedata entries
1854 1 sidedata entries
1857 entry-0014 size 14
1855 entry-0014 size 14
1858 '\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'
1859 removed : d, ;
1857 removed : d, ;
1860 ##### revision "d-1 delete d" #####
1858 ##### revision "d-1 delete d" #####
1861 1 sidedata entries
1859 1 sidedata entries
1862 entry-0014 size 14
1860 entry-0014 size 14
1863 '\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'
1864 removed : d, ;
1862 removed : d, ;
1865 ##### revision "d-2 re-add d" #####
1863 ##### revision "d-2 re-add d" #####
1866 1 sidedata entries
1864 1 sidedata entries
1867 entry-0014 size 14
1865 entry-0014 size 14
1868 '\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'
1869 added : d, ;
1867 added : d, ;
1870 ##### revision "e-1 b -move-> g" #####
1868 ##### revision "e-1 b -move-> g" #####
1871 1 sidedata entries
1869 1 sidedata entries
1872 entry-0014 size 24
1870 entry-0014 size 24
1873 '\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'
1874 removed : b, ;
1872 removed : b, ;
1875 added p1: g, b;
1873 added p1: g, b;
1876 ##### revision "e-2 g -move-> f" #####
1874 ##### revision "e-2 g -move-> f" #####
1877 1 sidedata entries
1875 1 sidedata entries
1878 entry-0014 size 24
1876 entry-0014 size 24
1879 '\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'
1880 added p1: f, g;
1878 added p1: f, g;
1881 removed : g, ;
1879 removed : g, ;
1882 ##### revision "p-1" #####
1880 ##### revision "p-1" #####
1883 1 sidedata entries
1881 1 sidedata entries
1884 entry-0014 size 24
1882 entry-0014 size 24
1885 '\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'
1886 removed : t, ;
1884 removed : t, ;
1887 added p1: u, t;
1885 added p1: u, t;
1888 ##### revision "p-2" #####
1886 ##### revision "p-2" #####
1889 1 sidedata entries
1887 1 sidedata entries
1890 entry-0014 size 24
1888 entry-0014 size 24
1891 '\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'
1892 removed : u, ;
1890 removed : u, ;
1893 added p1: v, u;
1891 added p1: v, u;
1894 ##### revision "q-1 r -move-> w" #####
1892 ##### revision "q-1 r -move-> w" #####
1895 1 sidedata entries
1893 1 sidedata entries
1896 entry-0014 size 24
1894 entry-0014 size 24
1897 '\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'
1898 removed : r, ;
1896 removed : r, ;
1899 added p1: w, r;
1897 added p1: w, r;
1900 ##### revision "q-2 w -move-> v" #####
1898 ##### revision "q-2 w -move-> v" #####
1901 1 sidedata entries
1899 1 sidedata entries
1902 entry-0014 size 24
1900 entry-0014 size 24
1903 '\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'
1904 added p1: v, w;
1902 added p1: v, w;
1905 removed : w, ;
1903 removed : w, ;
1906 ##### revision "mBAm-0 simple merge - A side" #####
1904 ##### revision "mBAm-0 simple merge - A side" #####
1907 1 sidedata entries
1905 1 sidedata entries
1908 entry-0014 size 4
1906 entry-0014 size 4
1909 '\x00\x00\x00\x00'
1907 '\x00\x00\x00\x00'
1910 ##### revision "mABm-0 simple merge - A side" #####
1908 ##### revision "mABm-0 simple merge - A side" #####
1911 1 sidedata entries
1909 1 sidedata entries
1912 entry-0014 size 4
1910 entry-0014 size 4
1913 '\x00\x00\x00\x00'
1911 '\x00\x00\x00\x00'
1914 ##### revision "mBCm-0 simple merge - C side" #####
1912 ##### revision "mBCm-0 simple merge - C side" #####
1915 1 sidedata entries
1913 1 sidedata entries
1916 entry-0014 size 4
1914 entry-0014 size 4
1917 '\x00\x00\x00\x00'
1915 '\x00\x00\x00\x00'
1918 ##### revision "mBCm-1 re-add d" #####
1916 ##### revision "mBCm-1 re-add d" #####
1919 1 sidedata entries
1917 1 sidedata entries
1920 entry-0014 size 14
1918 entry-0014 size 14
1921 '\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'
1922 added : d, ;
1920 added : d, ;
1923 ##### revision "mCBm-0 simple merge - C side" #####
1921 ##### revision "mCBm-0 simple merge - C side" #####
1924 1 sidedata entries
1922 1 sidedata entries
1925 entry-0014 size 4
1923 entry-0014 size 4
1926 '\x00\x00\x00\x00'
1924 '\x00\x00\x00\x00'
1927 ##### revision "mCBm-1 re-add d" #####
1925 ##### revision "mCBm-1 re-add d" #####
1928 1 sidedata entries
1926 1 sidedata entries
1929 entry-0014 size 14
1927 entry-0014 size 14
1930 '\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'
1931 added : d, ;
1929 added : d, ;
1932 ##### revision "mBDm-0 simple merge - B side" #####
1930 ##### revision "mBDm-0 simple merge - B side" #####
1933 1 sidedata entries
1931 1 sidedata entries
1934 entry-0014 size 4
1932 entry-0014 size 4
1935 '\x00\x00\x00\x00'
1933 '\x00\x00\x00\x00'
1936 ##### revision "mDBm-0 simple merge - B side" #####
1934 ##### revision "mDBm-0 simple merge - B side" #####
1937 1 sidedata entries
1935 1 sidedata entries
1938 entry-0014 size 4
1936 entry-0014 size 4
1939 '\x00\x00\x00\x00'
1937 '\x00\x00\x00\x00'
1940 ##### 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" #####
1941 1 sidedata entries
1939 1 sidedata entries
1942 entry-0014 size 14
1940 entry-0014 size 14
1943 '\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'
1944 merged : f, ;
1942 merged : f, ;
1945 ##### 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" #####
1946 1 sidedata entries
1944 1 sidedata entries
1947 entry-0014 size 14
1945 entry-0014 size 14
1948 '\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'
1949 merged : f, ;
1947 merged : f, ;
1950 ##### 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" #####
1951 1 sidedata entries
1949 1 sidedata entries
1952 entry-0014 size 14
1950 entry-0014 size 14
1953 '\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'
1954 merged : v, ;
1952 merged : v, ;
1955 ##### 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" #####
1956 1 sidedata entries
1954 1 sidedata entries
1957 entry-0014 size 14
1955 entry-0014 size 14
1958 '\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'
1959 merged : v, ;
1957 merged : v, ;
1960 ##### revision "f-1" #####
1958 ##### revision "f-1" #####
1961 1 sidedata entries
1959 1 sidedata entries
1962 entry-0014 size 24
1960 entry-0014 size 24
1963 '\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'
1964 removed : h, ;
1962 removed : h, ;
1965 added p1: i, h;
1963 added p1: i, h;
1966 ##### revision "f-2" #####
1964 ##### revision "f-2" #####
1967 1 sidedata entries
1965 1 sidedata entries
1968 entry-0014 size 24
1966 entry-0014 size 24
1969 '\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'
1970 touched p1: d, i;
1968 touched p1: d, i;
1971 removed : i, ;
1969 removed : i, ;
1972 ##### revision "mBFm-0 simple merge - B side" #####
1970 ##### revision "mBFm-0 simple merge - B side" #####
1973 1 sidedata entries
1971 1 sidedata entries
1974 entry-0014 size 4
1972 entry-0014 size 4
1975 '\x00\x00\x00\x00'
1973 '\x00\x00\x00\x00'
1976 ##### revision "mFBm-0 simple merge - B side" #####
1974 ##### revision "mFBm-0 simple merge - B side" #####
1977 1 sidedata entries
1975 1 sidedata entries
1978 entry-0014 size 4
1976 entry-0014 size 4
1979 '\x00\x00\x00\x00'
1977 '\x00\x00\x00\x00'
1980 ##### revision "r-1" #####
1978 ##### revision "r-1" #####
1981 1 sidedata entries
1979 1 sidedata entries
1982 entry-0014 size 24
1980 entry-0014 size 24
1983 '\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'
1984 removed : r, ;
1982 removed : r, ;
1985 added p1: x, r;
1983 added p1: x, r;
1986 ##### revision "r-2" #####
1984 ##### revision "r-2" #####
1987 1 sidedata entries
1985 1 sidedata entries
1988 entry-0014 size 24
1986 entry-0014 size 24
1989 '\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'
1990 touched p1: t, x;
1988 touched p1: t, x;
1991 removed : x, ;
1989 removed : x, ;
1992 ##### revision "mBRm-0 simple merge - B side" #####
1990 ##### revision "mBRm-0 simple merge - B side" #####
1993 1 sidedata entries
1991 1 sidedata entries
1994 entry-0014 size 4
1992 entry-0014 size 4
1995 '\x00\x00\x00\x00'
1993 '\x00\x00\x00\x00'
1996 ##### revision "mRBm-0 simple merge - B side" #####
1994 ##### revision "mRBm-0 simple merge - B side" #####
1997 1 sidedata entries
1995 1 sidedata entries
1998 entry-0014 size 4
1996 entry-0014 size 4
1999 '\x00\x00\x00\x00'
1997 '\x00\x00\x00\x00'
2000 ##### revision "g-1" #####
1998 ##### revision "g-1" #####
2001 1 sidedata entries
1999 1 sidedata entries
2002 entry-0014 size 14
2000 entry-0014 size 14
2003 '\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'
2004 touched : d, ;
2002 touched : d, ;
2005 ##### 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" #####
2006 1 sidedata entries
2004 1 sidedata entries
2007 entry-0014 size 14
2005 entry-0014 size 14
2008 '\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'
2009 merged : d, ;
2007 merged : d, ;
2010 ##### 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" #####
2011 1 sidedata entries
2009 1 sidedata entries
2012 entry-0014 size 14
2010 entry-0014 size 14
2013 '\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'
2014 merged : d, ;
2012 merged : d, ;
2015 ##### revision "mFGm-0 merge - G side" #####
2013 ##### revision "mFGm-0 merge - G side" #####
2016 1 sidedata entries
2014 1 sidedata entries
2017 entry-0014 size 14
2015 entry-0014 size 14
2018 '\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'
2019 merged : d, ;
2017 merged : d, ;
2020 ##### revision "mGFm-0 merge - G side" #####
2018 ##### revision "mGFm-0 merge - G side" #####
2021 1 sidedata entries
2019 1 sidedata entries
2022 entry-0014 size 14
2020 entry-0014 size 14
2023 '\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'
2024 merged : d, ;
2022 merged : d, ;
2025 ##### 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" #####
2026 1 sidedata entries
2024 1 sidedata entries
2027 entry-0014 size 14
2025 entry-0014 size 14
2028 '\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'
2029 salvaged : d, ;
2027 salvaged : d, ;
2030 ##### 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" #####
2031 1 sidedata entries
2029 1 sidedata entries
2032 entry-0014 size 14
2030 entry-0014 size 14
2033 '\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'
2034 salvaged : d, ;
2032 salvaged : d, ;
2035 ##### 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" #####
2036 1 sidedata entries
2034 1 sidedata entries
2037 entry-0014 size 14
2035 entry-0014 size 14
2038 '\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'
2039 salvaged : d, ;
2037 salvaged : d, ;
2040 ##### 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" #####
2041 1 sidedata entries
2039 1 sidedata entries
2042 entry-0014 size 14
2040 entry-0014 size 14
2043 '\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'
2044 salvaged : d, ;
2042 salvaged : d, ;
2045 ##### revision "h-1" #####
2043 ##### revision "h-1" #####
2046 1 sidedata entries
2044 1 sidedata entries
2047 entry-0014 size 24
2045 entry-0014 size 24
2048 '\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'
2049 removed : b, ;
2047 removed : b, ;
2050 added p1: d, b;
2048 added p1: d, b;
2051 ##### revision "mCH-delete-before-conflict-m-0 simple merge - C side" #####
2049 ##### revision "mCH-delete-before-conflict-m-0 simple merge - C side" #####
2052 1 sidedata entries
2050 1 sidedata entries
2053 entry-0014 size 4
2051 entry-0014 size 4
2054 '\x00\x00\x00\x00'
2052 '\x00\x00\x00\x00'
2055 ##### revision "mHC-delete-before-conflict-m-0 simple merge - C side" #####
2053 ##### revision "mHC-delete-before-conflict-m-0 simple merge - C side" #####
2056 1 sidedata entries
2054 1 sidedata entries
2057 entry-0014 size 4
2055 entry-0014 size 4
2058 '\x00\x00\x00\x00'
2056 '\x00\x00\x00\x00'
2059 ##### 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" #####
2060 1 sidedata entries
2058 1 sidedata entries
2061 entry-0014 size 14
2059 entry-0014 size 14
2062 '\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'
2063 merged : f, ;
2061 merged : f, ;
2064 ##### 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" #####
2065 1 sidedata entries
2063 1 sidedata entries
2066 entry-0014 size 14
2064 entry-0014 size 14
2067 '\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'
2068 merged : f, ;
2066 merged : f, ;
2069 ##### revision "mBF-change-m-0 merge with extra change - B side" #####
2067 ##### revision "mBF-change-m-0 merge with extra change - B side" #####
2070 1 sidedata entries
2068 1 sidedata entries
2071 entry-0014 size 14
2069 entry-0014 size 14
2072 '\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'
2073 touched : d, ;
2071 touched : d, ;
2074 ##### revision "mFB-change-m-0 merge with extra change - B side" #####
2072 ##### revision "mFB-change-m-0 merge with extra change - B side" #####
2075 1 sidedata entries
2073 1 sidedata entries
2076 entry-0014 size 14
2074 entry-0014 size 14
2077 '\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'
2078 touched : d, ;
2076 touched : d, ;
2079 ##### 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" #####
2080 1 sidedata entries
2078 1 sidedata entries
2081 entry-0014 size 14
2079 entry-0014 size 14
2082 '\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'
2083 salvaged : d, ;
2081 salvaged : d, ;
2084 ##### 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" #####
2085 1 sidedata entries
2083 1 sidedata entries
2086 entry-0014 size 14
2084 entry-0014 size 14
2087 '\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'
2088 salvaged : d, ;
2086 salvaged : d, ;
2089 ##### revision "j-1" #####
2087 ##### revision "j-1" #####
2090 1 sidedata entries
2088 1 sidedata entries
2091 entry-0014 size 24
2089 entry-0014 size 24
2092 '\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'
2093 added : unrelated-j, ;
2091 added : unrelated-j, ;
2094 ##### revision "k-1" #####
2092 ##### revision "k-1" #####
2095 1 sidedata entries
2093 1 sidedata entries
2096 entry-0014 size 24
2094 entry-0014 size 24
2097 '\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'
2098 added : unrelated-k, ;
2096 added : unrelated-k, ;
2099 ##### revision "mAE,Km" #####
2097 ##### revision "mAE,Km" #####
2100 1 sidedata entries
2098 1 sidedata entries
2101 entry-0014 size 4
2099 entry-0014 size 4
2102 '\x00\x00\x00\x00'
2100 '\x00\x00\x00\x00'
2103 ##### revision "mK,AEm" #####
2101 ##### revision "mK,AEm" #####
2104 1 sidedata entries
2102 1 sidedata entries
2105 entry-0014 size 4
2103 entry-0014 size 4
2106 '\x00\x00\x00\x00'
2104 '\x00\x00\x00\x00'
2107 ##### revision "mEA,Jm" #####
2105 ##### revision "mEA,Jm" #####
2108 1 sidedata entries
2106 1 sidedata entries
2109 entry-0014 size 24
2107 entry-0014 size 24
2110 '\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'
2111 touched : unrelated-j, ;
2109 touched : unrelated-j, ;
2112 ##### revision "mJ,EAm" #####
2110 ##### revision "mJ,EAm" #####
2113 1 sidedata entries
2111 1 sidedata entries
2114 entry-0014 size 24
2112 entry-0014 size 24
2115 '\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'
2116 touched : unrelated-j, ;
2114 touched : unrelated-j, ;
2117 ##### revision "s-1" #####
2115 ##### revision "s-1" #####
2118 1 sidedata entries
2116 1 sidedata entries
2119 entry-0014 size 24
2117 entry-0014 size 24
2120 '\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'
2121 added : unrelated-s, ;
2119 added : unrelated-s, ;
2122 ##### revision "t-1" #####
2120 ##### revision "t-1" #####
2123 1 sidedata entries
2121 1 sidedata entries
2124 entry-0014 size 24
2122 entry-0014 size 24
2125 '\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'
2126 added : unrelated-t, ;
2124 added : unrelated-t, ;
2127 ##### revision "mPQ,Tm" #####
2125 ##### revision "mPQ,Tm" #####
2128 1 sidedata entries
2126 1 sidedata entries
2129 entry-0014 size 4
2127 entry-0014 size 4
2130 '\x00\x00\x00\x00'
2128 '\x00\x00\x00\x00'
2131 ##### revision "mT,PQm" #####
2129 ##### revision "mT,PQm" #####
2132 1 sidedata entries
2130 1 sidedata entries
2133 entry-0014 size 4
2131 entry-0014 size 4
2134 '\x00\x00\x00\x00'
2132 '\x00\x00\x00\x00'
2135 ##### revision "mQP,Sm" #####
2133 ##### revision "mQP,Sm" #####
2136 1 sidedata entries
2134 1 sidedata entries
2137 entry-0014 size 4
2135 entry-0014 size 4
2138 '\x00\x00\x00\x00'
2136 '\x00\x00\x00\x00'
2139 ##### revision "mS,QPm" #####
2137 ##### revision "mS,QPm" #####
2140 1 sidedata entries
2138 1 sidedata entries
2141 entry-0014 size 4
2139 entry-0014 size 4
2142 '\x00\x00\x00\x00'
2140 '\x00\x00\x00\x00'
2143 ##### revision "l-1" #####
2141 ##### revision "l-1" #####
2144 1 sidedata entries
2142 1 sidedata entries
2145 entry-0014 size 24
2143 entry-0014 size 24
2146 '\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'
2147 added : unrelated-l, ;
2145 added : unrelated-l, ;
2148 ##### revision "mBC+revert,Lm" #####
2146 ##### revision "mBC+revert,Lm" #####
2149 1 sidedata entries
2147 1 sidedata entries
2150 entry-0014 size 4
2148 entry-0014 size 4
2151 '\x00\x00\x00\x00'
2149 '\x00\x00\x00\x00'
2152 ##### revision "mCB+revert,Lm" #####
2150 ##### revision "mCB+revert,Lm" #####
2153 1 sidedata entries
2151 1 sidedata entries
2154 entry-0014 size 4
2152 entry-0014 size 4
2155 '\x00\x00\x00\x00'
2153 '\x00\x00\x00\x00'
2156 ##### revision "mL,BC+revertm" #####
2154 ##### revision "mL,BC+revertm" #####
2157 1 sidedata entries
2155 1 sidedata entries
2158 entry-0014 size 4
2156 entry-0014 size 4
2159 '\x00\x00\x00\x00'
2157 '\x00\x00\x00\x00'
2160 ##### revision "mL,CB+revertm" #####
2158 ##### revision "mL,CB+revertm" #####
2161 1 sidedata entries
2159 1 sidedata entries
2162 entry-0014 size 4
2160 entry-0014 size 4
2163 '\x00\x00\x00\x00'
2161 '\x00\x00\x00\x00'
2164 ##### revision "n-1" #####
2162 ##### revision "n-1" #####
2165 1 sidedata entries
2163 1 sidedata entries
2166 entry-0014 size 24
2164 entry-0014 size 24
2167 '\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'
2168 added : unrelated-n, ;
2166 added : unrelated-n, ;
2169 ##### revision "o-1" #####
2167 ##### revision "o-1" #####
2170 1 sidedata entries
2168 1 sidedata entries
2171 entry-0014 size 24
2169 entry-0014 size 24
2172 '\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'
2173 added : unrelated-o, ;
2171 added : unrelated-o, ;
2174 ##### revision "mFG,Om" #####
2172 ##### revision "mFG,Om" #####
2175 1 sidedata entries
2173 1 sidedata entries
2176 entry-0014 size 4
2174 entry-0014 size 4
2177 '\x00\x00\x00\x00'
2175 '\x00\x00\x00\x00'
2178 ##### revision "mO,FGm" #####
2176 ##### revision "mO,FGm" #####
2179 1 sidedata entries
2177 1 sidedata entries
2180 entry-0014 size 4
2178 entry-0014 size 4
2181 '\x00\x00\x00\x00'
2179 '\x00\x00\x00\x00'
2182 ##### revision "mGF,Nm" #####
2180 ##### revision "mGF,Nm" #####
2183 1 sidedata entries
2181 1 sidedata entries
2184 entry-0014 size 4
2182 entry-0014 size 4
2185 '\x00\x00\x00\x00'
2183 '\x00\x00\x00\x00'
2186 ##### revision "mN,GFm" #####
2184 ##### revision "mN,GFm" #####
2187 1 sidedata entries
2185 1 sidedata entries
2188 entry-0014 size 4
2186 entry-0014 size 4
2189 '\x00\x00\x00\x00'
2187 '\x00\x00\x00\x00'
2190 ##### revision "mAE-change,Km" #####
2188 ##### revision "mAE-change,Km" #####
2191 1 sidedata entries
2189 1 sidedata entries
2192 entry-0014 size 4
2190 entry-0014 size 4
2193 '\x00\x00\x00\x00'
2191 '\x00\x00\x00\x00'
2194 ##### revision "mK,AE-change-m" #####
2192 ##### revision "mK,AE-change-m" #####
2195 1 sidedata entries
2193 1 sidedata entries
2196 entry-0014 size 4
2194 entry-0014 size 4
2197 '\x00\x00\x00\x00'
2195 '\x00\x00\x00\x00'
2198 ##### revision "mEA-change,Jm" #####
2196 ##### revision "mEA-change,Jm" #####
2199 1 sidedata entries
2197 1 sidedata entries
2200 entry-0014 size 4
2198 entry-0014 size 4
2201 '\x00\x00\x00\x00'
2199 '\x00\x00\x00\x00'
2202 ##### revision "mJ,EA-change-m" #####
2200 ##### revision "mJ,EA-change-m" #####
2203 1 sidedata entries
2201 1 sidedata entries
2204 entry-0014 size 4
2202 entry-0014 size 4
2205 '\x00\x00\x00\x00'
2203 '\x00\x00\x00\x00'
2206
2204
2207 #endif
2205 #endif
2208
2206
2209
2207
2210 Test copy information chaining
2208 Test copy information chaining
2211 ==============================
2209 ==============================
2212
2210
2213 Check that matching only affect the destination and not intermediate path
2211 Check that matching only affect the destination and not intermediate path
2214 -------------------------------------------------------------------------
2212 -------------------------------------------------------------------------
2215
2213
2216 The two status call should give the same value for f
2214 The two status call should give the same value for f
2217
2215
2218 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("a-2")'
2216 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("a-2")'
2219 A f
2217 A f
2220 a
2218 a
2221 A t
2219 A t
2222 p
2220 p
2223 R a
2221 R a
2224 R p
2222 R p
2225 $ 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
2226 A f
2224 A f
2227 a (no-changeset no-compatibility !)
2225 a (no-changeset no-compatibility !)
2228
2226
2229 merging with unrelated change does not interfere with the renames
2227 merging with unrelated change does not interfere with the renames
2230 ---------------------------------------------------------------
2228 ---------------------------------------------------------------
2231
2229
2232 - rename on one side
2230 - rename on one side
2233 - unrelated change on the other side
2231 - unrelated change on the other side
2234
2232
2235 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
2233 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
2236 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
2237 |\
2235 |\
2238 +---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
2239 | |/
2237 | |/
2240 | o b-1: b update
2238 | o b-1: b update
2241 | |
2239 | |
2242 o | a-2: e -move-> f
2240 o | a-2: e -move-> f
2243 | |
2241 | |
2244 o | a-1: d -move-> e
2242 o | a-1: d -move-> e
2245 |/
2243 |/
2246 o i-2: c -move-> d, s -move-> t
2244 o i-2: c -move-> d, s -move-> t
2247 |
2245 |
2248 o i-1: a -move-> c, p -move-> s
2246 o i-1: a -move-> c, p -move-> s
2249 |
2247 |
2250 o i-0 initial commit: a b h p q r
2248 o i-0 initial commit: a b h p q r
2251
2249
2252
2250
2253 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mABm")'
2251 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mABm")'
2254 A f
2252 A f
2255 d
2253 d
2256 R d
2254 R d
2257 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBAm")'
2255 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBAm")'
2258 A f
2256 A f
2259 d
2257 d
2260 R d
2258 R d
2261 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mABm")'
2259 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mABm")'
2262 M b
2260 M b
2263 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mBAm")'
2261 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mBAm")'
2264 M b
2262 M b
2265 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mABm")'
2263 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mABm")'
2266 M b
2264 M b
2267 A f
2265 A f
2268 d
2266 d
2269 R d
2267 R d
2270 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBAm")'
2268 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBAm")'
2271 M b
2269 M b
2272 A f
2270 A f
2273 d
2271 d
2274 R d
2272 R d
2275 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mABm")'
2273 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mABm")'
2276 M b
2274 M b
2277 A f
2275 A f
2278 a
2276 a
2279 A t
2277 A t
2280 p
2278 p
2281 R a
2279 R a
2282 R p
2280 R p
2283 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBAm")'
2281 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBAm")'
2284 M b
2282 M b
2285 A f
2283 A f
2286 a
2284 a
2287 A t
2285 A t
2288 p
2286 p
2289 R a
2287 R a
2290 R p
2288 R p
2291
2289
2292 merging with the side having a delete
2290 merging with the side having a delete
2293 -------------------------------------
2291 -------------------------------------
2294
2292
2295 case summary:
2293 case summary:
2296 - one with change to an unrelated file
2294 - one with change to an unrelated file
2297 - one deleting the change
2295 - one deleting the change
2298 and recreate an unrelated file after the merge
2296 and recreate an unrelated file after the merge
2299
2297
2300 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
2298 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
2301 o mCBm-1 re-add d
2299 o mCBm-1 re-add d
2302 |
2300 |
2303 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
2304 |\
2302 |\
2305 | | o mBCm-1 re-add d
2303 | | o mBCm-1 re-add d
2306 | | |
2304 | | |
2307 +---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
2308 | |/
2306 | |/
2309 | o c-1 delete d
2307 | o c-1 delete d
2310 | |
2308 | |
2311 o | b-1: b update
2309 o | b-1: b update
2312 |/
2310 |/
2313 o i-2: c -move-> d, s -move-> t
2311 o i-2: c -move-> d, s -move-> t
2314 |
2312 |
2315 o i-1: a -move-> c, p -move-> s
2313 o i-1: a -move-> c, p -move-> s
2316 |
2314 |
2317 o i-0 initial commit: a b h p q r
2315 o i-0 initial commit: a b h p q r
2318
2316
2319 - comparing from the merge
2317 - comparing from the merge
2320
2318
2321 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-0")'
2319 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-0")'
2322 R d
2320 R d
2323 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-0")'
2321 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-0")'
2324 R d
2322 R d
2325 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-0")'
2323 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-0")'
2326 M b
2324 M b
2327 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-0")'
2325 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-0")'
2328 M b
2326 M b
2329 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-0")'
2327 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-0")'
2330 M b
2328 M b
2331 R d
2329 R d
2332 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-0")'
2330 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-0")'
2333 M b
2331 M b
2334 R d
2332 R d
2335 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-0")'
2333 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-0")'
2336 M b
2334 M b
2337 A t
2335 A t
2338 p
2336 p
2339 R a
2337 R a
2340 R p
2338 R p
2341 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-0")'
2339 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-0")'
2342 M b
2340 M b
2343 A t
2341 A t
2344 p
2342 p
2345 R a
2343 R a
2346 R p
2344 R p
2347
2345
2348 - comparing with the merge children re-adding the file
2346 - comparing with the merge children re-adding the file
2349
2347
2350 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-1")'
2348 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-1")'
2351 M d
2349 M d
2352 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-1")'
2350 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-1")'
2353 M d
2351 M d
2354 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-1")'
2352 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-1")'
2355 M b
2353 M b
2356 A d
2354 A d
2357 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-1")'
2355 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-1")'
2358 M b
2356 M b
2359 A d
2357 A d
2360 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-1")'
2358 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-1")'
2361 M b
2359 M b
2362 M d
2360 M d
2363 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-1")'
2361 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-1")'
2364 M b
2362 M b
2365 M d
2363 M d
2366 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-1")'
2364 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-1")'
2367 M b
2365 M b
2368 A d
2366 A d
2369 A t
2367 A t
2370 p
2368 p
2371 R a
2369 R a
2372 R p
2370 R p
2373 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-1")'
2371 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-1")'
2374 M b
2372 M b
2375 A d
2373 A d
2376 A t
2374 A t
2377 p
2375 p
2378 R a
2376 R a
2379 R p
2377 R p
2380
2378
2381 Comparing with a merge re-adding the file afterward
2379 Comparing with a merge re-adding the file afterward
2382 ---------------------------------------------------
2380 ---------------------------------------------------
2383
2381
2384 Merge:
2382 Merge:
2385 - one with change to an unrelated file
2383 - one with change to an unrelated file
2386 - one deleting and recreating the change
2384 - one deleting and recreating the change
2387
2385
2388 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
2386 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
2389 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
2390 |\
2388 |\
2391 +---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
2392 | |/
2390 | |/
2393 | o d-2 re-add d
2391 | o d-2 re-add d
2394 | |
2392 | |
2395 | o d-1 delete d
2393 | o d-1 delete d
2396 | |
2394 | |
2397 o | b-1: b update
2395 o | b-1: b update
2398 |/
2396 |/
2399 o i-2: c -move-> d, s -move-> t
2397 o i-2: c -move-> d, s -move-> t
2400 |
2398 |
2401 o i-1: a -move-> c, p -move-> s
2399 o i-1: a -move-> c, p -move-> s
2402 |
2400 |
2403 o i-0 initial commit: a b h p q r
2401 o i-0 initial commit: a b h p q r
2404
2402
2405 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBDm-0")'
2403 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBDm-0")'
2406 M d
2404 M d
2407 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mDBm-0")'
2405 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mDBm-0")'
2408 M d
2406 M d
2409 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mBDm-0")'
2407 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mBDm-0")'
2410 M b
2408 M b
2411 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDBm-0")'
2409 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDBm-0")'
2412 M b
2410 M b
2413 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBDm-0")'
2411 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBDm-0")'
2414 M b
2412 M b
2415 M d
2413 M d
2416 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mDBm-0")'
2414 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mDBm-0")'
2417 M b
2415 M b
2418 M d
2416 M d
2419
2417
2420 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
2421
2419
2422 $ hg manifest --debug --rev 'desc("mBDm-0")' | grep '644 d'
2420 $ hg manifest --debug --rev 'desc("mBDm-0")' | grep '644 d'
2423 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2421 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2424 $ hg manifest --debug --rev 'desc("mDBm-0")' | grep '644 d'
2422 $ hg manifest --debug --rev 'desc("mDBm-0")' | grep '644 d'
2425 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2423 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2426
2424
2427 $ hg manifest --debug --rev 'desc("d-2")' | grep '644 d'
2425 $ hg manifest --debug --rev 'desc("d-2")' | grep '644 d'
2428 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2426 b004912a8510032a0350a74daa2803dadfb00e12 644 d
2429 $ hg manifest --debug --rev 'desc("b-1")' | grep '644 d'
2427 $ hg manifest --debug --rev 'desc("b-1")' | grep '644 d'
2430 d8252ab2e760b0d4e5288fd44cbd15a0fa567e16 644 d (no-changeset !)
2428 d8252ab2e760b0d4e5288fd44cbd15a0fa567e16 644 d (no-changeset !)
2431 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 d (changeset !)
2429 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 d (changeset !)
2432 $ hg debugindex d | head -n 4 | "$PYTHON" ../no-linkrev
2430 $ hg debugindex d | head -n 4 | "$PYTHON" ../no-linkrev
2433 rev linkrev nodeid p1 p2
2431 rev linkrev nodeid p1 p2
2434 0 * d8252ab2e760 000000000000 000000000000 (no-changeset !)
2432 0 * d8252ab2e760 000000000000 000000000000 (no-changeset !)
2435 0 * ae258f702dfe 000000000000 000000000000 (changeset !)
2433 0 * ae258f702dfe 000000000000 000000000000 (changeset !)
2436 1 * b004912a8510 000000000000 000000000000
2434 1 * b004912a8510 000000000000 000000000000
2437 2 * 7b79e2fe0c89 000000000000 000000000000 (no-changeset !)
2435 2 * 7b79e2fe0c89 000000000000 000000000000 (no-changeset !)
2438 2 * 5cce88bf349f ae258f702dfe 000000000000 (changeset !)
2436 2 * 5cce88bf349f ae258f702dfe 000000000000 (changeset !)
2439
2437
2440 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
2441
2439
2442 $ hg log -Gfr 'desc("mBDm-0")' d
2440 $ hg log -Gfr 'desc("mBDm-0")' d
2443 o d-2 re-add d
2441 o d-2 re-add d
2444 |
2442 |
2445 ~
2443 ~
2446
2444
2447 $ hg log -Gfr 'desc("mDBm-0")' d
2445 $ hg log -Gfr 'desc("mDBm-0")' d
2448 o d-2 re-add d
2446 o d-2 re-add d
2449 |
2447 |
2450 ~
2448 ~
2451
2449
2452 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBDm-0")'
2450 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBDm-0")'
2453 M b
2451 M b
2454 A d
2452 A d
2455 A t
2453 A t
2456 p
2454 p
2457 R a
2455 R a
2458 R p
2456 R p
2459 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDBm-0")'
2457 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDBm-0")'
2460 M b
2458 M b
2461 A d
2459 A d
2462 A t
2460 A t
2463 p
2461 p
2464 R a
2462 R a
2465 R p
2463 R p
2466
2464
2467
2465
2468 Comparing with a merge with colliding rename
2466 Comparing with a merge with colliding rename
2469 --------------------------------------------
2467 --------------------------------------------
2470
2468
2471 Subcase: new copy information on both side
2469 Subcase: new copy information on both side
2472 ``````````````````````````````````````````
2470 ``````````````````````````````````````````
2473
2471
2474 - the "e-" branch renaming b to f (through 'g')
2472 - the "e-" branch renaming b to f (through 'g')
2475 - the "a-" branch renaming d to f (through e)
2473 - the "a-" branch renaming d to f (through e)
2476
2474
2477 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
2475 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
2478 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
2479 |\
2477 |\
2480 +---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
2481 | |/
2479 | |/
2482 | o e-2 g -move-> f
2480 | o e-2 g -move-> f
2483 | |
2481 | |
2484 | o e-1 b -move-> g
2482 | o e-1 b -move-> g
2485 | |
2483 | |
2486 o | a-2: e -move-> f
2484 o | a-2: e -move-> f
2487 | |
2485 | |
2488 o | a-1: d -move-> e
2486 o | a-1: d -move-> e
2489 |/
2487 |/
2490 o i-2: c -move-> d, s -move-> t
2488 o i-2: c -move-> d, s -move-> t
2491 |
2489 |
2492 o i-1: a -move-> c, p -move-> s
2490 o i-1: a -move-> c, p -move-> s
2493 |
2491 |
2494 o i-0 initial commit: a b h p q r
2492 o i-0 initial commit: a b h p q r
2495
2493
2496 #if no-changeset
2494 #if no-changeset
2497 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
2495 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
2498 2ff93c643948464ee1f871867910ae43a45b0bea 644 f
2496 2ff93c643948464ee1f871867910ae43a45b0bea 644 f
2499 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
2497 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
2500 2ff93c643948464ee1f871867910ae43a45b0bea 644 f
2498 2ff93c643948464ee1f871867910ae43a45b0bea 644 f
2501 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
2499 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
2502 b76eb76580df486c3d51d63c5c210d4dd43a8ac7 644 f
2500 b76eb76580df486c3d51d63c5c210d4dd43a8ac7 644 f
2503 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
2501 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
2504 e8825b386367b29fec957283a80bb47b47483fe1 644 f
2502 e8825b386367b29fec957283a80bb47b47483fe1 644 f
2505 $ hg debugindex f | "$PYTHON" ../no-linkrev
2503 $ hg debugindex f | "$PYTHON" ../no-linkrev
2506 rev linkrev nodeid p1 p2
2504 rev linkrev nodeid p1 p2
2507 0 * b76eb76580df 000000000000 000000000000
2505 0 * b76eb76580df 000000000000 000000000000
2508 1 * e8825b386367 000000000000 000000000000
2506 1 * e8825b386367 000000000000 000000000000
2509 2 * 2ff93c643948 b76eb76580df e8825b386367
2507 2 * 2ff93c643948 b76eb76580df e8825b386367
2510 3 * 2f649fba7eb2 b76eb76580df e8825b386367
2508 3 * 2f649fba7eb2 b76eb76580df e8825b386367
2511 4 * 774e7c1637d5 e8825b386367 b76eb76580df
2509 4 * 774e7c1637d5 e8825b386367 b76eb76580df
2512 #else
2510 #else
2513 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
2511 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
2514 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2512 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2515 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
2513 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
2516 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2514 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2517 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
2515 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
2518 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2516 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2519 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
2517 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
2520 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2518 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
2521 $ hg debugindex f | "$PYTHON" ../no-linkrev
2519 $ hg debugindex f | "$PYTHON" ../no-linkrev
2522 rev linkrev nodeid p1 p2
2520 rev linkrev nodeid p1 p2
2523 0 * ae258f702dfe 000000000000 000000000000
2521 0 * ae258f702dfe 000000000000 000000000000
2524 1 * d3613c1ec831 ae258f702dfe 000000000000
2522 1 * d3613c1ec831 ae258f702dfe 000000000000
2525 2 * 05e03c868bbc ae258f702dfe 000000000000
2523 2 * 05e03c868bbc ae258f702dfe 000000000000
2526 #endif
2524 #endif
2527
2525
2528 # Here the filelog based implementation is not looking at the rename
2526 # Here the filelog based implementation is not looking at the rename
2529 # information (because the file exist on both side). However the changelog
2527 # information (because the file exist on both side). However the changelog
2530 # based on works fine. We have different output.
2528 # based on works fine. We have different output.
2531
2529
2532 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAEm-0")'
2530 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAEm-0")'
2533 M f (no-changeset !)
2531 M f (no-changeset !)
2534 b (no-filelog no-changeset !)
2532 b (no-filelog no-changeset !)
2535 R b
2533 R b
2536 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEAm-0")'
2534 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEAm-0")'
2537 M f (no-changeset !)
2535 M f (no-changeset !)
2538 b (no-filelog no-changeset !)
2536 b (no-filelog no-changeset !)
2539 R b
2537 R b
2540 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAEm-0")'
2538 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAEm-0")'
2541 M f (no-changeset !)
2539 M f (no-changeset !)
2542 d (no-filelog no-changeset !)
2540 d (no-filelog no-changeset !)
2543 R d
2541 R d
2544 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEAm-0")'
2542 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEAm-0")'
2545 M f (no-changeset !)
2543 M f (no-changeset !)
2546 d (no-filelog no-changeset !)
2544 d (no-filelog no-changeset !)
2547 R d
2545 R d
2548 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")'
2546 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")'
2549 A f
2547 A f
2550 d
2548 d
2551 R d
2549 R d
2552 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("e-2")'
2550 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("e-2")'
2553 A f
2551 A f
2554 b
2552 b
2555 R b
2553 R b
2556
2554
2557 # 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.
2558 #
2556 #
2559 # The filelog based implementation picks an arbitrary side based on revision
2557 # The filelog based implementation picks an arbitrary side based on revision
2560 # 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
2561 # 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
2562 # different from one repository to the next.
2560 # different from one repository to the next.
2563 #
2561 #
2564 # 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
2565 # 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.
2566 # That order is stable accross repositories. (data from p1 prevails)
2564 # That order is stable accross repositories. (data from p1 prevails)
2567
2565
2568 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAEm-0")'
2566 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAEm-0")'
2569 A f
2567 A f
2570 d
2568 d
2571 R b
2569 R b
2572 R d
2570 R d
2573 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEAm-0")'
2571 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEAm-0")'
2574 A f
2572 A f
2575 d (filelog !)
2573 d (filelog !)
2576 b (no-filelog !)
2574 b (no-filelog !)
2577 R b
2575 R b
2578 R d
2576 R d
2579 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm-0")'
2577 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm-0")'
2580 A f
2578 A f
2581 a
2579 a
2582 A t
2580 A t
2583 p
2581 p
2584 R a
2582 R a
2585 R b
2583 R b
2586 R p
2584 R p
2587 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm-0")'
2585 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm-0")'
2588 A f
2586 A f
2589 a (filelog !)
2587 a (filelog !)
2590 b (no-filelog !)
2588 b (no-filelog !)
2591 A t
2589 A t
2592 p
2590 p
2593 R a
2591 R a
2594 R b
2592 R b
2595 R p
2593 R p
2596
2594
2597
2595
2598 Subcase: existing copy information overwritten on one branch
2596 Subcase: existing copy information overwritten on one branch
2599 ````````````````````````````````````````````````````````````
2597 ````````````````````````````````````````````````````````````
2600
2598
2601 Note:
2599 Note:
2602 | 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.
2603 | This lead to bad copy tracing information to be dug up.
2601 | This lead to bad copy tracing information to be dug up.
2604
2602
2605
2603
2606 Merge:
2604 Merge:
2607 - one with change to an unrelated file (b)
2605 - one with change to an unrelated file (b)
2608 - 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)
2609
2607
2610 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
2608 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
2611 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
2612 |\
2610 |\
2613 +---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
2614 | |/
2612 | |/
2615 | o f-2: rename i -> d
2613 | o f-2: rename i -> d
2616 | |
2614 | |
2617 | o f-1: rename h -> i
2615 | o f-1: rename h -> i
2618 | |
2616 | |
2619 o | b-1: b update
2617 o | b-1: b update
2620 |/
2618 |/
2621 o i-2: c -move-> d, s -move-> t
2619 o i-2: c -move-> d, s -move-> t
2622 |
2620 |
2623 o i-1: a -move-> c, p -move-> s
2621 o i-1: a -move-> c, p -move-> s
2624 |
2622 |
2625 o i-0 initial commit: a b h p q r
2623 o i-0 initial commit: a b h p q r
2626
2624
2627 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBFm-0")'
2625 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBFm-0")'
2628 M b
2626 M b
2629 A d
2627 A d
2630 h
2628 h
2631 A t
2629 A t
2632 p
2630 p
2633 R a
2631 R a
2634 R h
2632 R h
2635 R p
2633 R p
2636 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFBm-0")'
2634 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFBm-0")'
2637 M b
2635 M b
2638 A d
2636 A d
2639 h
2637 h
2640 A t
2638 A t
2641 p
2639 p
2642 R a
2640 R a
2643 R h
2641 R h
2644 R p
2642 R p
2645 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBFm-0")'
2643 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBFm-0")'
2646 M d (no-changeset !)
2644 M d (no-changeset !)
2647 h (no-filelog no-changeset !)
2645 h (no-filelog no-changeset !)
2648 R h
2646 R h
2649 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mBFm-0")'
2647 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mBFm-0")'
2650 M b
2648 M b
2651 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mBFm-0")'
2649 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mBFm-0")'
2652 M b
2650 M b
2653 M d (no-changeset !)
2651 M d (no-changeset !)
2654 i (no-filelog no-changeset !)
2652 i (no-filelog no-changeset !)
2655 R i
2653 R i
2656 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mFBm-0")'
2654 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mFBm-0")'
2657 M d (no-changeset !)
2655 M d (no-changeset !)
2658 h (no-filelog no-changeset !)
2656 h (no-filelog no-changeset !)
2659 R h
2657 R h
2660 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFBm-0")'
2658 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFBm-0")'
2661 M b
2659 M b
2662 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFBm-0")'
2660 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFBm-0")'
2663 M b
2661 M b
2664 M d (no-changeset !)
2662 M d (no-changeset !)
2665 i (no-filelog no-changeset !)
2663 i (no-filelog no-changeset !)
2666 R i
2664 R i
2667
2665
2668 #if no-changeset
2666 #if no-changeset
2669 $ hg log -Gfr 'desc("mBFm-0")' d
2667 $ hg log -Gfr 'desc("mBFm-0")' d
2670 o f-2: rename i -> d
2668 o f-2: rename i -> d
2671 |
2669 |
2672 o f-1: rename h -> i
2670 o f-1: rename h -> i
2673 :
2671 :
2674 o i-0 initial commit: a b h p q r
2672 o i-0 initial commit: a b h p q r
2675
2673
2676 #else
2674 #else
2677 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2675 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2678 $ hg log -Gfr 'desc("mBFm-0")' d
2676 $ hg log -Gfr 'desc("mBFm-0")' d
2679 o i-2: c -move-> d, s -move-> t
2677 o i-2: c -move-> d, s -move-> t
2680 |
2678 |
2681 ~
2679 ~
2682 #endif
2680 #endif
2683
2681
2684 #if no-changeset
2682 #if no-changeset
2685 $ hg log -Gfr 'desc("mFBm-0")' d
2683 $ hg log -Gfr 'desc("mFBm-0")' d
2686 o f-2: rename i -> d
2684 o f-2: rename i -> d
2687 |
2685 |
2688 o f-1: rename h -> i
2686 o f-1: rename h -> i
2689 :
2687 :
2690 o i-0 initial commit: a b h p q r
2688 o i-0 initial commit: a b h p q r
2691
2689
2692 #else
2690 #else
2693 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2691 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2694 $ hg log -Gfr 'desc("mFBm-0")' d
2692 $ hg log -Gfr 'desc("mFBm-0")' d
2695 o i-2: c -move-> d, s -move-> t
2693 o i-2: c -move-> d, s -move-> t
2696 |
2694 |
2697 ~
2695 ~
2698 #endif
2696 #endif
2699
2697
2700
2698
2701 Subcase: existing copy information overwritten on one branch, with different content)
2699 Subcase: existing copy information overwritten on one branch, with different content)
2702 `````````````````````````````````````````````````````````````````````````````````````
2700 `````````````````````````````````````````````````````````````````````````````````````
2703
2701
2704 Merge:
2702 Merge:
2705 - one with change to an unrelated file (b)
2703 - one with change to an unrelated file (b)
2706 - 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
2707
2705
2708 $ hg log -G --rev '::(desc("mBRm")+desc("mRBm"))'
2706 $ hg log -G --rev '::(desc("mBRm")+desc("mRBm"))'
2709 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
2710 |\
2708 |\
2711 +---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
2712 | |/
2710 | |/
2713 | o r-2: rename t -> x
2711 | o r-2: rename t -> x
2714 | |
2712 | |
2715 | o r-1: rename r -> x
2713 | o r-1: rename r -> x
2716 | |
2714 | |
2717 o | b-1: b update
2715 o | b-1: b update
2718 |/
2716 |/
2719 o i-2: c -move-> d, s -move-> t
2717 o i-2: c -move-> d, s -move-> t
2720 |
2718 |
2721 o i-1: a -move-> c, p -move-> s
2719 o i-1: a -move-> c, p -move-> s
2722 |
2720 |
2723 o i-0 initial commit: a b h p q r
2721 o i-0 initial commit: a b h p q r
2724
2722
2725 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBRm-0")'
2723 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBRm-0")'
2726 M b
2724 M b
2727 A d
2725 A d
2728 a
2726 a
2729 A t
2727 A t
2730 r
2728 r
2731 R a
2729 R a
2732 R p
2730 R p
2733 R r
2731 R r
2734 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mRBm-0")'
2732 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mRBm-0")'
2735 M b
2733 M b
2736 A d
2734 A d
2737 a
2735 a
2738 A t
2736 A t
2739 r
2737 r
2740 R a
2738 R a
2741 R p
2739 R p
2742 R r
2740 R r
2743 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBRm-0")'
2741 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBRm-0")'
2744 M t
2742 M t
2745 r (no-filelog !)
2743 r (no-filelog !)
2746 R r
2744 R r
2747 $ hg status --copies --rev 'desc("r-2")' --rev 'desc("mBRm-0")'
2745 $ hg status --copies --rev 'desc("r-2")' --rev 'desc("mBRm-0")'
2748 M b
2746 M b
2749 $ hg status --copies --rev 'desc("r-1")' --rev 'desc("mBRm-0")'
2747 $ hg status --copies --rev 'desc("r-1")' --rev 'desc("mBRm-0")'
2750 M b
2748 M b
2751 M t
2749 M t
2752 x (no-filelog !)
2750 x (no-filelog !)
2753 R x
2751 R x
2754 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mRBm-0")'
2752 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mRBm-0")'
2755 M t
2753 M t
2756 r (no-filelog !)
2754 r (no-filelog !)
2757 R r
2755 R r
2758 $ hg status --copies --rev 'desc("r-2")' --rev 'desc("mRBm-0")'
2756 $ hg status --copies --rev 'desc("r-2")' --rev 'desc("mRBm-0")'
2759 M b
2757 M b
2760 $ hg status --copies --rev 'desc("r-1")' --rev 'desc("mRBm-0")'
2758 $ hg status --copies --rev 'desc("r-1")' --rev 'desc("mRBm-0")'
2761 M b
2759 M b
2762 M t
2760 M t
2763 x (no-filelog !)
2761 x (no-filelog !)
2764 R x
2762 R x
2765
2763
2766 #if no-changeset
2764 #if no-changeset
2767 $ hg log -Gfr 'desc("mBRm-0")' d
2765 $ hg log -Gfr 'desc("mBRm-0")' d
2768 o i-2: c -move-> d, s -move-> t
2766 o i-2: c -move-> d, s -move-> t
2769 |
2767 |
2770 o i-1: a -move-> c, p -move-> s
2768 o i-1: a -move-> c, p -move-> s
2771 |
2769 |
2772 o i-0 initial commit: a b h p q r
2770 o i-0 initial commit: a b h p q r
2773
2771
2774 #else
2772 #else
2775 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2773 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2776 $ hg log -Gfr 'desc("mBRm-0")' d
2774 $ hg log -Gfr 'desc("mBRm-0")' d
2777 o i-2: c -move-> d, s -move-> t
2775 o i-2: c -move-> d, s -move-> t
2778 |
2776 |
2779 ~
2777 ~
2780 #endif
2778 #endif
2781
2779
2782 #if no-changeset
2780 #if no-changeset
2783 $ hg log -Gfr 'desc("mRBm-0")' d
2781 $ hg log -Gfr 'desc("mRBm-0")' d
2784 o i-2: c -move-> d, s -move-> t
2782 o i-2: c -move-> d, s -move-> t
2785 |
2783 |
2786 o i-1: a -move-> c, p -move-> s
2784 o i-1: a -move-> c, p -move-> s
2787 |
2785 |
2788 o i-0 initial commit: a b h p q r
2786 o i-0 initial commit: a b h p q r
2789
2787
2790 #else
2788 #else
2791 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2789 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2792 $ hg log -Gfr 'desc("mRBm-0")' d
2790 $ hg log -Gfr 'desc("mRBm-0")' d
2793 o i-2: c -move-> d, s -move-> t
2791 o i-2: c -move-> d, s -move-> t
2794 |
2792 |
2795 ~
2793 ~
2796 #endif
2794 #endif
2797
2795
2798 Subcase: reset of the copy history on one side
2796 Subcase: reset of the copy history on one side
2799 ``````````````````````````````````````````````
2797 ``````````````````````````````````````````````
2800
2798
2801 Merge:
2799 Merge:
2802 - one with change to a file
2800 - one with change to a file
2803 - one deleting and recreating the file
2801 - one deleting and recreating the file
2804
2802
2805 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
2806 consider history and rename on both branch of the merge.
2804 consider history and rename on both branch of the merge.
2807
2805
2808 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
2806 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
2809 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
2810 |\
2808 |\
2811 +---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
2812 | |/
2810 | |/
2813 | o g-1: update d
2811 | o g-1: update d
2814 | |
2812 | |
2815 o | d-2 re-add d
2813 o | d-2 re-add d
2816 | |
2814 | |
2817 o | d-1 delete d
2815 o | d-1 delete d
2818 |/
2816 |/
2819 o i-2: c -move-> d, s -move-> t
2817 o i-2: c -move-> d, s -move-> t
2820 |
2818 |
2821 o i-1: a -move-> c, p -move-> s
2819 o i-1: a -move-> c, p -move-> s
2822 |
2820 |
2823 o i-0 initial commit: a b h p q r
2821 o i-0 initial commit: a b h p q r
2824
2822
2825 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
2826 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".
2827
2825
2828 (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
2829 revision numbers)
2827 revision numbers)
2830
2828
2831 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDGm-0")'
2829 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDGm-0")'
2832 A d
2830 A d
2833 a (filelog !)
2831 a (filelog !)
2834 A t
2832 A t
2835 p
2833 p
2836 R a
2834 R a
2837 R p
2835 R p
2838 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGDm-0")'
2836 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGDm-0")'
2839 A d
2837 A d
2840 a
2838 a
2841 A t
2839 A t
2842 p
2840 p
2843 R a
2841 R a
2844 R p
2842 R p
2845 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDGm-0")'
2843 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDGm-0")'
2846 M d
2844 M d
2847 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mGDm-0")'
2845 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mGDm-0")'
2848 M d
2846 M d
2849 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mDGm-0")'
2847 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mDGm-0")'
2850 M d
2848 M d
2851 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGDm-0")'
2849 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGDm-0")'
2852 M d
2850 M d
2853
2851
2854 #if no-changeset
2852 #if no-changeset
2855 $ hg log -Gfr 'desc("mDGm-0")' d
2853 $ hg log -Gfr 'desc("mDGm-0")' d
2856 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
2857 |\
2855 |\
2858 | o g-1: update d
2856 | o g-1: update d
2859 | |
2857 | |
2860 o | d-2 re-add d
2858 o | d-2 re-add d
2861 |/
2859 |/
2862 o i-2: c -move-> d, s -move-> t
2860 o i-2: c -move-> d, s -move-> t
2863 |
2861 |
2864 o i-1: a -move-> c, p -move-> s
2862 o i-1: a -move-> c, p -move-> s
2865 |
2863 |
2866 o i-0 initial commit: a b h p q r
2864 o i-0 initial commit: a b h p q r
2867
2865
2868 #else
2866 #else
2869 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2867 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2870 $ hg log -Gfr 'desc("mDGm-0")' d
2868 $ hg log -Gfr 'desc("mDGm-0")' d
2871 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
2872 |\
2870 |\
2873 | o g-1: update d
2871 | o g-1: update d
2874 | |
2872 | |
2875 o | d-2 re-add d
2873 o | d-2 re-add d
2876 |/
2874 |/
2877 o i-2: c -move-> d, s -move-> t
2875 o i-2: c -move-> d, s -move-> t
2878 |
2876 |
2879 ~
2877 ~
2880 #endif
2878 #endif
2881
2879
2882
2880
2883 #if no-changeset
2881 #if no-changeset
2884 $ hg log -Gfr 'desc("mDGm-0")' d
2882 $ hg log -Gfr 'desc("mDGm-0")' d
2885 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
2886 |\
2884 |\
2887 | o g-1: update d
2885 | o g-1: update d
2888 | |
2886 | |
2889 o | d-2 re-add d
2887 o | d-2 re-add d
2890 |/
2888 |/
2891 o i-2: c -move-> d, s -move-> t
2889 o i-2: c -move-> d, s -move-> t
2892 |
2890 |
2893 o i-1: a -move-> c, p -move-> s
2891 o i-1: a -move-> c, p -move-> s
2894 |
2892 |
2895 o i-0 initial commit: a b h p q r
2893 o i-0 initial commit: a b h p q r
2896
2894
2897 #else
2895 #else
2898 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2896 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
2899 $ hg log -Gfr 'desc("mDGm-0")' d
2897 $ hg log -Gfr 'desc("mDGm-0")' d
2900 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
2901 |\
2899 |\
2902 | o g-1: update d
2900 | o g-1: update d
2903 | |
2901 | |
2904 o | d-2 re-add d
2902 o | d-2 re-add d
2905 |/
2903 |/
2906 o i-2: c -move-> d, s -move-> t
2904 o i-2: c -move-> d, s -move-> t
2907 |
2905 |
2908 ~
2906 ~
2909 #endif
2907 #endif
2910
2908
2911 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
2912 ````````````````````````````````````````````````````````````````````````````````````````````
2910 ````````````````````````````````````````````````````````````````````````````````````````````
2913
2911
2914 Merge:
2912 Merge:
2915 - one with change to a file (d)
2913 - one with change to a file (d)
2916 - 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)
2917
2915
2918 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
2919 history are relevant.
2917 history are relevant.
2920
2918
2921
2919
2922 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
2920 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
2923 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
2924 |\
2922 |\
2925 +---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
2926 | |/
2924 | |/
2927 | o g-1: update d
2925 | o g-1: update d
2928 | |
2926 | |
2929 o | f-2: rename i -> d
2927 o | f-2: rename i -> d
2930 | |
2928 | |
2931 o | f-1: rename h -> i
2929 o | f-1: rename h -> i
2932 |/
2930 |/
2933 o i-2: c -move-> d, s -move-> t
2931 o i-2: c -move-> d, s -move-> t
2934 |
2932 |
2935 o i-1: a -move-> c, p -move-> s
2933 o i-1: a -move-> c, p -move-> s
2936 |
2934 |
2937 o i-0 initial commit: a b h p q r
2935 o i-0 initial commit: a b h p q r
2938
2936
2939
2937
2940 Note:
2938 Note:
2941 | 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
2942 | "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".
2943 |
2941 |
2944 | 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:
2945
2943
2946 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"):
2947
2945
2948 Details on this hash ordering pick:
2946 Details on this hash ordering pick:
2949
2947
2950 $ hg manifest --debug 'desc("g-1")' | egrep 'd$'
2948 $ hg manifest --debug 'desc("g-1")' | egrep 'd$'
2951 17ec97e605773eb44a117d1136b3849bcdc1924f 644 d (no-changeset !)
2949 17ec97e605773eb44a117d1136b3849bcdc1924f 644 d (no-changeset !)
2952 5cce88bf349f7c742bb440f2c53f81db9c294279 644 d (changeset !)
2950 5cce88bf349f7c742bb440f2c53f81db9c294279 644 d (changeset !)
2953 $ 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
2954 A d
2952 A d
2955 a (no-changeset no-compatibility !)
2953 a (no-changeset no-compatibility !)
2956
2954
2957 $ hg manifest --debug 'desc("f-2")' | egrep 'd$'
2955 $ hg manifest --debug 'desc("f-2")' | egrep 'd$'
2958 7b79e2fe0c8924e0e598a82f048a7b024afa4d96 644 d (no-changeset !)
2956 7b79e2fe0c8924e0e598a82f048a7b024afa4d96 644 d (no-changeset !)
2959 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 d (changeset !)
2957 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 d (changeset !)
2960 $ 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
2961 A d
2959 A d
2962 h (no-changeset no-compatibility !)
2960 h (no-changeset no-compatibility !)
2963
2961
2964 Copy tracing data on the resulting merge:
2962 Copy tracing data on the resulting merge:
2965
2963
2966 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm-0")'
2964 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm-0")'
2967 A d
2965 A d
2968 h (no-filelog !)
2966 h (no-filelog !)
2969 a (filelog !)
2967 a (filelog !)
2970 A t
2968 A t
2971 p
2969 p
2972 R a
2970 R a
2973 R h
2971 R h
2974 R p
2972 R p
2975 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm-0")'
2973 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm-0")'
2976 A d
2974 A d
2977 a (no-changeset !)
2975 a (no-changeset !)
2978 h (changeset !)
2976 h (changeset !)
2979 A t
2977 A t
2980 p
2978 p
2981 R a
2979 R a
2982 R h
2980 R h
2983 R p
2981 R p
2984 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFGm-0")'
2982 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFGm-0")'
2985 M d
2983 M d
2986 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mGFm-0")'
2984 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mGFm-0")'
2987 M d
2985 M d
2988 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFGm-0")'
2986 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFGm-0")'
2989 M d
2987 M d
2990 i (no-filelog !)
2988 i (no-filelog !)
2991 R i
2989 R i
2992 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mGFm-0")'
2990 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mGFm-0")'
2993 M d
2991 M d
2994 i (no-filelog !)
2992 i (no-filelog !)
2995 R i
2993 R i
2996 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mFGm-0")'
2994 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mFGm-0")'
2997 M d (no-changeset !)
2995 M d (no-changeset !)
2998 h (no-filelog no-changeset !)
2996 h (no-filelog no-changeset !)
2999 R h
2997 R h
3000 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGFm-0")'
2998 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGFm-0")'
3001 M d (no-changeset !)
2999 M d (no-changeset !)
3002 h (no-filelog no-changeset !)
3000 h (no-filelog no-changeset !)
3003 R h
3001 R h
3004
3002
3005 #if no-changeset
3003 #if no-changeset
3006 $ hg log -Gfr 'desc("mFGm-0")' d
3004 $ hg log -Gfr 'desc("mFGm-0")' d
3007 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
3008 |\
3006 |\
3009 | o g-1: update d
3007 | o g-1: update d
3010 | |
3008 | |
3011 o | f-2: rename i -> d
3009 o | f-2: rename i -> d
3012 | |
3010 | |
3013 o | f-1: rename h -> i
3011 o | f-1: rename h -> i
3014 |/
3012 |/
3015 o i-2: c -move-> d, s -move-> t
3013 o i-2: c -move-> d, s -move-> t
3016 |
3014 |
3017 o i-1: a -move-> c, p -move-> s
3015 o i-1: a -move-> c, p -move-> s
3018 |
3016 |
3019 o i-0 initial commit: a b h p q r
3017 o i-0 initial commit: a b h p q r
3020
3018
3021 #else
3019 #else
3022 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3020 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3023 $ hg log -Gfr 'desc("mFGm-0")' d
3021 $ hg log -Gfr 'desc("mFGm-0")' d
3024 o g-1: update d
3022 o g-1: update d
3025 |
3023 |
3026 o i-2: c -move-> d, s -move-> t
3024 o i-2: c -move-> d, s -move-> t
3027 |
3025 |
3028 ~
3026 ~
3029 #endif
3027 #endif
3030
3028
3031 #if no-changeset
3029 #if no-changeset
3032 $ hg log -Gfr 'desc("mGFm-0")' d
3030 $ hg log -Gfr 'desc("mGFm-0")' d
3033 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
3034 |\
3032 |\
3035 | o g-1: update d
3033 | o g-1: update d
3036 | |
3034 | |
3037 o | f-2: rename i -> d
3035 o | f-2: rename i -> d
3038 | |
3036 | |
3039 o | f-1: rename h -> i
3037 o | f-1: rename h -> i
3040 |/
3038 |/
3041 o i-2: c -move-> d, s -move-> t
3039 o i-2: c -move-> d, s -move-> t
3042 |
3040 |
3043 o i-1: a -move-> c, p -move-> s
3041 o i-1: a -move-> c, p -move-> s
3044 |
3042 |
3045 o i-0 initial commit: a b h p q r
3043 o i-0 initial commit: a b h p q r
3046
3044
3047 #else
3045 #else
3048 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3046 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3049 $ hg log -Gfr 'desc("mGFm-0")' d
3047 $ hg log -Gfr 'desc("mGFm-0")' d
3050 o g-1: update d
3048 o g-1: update d
3051 |
3049 |
3052 o i-2: c -move-> d, s -move-> t
3050 o i-2: c -move-> d, s -move-> t
3053 |
3051 |
3054 ~
3052 ~
3055 #endif
3053 #endif
3056
3054
3057 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
3058 `````````````````````````````````````````````````````````````````````````
3056 `````````````````````````````````````````````````````````````````````````
3059
3057
3060 - the "p-" branch renaming 't' to 'v' (through 'u')
3058 - the "p-" branch renaming 't' to 'v' (through 'u')
3061 - the "q-" branch renaming 'r' to 'v' (through 'w')
3059 - the "q-" branch renaming 'r' to 'v' (through 'w')
3062
3060
3063
3061
3064 $ hg log -G --rev '::(desc("mPQm")+desc("mQPm"))'
3062 $ hg log -G --rev '::(desc("mPQm")+desc("mQPm"))'
3065 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
3066 |\
3064 |\
3067 +---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
3068 | |/
3066 | |/
3069 | o q-2 w -move-> v
3067 | o q-2 w -move-> v
3070 | |
3068 | |
3071 | o q-1 r -move-> w
3069 | o q-1 r -move-> w
3072 | |
3070 | |
3073 o | p-2: u -move-> v
3071 o | p-2: u -move-> v
3074 | |
3072 | |
3075 o | p-1: t -move-> u
3073 o | p-1: t -move-> u
3076 |/
3074 |/
3077 o i-2: c -move-> d, s -move-> t
3075 o i-2: c -move-> d, s -move-> t
3078 |
3076 |
3079 o i-1: a -move-> c, p -move-> s
3077 o i-1: a -move-> c, p -move-> s
3080 |
3078 |
3081 o i-0 initial commit: a b h p q r
3079 o i-0 initial commit: a b h p q r
3082
3080
3083
3081
3084 #if no-changeset
3082 #if no-changeset
3085 $ hg manifest --debug --rev 'desc("mPQm-0")' | grep '644 v'
3083 $ hg manifest --debug --rev 'desc("mPQm-0")' | grep '644 v'
3086 0946c662ef16e4e67397fd717389eb6693d41749 644 v
3084 0946c662ef16e4e67397fd717389eb6693d41749 644 v
3087 $ hg manifest --debug --rev 'desc("mQPm-0")' | grep '644 v'
3085 $ hg manifest --debug --rev 'desc("mQPm-0")' | grep '644 v'
3088 0db3aad7fcc1ec27fab57060e327b9e864ea0cc9 644 v
3086 0db3aad7fcc1ec27fab57060e327b9e864ea0cc9 644 v
3089 $ hg manifest --debug --rev 'desc("p-2")' | grep '644 v'
3087 $ hg manifest --debug --rev 'desc("p-2")' | grep '644 v'
3090 3f91841cd75cadc9a1f1b4e7c1aa6d411f76032e 644 v
3088 3f91841cd75cadc9a1f1b4e7c1aa6d411f76032e 644 v
3091 $ hg manifest --debug --rev 'desc("q-2")' | grep '644 v'
3089 $ hg manifest --debug --rev 'desc("q-2")' | grep '644 v'
3092 c43c088b811fd27983c0a9aadf44f3343cd4cd7e 644 v
3090 c43c088b811fd27983c0a9aadf44f3343cd4cd7e 644 v
3093 $ hg debugindex v | "$PYTHON" ../no-linkrev
3091 $ hg debugindex v | "$PYTHON" ../no-linkrev
3094 rev linkrev nodeid p1 p2
3092 rev linkrev nodeid p1 p2
3095 0 * 3f91841cd75c 000000000000 000000000000
3093 0 * 3f91841cd75c 000000000000 000000000000
3096 1 * c43c088b811f 000000000000 000000000000
3094 1 * c43c088b811f 000000000000 000000000000
3097 2 * 0946c662ef16 3f91841cd75c c43c088b811f
3095 2 * 0946c662ef16 3f91841cd75c c43c088b811f
3098 3 * 0db3aad7fcc1 c43c088b811f 3f91841cd75c
3096 3 * 0db3aad7fcc1 c43c088b811f 3f91841cd75c
3099 #else
3097 #else
3100 $ hg manifest --debug --rev 'desc("mPQm-0")' | grep '644 v'
3098 $ hg manifest --debug --rev 'desc("mPQm-0")' | grep '644 v'
3101 65fde9f6e4d4da23b3f610e07b53673ea9541d75 644 v
3099 65fde9f6e4d4da23b3f610e07b53673ea9541d75 644 v
3102 $ hg manifest --debug --rev 'desc("mQPm-0")' | grep '644 v'
3100 $ hg manifest --debug --rev 'desc("mQPm-0")' | grep '644 v'
3103 a098dda6413aecf154eefc976afc38b295acb7e5 644 v
3101 a098dda6413aecf154eefc976afc38b295acb7e5 644 v
3104 $ hg manifest --debug --rev 'desc("p-2")' | grep '644 v'
3102 $ hg manifest --debug --rev 'desc("p-2")' | grep '644 v'
3105 5aed6a8dbff0301328c08360d24354d3d064cf0d 644 v
3103 5aed6a8dbff0301328c08360d24354d3d064cf0d 644 v
3106 $ hg manifest --debug --rev 'desc("q-2")' | grep '644 v'
3104 $ hg manifest --debug --rev 'desc("q-2")' | grep '644 v'
3107 a38b2fa170219750dac9bc7d19df831f213ba708 644 v
3105 a38b2fa170219750dac9bc7d19df831f213ba708 644 v
3108 $ hg debugindex v | "$PYTHON" ../no-linkrev
3106 $ hg debugindex v | "$PYTHON" ../no-linkrev
3109 rev linkrev nodeid p1 p2
3107 rev linkrev nodeid p1 p2
3110 0 * 5aed6a8dbff0 000000000000 000000000000
3108 0 * 5aed6a8dbff0 000000000000 000000000000
3111 1 * a38b2fa17021 000000000000 000000000000
3109 1 * a38b2fa17021 000000000000 000000000000
3112 2 * 65fde9f6e4d4 5aed6a8dbff0 a38b2fa17021
3110 2 * 65fde9f6e4d4 5aed6a8dbff0 a38b2fa17021
3113 3 * a098dda6413a a38b2fa17021 5aed6a8dbff0
3111 3 * a098dda6413a a38b2fa17021 5aed6a8dbff0
3114 #endif
3112 #endif
3115
3113
3116 # Here the filelog based implementation is not looking at the rename
3114 # Here the filelog based implementation is not looking at the rename
3117 # information (because the file exist on both side). However the changelog
3115 # information (because the file exist on both side). However the changelog
3118 # based on works fine. We have different output.
3116 # based on works fine. We have different output.
3119
3117
3120 $ hg status --copies --rev 'desc("p-2")' --rev 'desc("mPQm-0")'
3118 $ hg status --copies --rev 'desc("p-2")' --rev 'desc("mPQm-0")'
3121 M v
3119 M v
3122 r (no-filelog !)
3120 r (no-filelog !)
3123 R r
3121 R r
3124 $ hg status --copies --rev 'desc("p-2")' --rev 'desc("mQPm-0")'
3122 $ hg status --copies --rev 'desc("p-2")' --rev 'desc("mQPm-0")'
3125 M v
3123 M v
3126 r (no-filelog !)
3124 r (no-filelog !)
3127 R r
3125 R r
3128 $ hg status --copies --rev 'desc("q-2")' --rev 'desc("mPQm-0")'
3126 $ hg status --copies --rev 'desc("q-2")' --rev 'desc("mPQm-0")'
3129 M v
3127 M v
3130 t (no-filelog !)
3128 t (no-filelog !)
3131 R t
3129 R t
3132 $ hg status --copies --rev 'desc("q-2")' --rev 'desc("mQPm-0")'
3130 $ hg status --copies --rev 'desc("q-2")' --rev 'desc("mQPm-0")'
3133 M v
3131 M v
3134 t (no-filelog !)
3132 t (no-filelog !)
3135 R t
3133 R t
3136 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("p-2")'
3134 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("p-2")'
3137 A v
3135 A v
3138 t
3136 t
3139 R t
3137 R t
3140 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("q-2")'
3138 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("q-2")'
3141 A v
3139 A v
3142 r
3140 r
3143 R r
3141 R r
3144
3142
3145 # 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.
3146 #
3144 #
3147 # The filelog based implementation picks an arbitrary side based on revision
3145 # The filelog based implementation picks an arbitrary side based on revision
3148 # 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
3149 # 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
3150 # different from one repository to the next.
3148 # different from one repository to the next.
3151 #
3149 #
3152 # 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
3153 # 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.
3154 # That order is stable accross repositories. (data from p1 prevails)
3152 # That order is stable accross repositories. (data from p1 prevails)
3155
3153
3156 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mPQm-0")'
3154 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mPQm-0")'
3157 A v
3155 A v
3158 t
3156 t
3159 R r
3157 R r
3160 R t
3158 R t
3161 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mQPm-0")'
3159 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mQPm-0")'
3162 A v
3160 A v
3163 t (filelog !)
3161 t (filelog !)
3164 r (no-filelog !)
3162 r (no-filelog !)
3165 R r
3163 R r
3166 R t
3164 R t
3167 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mPQm-0")'
3165 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mPQm-0")'
3168 A d
3166 A d
3169 a
3167 a
3170 A v
3168 A v
3171 r (filelog !)
3169 r (filelog !)
3172 p (no-filelog !)
3170 p (no-filelog !)
3173 R a
3171 R a
3174 R p
3172 R p
3175 R r
3173 R r
3176 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mQPm-0")'
3174 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mQPm-0")'
3177 A d
3175 A d
3178 a
3176 a
3179 A v
3177 A v
3180 r
3178 r
3181 R a
3179 R a
3182 R p
3180 R p
3183 R r
3181 R r
3184
3182
3185
3183
3186 Comparing with merging with a deletion (and keeping the file)
3184 Comparing with merging with a deletion (and keeping the file)
3187 -------------------------------------------------------------
3185 -------------------------------------------------------------
3188
3186
3189 Merge:
3187 Merge:
3190 - one removing a file (d)
3188 - one removing a file (d)
3191 - one updating that file
3189 - one updating that file
3192 - 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)
3193
3191
3194 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
3195 copy tracing chain.
3193 copy tracing chain.
3196
3194
3197 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
3195 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
3198 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
3199 |\
3197 |\
3200 +---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
3201 | |/
3199 | |/
3202 | o g-1: update d
3200 | o g-1: update d
3203 | |
3201 | |
3204 o | c-1 delete d
3202 o | c-1 delete d
3205 |/
3203 |/
3206 o i-2: c -move-> d, s -move-> t
3204 o i-2: c -move-> d, s -move-> t
3207 |
3205 |
3208 o i-1: a -move-> c, p -move-> s
3206 o i-1: a -move-> c, p -move-> s
3209 |
3207 |
3210 o i-0 initial commit: a b h p q r
3208 o i-0 initial commit: a b h p q r
3211
3209
3212
3210
3213 'a' is the copy source of 'd'
3211 'a' is the copy source of 'd'
3214
3212
3215 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCGm-0")'
3213 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCGm-0")'
3216 A d
3214 A d
3217 a (no-compatibility no-changeset !)
3215 a (no-compatibility no-changeset !)
3218 A t
3216 A t
3219 p
3217 p
3220 R a
3218 R a
3221 R p
3219 R p
3222 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGCm-0")'
3220 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGCm-0")'
3223 A d
3221 A d
3224 a (no-compatibility no-changeset !)
3222 a (no-compatibility no-changeset !)
3225 A t
3223 A t
3226 p
3224 p
3227 R a
3225 R a
3228 R p
3226 R p
3229 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCGm-0")'
3227 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCGm-0")'
3230 A d
3228 A d
3231 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mGCm-0")'
3229 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mGCm-0")'
3232 A d
3230 A d
3233 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mCGm-0")'
3231 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mCGm-0")'
3234 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGCm-0")'
3232 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGCm-0")'
3235
3233
3236
3234
3237 Comparing with merge restoring an untouched deleted file
3235 Comparing with merge restoring an untouched deleted file
3238 --------------------------------------------------------
3236 --------------------------------------------------------
3239
3237
3240 Merge:
3238 Merge:
3241 - one removing a file (d)
3239 - one removing a file (d)
3242 - one leaving the file untouched
3240 - one leaving the file untouched
3243 - the merge actively restore the file to the same content.
3241 - the merge actively restore the file to the same content.
3244
3242
3245 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
3246 copy tracing chain.
3244 copy tracing chain.
3247
3245
3248 $ 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"))'
3249 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
3250 |\
3248 |\
3251 +---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
3252 | |/
3250 | |/
3253 | o c-1 delete d
3251 | o c-1 delete d
3254 | |
3252 | |
3255 o | b-1: b update
3253 o | b-1: b update
3256 |/
3254 |/
3257 o i-2: c -move-> d, s -move-> t
3255 o i-2: c -move-> d, s -move-> t
3258 |
3256 |
3259 o i-1: a -move-> c, p -move-> s
3257 o i-1: a -move-> c, p -move-> s
3260 |
3258 |
3261 o i-0 initial commit: a b h p q r
3259 o i-0 initial commit: a b h p q r
3262
3260
3263
3261
3264 'a' is the the copy source of 'd'
3262 'a' is the the copy source of 'd'
3265
3263
3266 $ 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")'
3267 M b
3265 M b
3268 A d
3266 A d
3269 a (no-compatibility no-changeset !)
3267 a (no-compatibility no-changeset !)
3270 A t
3268 A t
3271 p
3269 p
3272 R a
3270 R a
3273 R p
3271 R p
3274 $ 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")'
3275 M b
3273 M b
3276 A d
3274 A d
3277 a (no-compatibility no-changeset !)
3275 a (no-compatibility no-changeset !)
3278 A t
3276 A t
3279 p
3277 p
3280 R a
3278 R a
3281 R p
3279 R p
3282 $ 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")'
3283 M b
3281 M b
3284 A d
3282 A d
3285 $ 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")'
3286 M b
3284 M b
3287 A d
3285 A d
3288 $ 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")'
3289 $ 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")'
3290
3288
3291
3289
3292 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
3293 ------------------------------------------------------------------------------------------
3291 ------------------------------------------------------------------------------------------
3294
3292
3295 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
3296 rename information actually conflict with the other branch.
3294 rename information actually conflict with the other branch.
3297
3295
3298 (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).
3299
3297
3300 $ 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"))'
3301 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
3302 |\
3300 |\
3303 +---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
3304 | |/
3302 | |/
3305 | o h-1: b -(move)-> d
3303 | o h-1: b -(move)-> d
3306 | |
3304 | |
3307 o | c-1 delete d
3305 o | c-1 delete d
3308 | |
3306 | |
3309 o | i-2: c -move-> d, s -move-> t
3307 o | i-2: c -move-> d, s -move-> t
3310 | |
3308 | |
3311 o | i-1: a -move-> c, p -move-> s
3309 o | i-1: a -move-> c, p -move-> s
3312 |/
3310 |/
3313 o i-0 initial commit: a b h p q r
3311 o i-0 initial commit: a b h p q r
3314
3312
3315
3313
3316 $ 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")'
3317 A d
3315 A d
3318 b (no-compatibility no-changeset !)
3316 b (no-compatibility no-changeset !)
3319 A t
3317 A t
3320 p
3318 p
3321 R a
3319 R a
3322 R b
3320 R b
3323 R p
3321 R p
3324 $ 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")'
3325 A d
3323 A d
3326 b
3324 b
3327 A t
3325 A t
3328 p
3326 p
3329 R a
3327 R a
3330 R b
3328 R b
3331 R p
3329 R p
3332 $ 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")'
3333 A d
3331 A d
3334 b
3332 b
3335 R b
3333 R b
3336 $ 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")'
3337 A d
3335 A d
3338 b
3336 b
3339 R b
3337 R b
3340 $ 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")'
3341 A t
3339 A t
3342 p
3340 p
3343 R a
3341 R a
3344 R p
3342 R p
3345 $ 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")'
3346 A t
3344 A t
3347 p
3345 p
3348 R a
3346 R a
3349 R p
3347 R p
3350
3348
3351 Variant of previous with extra changes introduced by the merge
3349 Variant of previous with extra changes introduced by the merge
3352 --------------------------------------------------------------
3350 --------------------------------------------------------------
3353
3351
3354 (see case declaration for details)
3352 (see case declaration for details)
3355
3353
3356 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
3357 ```````````````````````````````````````````````````````````````````````````````````
3355 ```````````````````````````````````````````````````````````````````````````````````
3358
3356
3359 - the "e-" branch renaming b to f (through 'g')
3357 - the "e-" branch renaming b to f (through 'g')
3360 - the "a-" branch renaming d to f (through e)
3358 - the "a-" branch renaming d to f (through e)
3361 - the merge add new change to b
3359 - the merge add new change to b
3362
3360
3363 $ 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"))'
3364 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
3365 |\
3363 |\
3366 +---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
3367 | |/
3365 | |/
3368 | o e-2 g -move-> f
3366 | o e-2 g -move-> f
3369 | |
3367 | |
3370 | o e-1 b -move-> g
3368 | o e-1 b -move-> g
3371 | |
3369 | |
3372 o | a-2: e -move-> f
3370 o | a-2: e -move-> f
3373 | |
3371 | |
3374 o | a-1: d -move-> e
3372 o | a-1: d -move-> e
3375 |/
3373 |/
3376 o i-2: c -move-> d, s -move-> t
3374 o i-2: c -move-> d, s -move-> t
3377 |
3375 |
3378 o i-1: a -move-> c, p -move-> s
3376 o i-1: a -move-> c, p -move-> s
3379 |
3377 |
3380 o i-0 initial commit: a b h p q r
3378 o i-0 initial commit: a b h p q r
3381
3379
3382 #if no-changeset
3380 #if no-changeset
3383 $ 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'
3384 2f649fba7eb284e720d02b61f0546fcef694c045 644 f
3382 2f649fba7eb284e720d02b61f0546fcef694c045 644 f
3385 $ 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'
3386 774e7c1637d536b99e2d8ef16fd731f87a82bd09 644 f
3384 774e7c1637d536b99e2d8ef16fd731f87a82bd09 644 f
3387 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
3385 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
3388 b76eb76580df486c3d51d63c5c210d4dd43a8ac7 644 f
3386 b76eb76580df486c3d51d63c5c210d4dd43a8ac7 644 f
3389 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
3387 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
3390 e8825b386367b29fec957283a80bb47b47483fe1 644 f
3388 e8825b386367b29fec957283a80bb47b47483fe1 644 f
3391 $ hg debugindex f | "$PYTHON" ../no-linkrev
3389 $ hg debugindex f | "$PYTHON" ../no-linkrev
3392 rev linkrev nodeid p1 p2
3390 rev linkrev nodeid p1 p2
3393 0 * b76eb76580df 000000000000 000000000000
3391 0 * b76eb76580df 000000000000 000000000000
3394 1 * e8825b386367 000000000000 000000000000
3392 1 * e8825b386367 000000000000 000000000000
3395 2 * 2ff93c643948 b76eb76580df e8825b386367
3393 2 * 2ff93c643948 b76eb76580df e8825b386367
3396 3 * 2f649fba7eb2 b76eb76580df e8825b386367
3394 3 * 2f649fba7eb2 b76eb76580df e8825b386367
3397 4 * 774e7c1637d5 e8825b386367 b76eb76580df
3395 4 * 774e7c1637d5 e8825b386367 b76eb76580df
3398 #else
3396 #else
3399 $ 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'
3400 d3613c1ec8310a812ac4268fd853ac576b6caea5 644 f
3398 d3613c1ec8310a812ac4268fd853ac576b6caea5 644 f
3401 $ 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'
3402 05e03c868bbcab4a649cb33a238d7aa07398a469 644 f
3400 05e03c868bbcab4a649cb33a238d7aa07398a469 644 f
3403 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
3401 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
3404 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
3402 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
3405 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
3403 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
3406 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
3404 ae258f702dfeca05bf9b6a22a97a4b5645570f11 644 f
3407 $ hg debugindex f | "$PYTHON" ../no-linkrev
3405 $ hg debugindex f | "$PYTHON" ../no-linkrev
3408 rev linkrev nodeid p1 p2
3406 rev linkrev nodeid p1 p2
3409 0 * ae258f702dfe 000000000000 000000000000
3407 0 * ae258f702dfe 000000000000 000000000000
3410 1 * d3613c1ec831 ae258f702dfe 000000000000
3408 1 * d3613c1ec831 ae258f702dfe 000000000000
3411 2 * 05e03c868bbc ae258f702dfe 000000000000
3409 2 * 05e03c868bbc ae258f702dfe 000000000000
3412 #endif
3410 #endif
3413
3411
3414 # Here the filelog based implementation is not looking at the rename
3412 # Here the filelog based implementation is not looking at the rename
3415 # information (because the file exist on both side). However the changelog
3413 # information (because the file exist on both side). However the changelog
3416 # based on works fine. We have different output.
3414 # based on works fine. We have different output.
3417
3415
3418 $ 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")'
3419 M f
3417 M f
3420 b (no-filelog !)
3418 b (no-filelog !)
3421 R b
3419 R b
3422 $ 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")'
3423 M f
3421 M f
3424 b (no-filelog !)
3422 b (no-filelog !)
3425 R b
3423 R b
3426 $ 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")'
3427 M f
3425 M f
3428 d (no-filelog !)
3426 d (no-filelog !)
3429 R d
3427 R d
3430 $ 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")'
3431 M f
3429 M f
3432 d (no-filelog !)
3430 d (no-filelog !)
3433 R d
3431 R d
3434 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")'
3432 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")'
3435 A f
3433 A f
3436 d
3434 d
3437 R d
3435 R d
3438 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("e-2")'
3436 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("e-2")'
3439 A f
3437 A f
3440 b
3438 b
3441 R b
3439 R b
3442
3440
3443 # 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.
3444 #
3442 #
3445 # The filelog based implementation picks an arbitrary side based on revision
3443 # The filelog based implementation picks an arbitrary side based on revision
3446 # 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
3447 # 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
3448 # different from one repository to the next.
3446 # different from one repository to the next.
3449 #
3447 #
3450 # 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
3451 # 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.
3452 # That order is stable accross repositories. (data from p1 prevails)
3450 # That order is stable accross repositories. (data from p1 prevails)
3453
3451
3454 $ 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")'
3455 A f
3453 A f
3456 d
3454 d
3457 R b
3455 R b
3458 R d
3456 R d
3459 $ 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")'
3460 A f
3458 A f
3461 d (filelog !)
3459 d (filelog !)
3462 b (no-filelog !)
3460 b (no-filelog !)
3463 R b
3461 R b
3464 R d
3462 R d
3465 $ 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")'
3466 A f
3464 A f
3467 a
3465 a
3468 A t
3466 A t
3469 p
3467 p
3470 R a
3468 R a
3471 R b
3469 R b
3472 R p
3470 R p
3473 $ 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")'
3474 A f
3472 A f
3475 a (filelog !)
3473 a (filelog !)
3476 b (no-filelog !)
3474 b (no-filelog !)
3477 A t
3475 A t
3478 p
3476 p
3479 R a
3477 R a
3480 R b
3478 R b
3481 R p
3479 R p
3482
3480
3483
3481
3484 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
3485 ```````````````````````````````````````````````````````````````````````````````````
3483 ```````````````````````````````````````````````````````````````````````````````````
3486
3484
3487 Merge:
3485 Merge:
3488 - one with change to an unrelated file (b)
3486 - one with change to an unrelated file (b)
3489 - 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)
3490
3488
3491 $ 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"))'
3492 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
3493 |\
3491 |\
3494 +---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
3495 | |/
3493 | |/
3496 | o f-2: rename i -> d
3494 | o f-2: rename i -> d
3497 | |
3495 | |
3498 | o f-1: rename h -> i
3496 | o f-1: rename h -> i
3499 | |
3497 | |
3500 o | b-1: b update
3498 o | b-1: b update
3501 |/
3499 |/
3502 o i-2: c -move-> d, s -move-> t
3500 o i-2: c -move-> d, s -move-> t
3503 |
3501 |
3504 o i-1: a -move-> c, p -move-> s
3502 o i-1: a -move-> c, p -move-> s
3505 |
3503 |
3506 o i-0 initial commit: a b h p q r
3504 o i-0 initial commit: a b h p q r
3507
3505
3508 $ 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")'
3509 M b
3507 M b
3510 A d
3508 A d
3511 h
3509 h
3512 A t
3510 A t
3513 p
3511 p
3514 R a
3512 R a
3515 R h
3513 R h
3516 R p
3514 R p
3517 $ 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")'
3518 M b
3516 M b
3519 A d
3517 A d
3520 h
3518 h
3521 A t
3519 A t
3522 p
3520 p
3523 R a
3521 R a
3524 R h
3522 R h
3525 R p
3523 R p
3526 $ 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")'
3527 M d
3525 M d
3528 h (no-filelog !)
3526 h (no-filelog !)
3529 R h
3527 R h
3530 $ 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")'
3531 M b
3529 M b
3532 M d
3530 M d
3533 $ 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")'
3534 M b
3532 M b
3535 M d
3533 M d
3536 i (no-filelog !)
3534 i (no-filelog !)
3537 R i
3535 R i
3538 $ 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")'
3539 M d
3537 M d
3540 h (no-filelog !)
3538 h (no-filelog !)
3541 R h
3539 R h
3542 $ 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")'
3543 M b
3541 M b
3544 M d
3542 M d
3545 $ 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")'
3546 M b
3544 M b
3547 M d
3545 M d
3548 i (no-filelog !)
3546 i (no-filelog !)
3549 R i
3547 R i
3550
3548
3551 #if no-changeset
3549 #if no-changeset
3552 $ hg log -Gfr 'desc("mBF-change-m-0")' d
3550 $ hg log -Gfr 'desc("mBF-change-m-0")' d
3553 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
3554 |\
3552 |\
3555 o : f-2: rename i -> d
3553 o : f-2: rename i -> d
3556 | :
3554 | :
3557 o : f-1: rename h -> i
3555 o : f-1: rename h -> i
3558 :/
3556 :/
3559 o i-0 initial commit: a b h p q r
3557 o i-0 initial commit: a b h p q r
3560
3558
3561 #else
3559 #else
3562 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3560 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3563 $ hg log -Gfr 'desc("mBF-change-m-0")' d
3561 $ hg log -Gfr 'desc("mBF-change-m-0")' d
3564 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
3565 :
3563 :
3566 o i-2: c -move-> d, s -move-> t
3564 o i-2: c -move-> d, s -move-> t
3567 |
3565 |
3568 ~
3566 ~
3569 #endif
3567 #endif
3570
3568
3571 #if no-changeset
3569 #if no-changeset
3572 $ hg log -Gfr 'desc("mFB-change-m-0")' d
3570 $ hg log -Gfr 'desc("mFB-change-m-0")' d
3573 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
3574 |\
3572 |\
3575 o : f-2: rename i -> d
3573 o : f-2: rename i -> d
3576 | :
3574 | :
3577 o : f-1: rename h -> i
3575 o : f-1: rename h -> i
3578 :/
3576 :/
3579 o i-0 initial commit: a b h p q r
3577 o i-0 initial commit: a b h p q r
3580
3578
3581 #else
3579 #else
3582 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3580 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
3583 $ hg log -Gfr 'desc("mFB-change-m-0")' d
3581 $ hg log -Gfr 'desc("mFB-change-m-0")' d
3584 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
3585 :
3583 :
3586 o i-2: c -move-> d, s -move-> t
3584 o i-2: c -move-> d, s -move-> t
3587 |
3585 |
3588 ~
3586 ~
3589 #endif
3587 #endif
3590
3588
3591
3589
3592 Subcase: restoring and untouched deleted file, while touching it
3590 Subcase: restoring and untouched deleted file, while touching it
3593 ````````````````````````````````````````````````````````````````
3591 ````````````````````````````````````````````````````````````````
3594
3592
3595 Merge:
3593 Merge:
3596 - one removing a file (d)
3594 - one removing a file (d)
3597 - one leaving the file untouched
3595 - one leaving the file untouched
3598 - the merge actively restore the file to the same content.
3596 - the merge actively restore the file to the same content.
3599
3597
3600 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
3601 copy tracing chain.
3599 copy tracing chain.
3602
3600
3603 $ 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"))'
3604 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
3605 |\
3603 |\
3606 +---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
3607 | |/
3605 | |/
3608 | o c-1 delete d
3606 | o c-1 delete d
3609 | |
3607 | |
3610 o | b-1: b update
3608 o | b-1: b update
3611 |/
3609 |/
3612 o i-2: c -move-> d, s -move-> t
3610 o i-2: c -move-> d, s -move-> t
3613 |
3611 |
3614 o i-1: a -move-> c, p -move-> s
3612 o i-1: a -move-> c, p -move-> s
3615 |
3613 |
3616 o i-0 initial commit: a b h p q r
3614 o i-0 initial commit: a b h p q r
3617
3615
3618
3616
3619 'a' is the the copy source of 'd'
3617 'a' is the the copy source of 'd'
3620
3618
3621 $ 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")'
3622 M b
3620 M b
3623 A d
3621 A d
3624 a (no-compatibility no-changeset !)
3622 a (no-compatibility no-changeset !)
3625 A t
3623 A t
3626 p
3624 p
3627 R a
3625 R a
3628 R p
3626 R p
3629 $ 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")'
3630 M b
3628 M b
3631 A d
3629 A d
3632 a (no-compatibility no-changeset !)
3630 a (no-compatibility no-changeset !)
3633 A t
3631 A t
3634 p
3632 p
3635 R a
3633 R a
3636 R p
3634 R p
3637 $ 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")'
3638 M b
3636 M b
3639 A d
3637 A d
3640 $ 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")'
3641 M b
3639 M b
3642 A d
3640 A d
3643 $ 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")'
3644 M d
3642 M d
3645 $ 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")'
3646 M d
3644 M d
3647
3645
3648
3646
3649 Decision from previous merge are properly chained with later merge
3647 Decision from previous merge are properly chained with later merge
3650 ------------------------------------------------------------------
3648 ------------------------------------------------------------------
3651
3649
3652
3650
3653 Subcase: chaining conflicting rename resolution
3651 Subcase: chaining conflicting rename resolution
3654 ```````````````````````````````````````````````
3652 ```````````````````````````````````````````````
3655
3653
3656 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
3657 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
3658 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"
3659 about that file should stay unchanged.
3657 about that file should stay unchanged.
3660
3658
3661 The result from mAEm is the same for the subsequent merge:
3659 The result from mAEm is the same for the subsequent merge:
3662
3660
3663 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm")' f
3661 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm")' f
3664 A f
3662 A f
3665 a (no-changeset no-compatibility !)
3663 a (no-changeset no-compatibility !)
3666
3664
3667 $ 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
3668 A f
3666 A f
3669 a (no-changeset no-compatibility !)
3667 a (no-changeset no-compatibility !)
3670
3668
3671 $ 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
3672 A f
3670 A f
3673 a (no-changeset no-compatibility !)
3671 a (no-changeset no-compatibility !)
3674
3672
3675
3673
3676 The result from mEAm is the same for the subsequent merge:
3674 The result from mEAm is the same for the subsequent merge:
3677
3675
3678 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm")' f
3676 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm")' f
3679 A f
3677 A f
3680 a (filelog !)
3678 a (filelog !)
3681 b (no-changeset no-compatibility no-filelog !)
3679 b (no-changeset no-compatibility no-filelog !)
3682
3680
3683 $ 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
3684 A f
3682 A f
3685 a (filelog !)
3683 a (filelog !)
3686 b (no-changeset no-compatibility no-filelog !)
3684 b (no-changeset no-compatibility no-filelog !)
3687
3685
3688 $ 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
3689 A f
3687 A f
3690 a (filelog !)
3688 a (filelog !)
3691 b (no-changeset no-compatibility no-filelog !)
3689 b (no-changeset no-compatibility no-filelog !)
3692
3690
3693 Subcase: chaining conflicting rename resolution
3691 Subcase: chaining conflicting rename resolution
3694 ```````````````````````````````````````````````
3692 ```````````````````````````````````````````````
3695
3693
3696 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
3697 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
3698 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"
3699 about that file should stay unchanged.
3697 about that file should stay unchanged.
3700
3698
3701 The result from mPQm is the same for the subsequent merge:
3699 The result from mPQm is the same for the subsequent merge:
3702
3700
3703 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mPQm")' v
3701 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mPQm")' v
3704 A v
3702 A v
3705 r (filelog !)
3703 r (filelog !)
3706 p (no-changeset no-compatibility no-filelog !)
3704 p (no-changeset no-compatibility no-filelog !)
3707
3705
3708 $ 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
3709 A v
3707 A v
3710 r (filelog !)
3708 r (filelog !)
3711 p (no-changeset no-compatibility no-filelog !)
3709 p (no-changeset no-compatibility no-filelog !)
3712
3710
3713 $ 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
3714 A v
3712 A v
3715 r (filelog !)
3713 r (filelog !)
3716 p (no-changeset no-compatibility no-filelog !)
3714 p (no-changeset no-compatibility no-filelog !)
3717
3715
3718 The result from mQPm is the same for the subsequent merge:
3716 The result from mQPm is the same for the subsequent merge:
3719
3717
3720 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mQPm")' v
3718 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mQPm")' v
3721 A v
3719 A v
3722 r (no-changeset no-compatibility !)
3720 r (no-changeset no-compatibility !)
3723
3721
3724 $ 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
3725 A v
3723 A v
3726 r (no-changeset no-compatibility !)
3724 r (no-changeset no-compatibility !)
3727
3725
3728 $ 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
3729 A v
3727 A v
3730 r (filelog !)
3728 r (filelog !)
3731 r (no-changeset no-compatibility no-filelog !)
3729 r (no-changeset no-compatibility no-filelog !)
3732
3730
3733
3731
3734 Subcase: chaining salvage information during a merge
3732 Subcase: chaining salvage information during a merge
3735 ````````````````````````````````````````````````````
3733 ````````````````````````````````````````````````````
3736
3734
3737 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
3738 should preserve the fact eh file was salvaged.
3736 should preserve the fact eh file was salvaged.
3739
3737
3740 reference output:
3738 reference output:
3741
3739
3742 $ 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")'
3743 M b
3741 M b
3744 A d
3742 A d
3745 a (no-changeset no-compatibility !)
3743 a (no-changeset no-compatibility !)
3746 A t
3744 A t
3747 p
3745 p
3748 R a
3746 R a
3749 R p
3747 R p
3750 $ 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")'
3751 M b
3749 M b
3752 A d
3750 A d
3753 a (no-changeset no-compatibility !)
3751 a (no-changeset no-compatibility !)
3754 A t
3752 A t
3755 p
3753 p
3756 R a
3754 R a
3757 R p
3755 R p
3758
3756
3759 chained output
3757 chained output
3760 $ 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")'
3761 M b
3759 M b
3762 A d
3760 A d
3763 a (no-changeset no-compatibility !)
3761 a (no-changeset no-compatibility !)
3764 A t
3762 A t
3765 p
3763 p
3766 A unrelated-l
3764 A unrelated-l
3767 R a
3765 R a
3768 R p
3766 R p
3769 $ 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")'
3770 M b
3768 M b
3771 A d
3769 A d
3772 a (no-changeset no-compatibility !)
3770 a (no-changeset no-compatibility !)
3773 A t
3771 A t
3774 p
3772 p
3775 A unrelated-l
3773 A unrelated-l
3776 R a
3774 R a
3777 R p
3775 R p
3778 $ 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")'
3779 M b
3777 M b
3780 A d
3778 A d
3781 a (no-changeset no-compatibility !)
3779 a (no-changeset no-compatibility !)
3782 A t
3780 A t
3783 p
3781 p
3784 A unrelated-l
3782 A unrelated-l
3785 R a
3783 R a
3786 R p
3784 R p
3787 $ 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")'
3788 M b
3786 M b
3789 A d
3787 A d
3790 a (no-changeset no-compatibility !)
3788 a (no-changeset no-compatibility !)
3791 A t
3789 A t
3792 p
3790 p
3793 A unrelated-l
3791 A unrelated-l
3794 R a
3792 R a
3795 R p
3793 R p
3796
3794
3797 Subcase: chaining "merged" information during a merge
3795 Subcase: chaining "merged" information during a merge
3798 ``````````````````````````````````````````````````````
3796 ``````````````````````````````````````````````````````
3799
3797
3800 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.
3801
3799
3802
3800
3803 reference output:
3801 reference output:
3804
3802
3805 (for details about the filelog pick, check the mFGm/mGFm case)
3803 (for details about the filelog pick, check the mFGm/mGFm case)
3806
3804
3807 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm")' d
3805 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm")' d
3808 A d
3806 A d
3809 a (filelog !)
3807 a (filelog !)
3810 h (no-changeset no-compatibility no-filelog !)
3808 h (no-changeset no-compatibility no-filelog !)
3811 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm")' d
3809 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm")' d
3812 A d
3810 A d
3813 a (filelog !)
3811 a (filelog !)
3814 a (no-changeset no-compatibility no-filelog !)
3812 a (no-changeset no-compatibility no-filelog !)
3815
3813
3816 Chained output
3814 Chained output
3817
3815
3818 $ 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
3819 A d
3817 A d
3820 a (filelog !)
3818 a (filelog !)
3821 h (no-changeset no-compatibility no-filelog !)
3819 h (no-changeset no-compatibility no-filelog !)
3822 $ 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
3823 A d
3821 A d
3824 a (filelog !)
3822 a (filelog !)
3825 h (no-changeset no-compatibility no-filelog !)
3823 h (no-changeset no-compatibility no-filelog !)
3826
3824
3827
3825
3828 $ 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
3829 A d
3827 A d
3830 a (no-changeset no-compatibility !)
3828 a (no-changeset no-compatibility !)
3831 $ 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
3832 A d
3830 A d
3833 a (no-changeset no-compatibility !)
3831 a (no-changeset no-compatibility !)
3834
3832
3835
3833
3836 Subcase: chaining conflicting rename resolution, with extra change during the merge
3834 Subcase: chaining conflicting rename resolution, with extra change during the merge
3837 ```````````````````````````````````````````````````````````````````````````````````
3835 ```````````````````````````````````````````````````````````````````````````````````
3838
3836
3839 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
3840 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
3841 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"
3842 about that file should stay unchanged.
3840 about that file should stay unchanged.
3843
3841
3844 The result from mAEm is the same for the subsequent merge:
3842 The result from mAEm is the same for the subsequent merge:
3845
3843
3846 $ 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
3847 A f
3845 A f
3848 a (no-changeset no-compatibility !)
3846 a (no-changeset no-compatibility !)
3849
3847
3850 $ 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
3851 A f
3849 A f
3852 a (no-changeset no-compatibility !)
3850 a (no-changeset no-compatibility !)
3853
3851
3854 $ 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
3855 A f
3853 A f
3856 a (no-changeset no-compatibility !)
3854 a (no-changeset no-compatibility !)
3857
3855
3858
3856
3859 The result from mEAm is the same for the subsequent merge:
3857 The result from mEAm is the same for the subsequent merge:
3860
3858
3861 $ 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
3862 A f
3860 A f
3863 a (filelog !)
3861 a (filelog !)
3864 b (no-changeset no-compatibility no-filelog !)
3862 b (no-changeset no-compatibility no-filelog !)
3865
3863
3866 $ 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
3867 A f
3865 A f
3868 a (filelog !)
3866 a (filelog !)
3869 b (no-changeset no-compatibility no-filelog !)
3867 b (no-changeset no-compatibility no-filelog !)
3870
3868
3871 $ 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
3872 A f
3870 A f
3873 a (filelog !)
3871 a (filelog !)
3874 b (no-changeset no-compatibility no-filelog !)
3872 b (no-changeset no-compatibility no-filelog !)
@@ -1,509 +1,509 b''
1 #testcases extra sidedata
1 #testcases extra sidedata
2
2
3 #if extra
3 #if extra
4 $ cat >> $HGRCPATH << EOF
4 $ cat >> $HGRCPATH << EOF
5 > [experimental]
5 > [experimental]
6 > copies.write-to=changeset-only
6 > copies.write-to=changeset-only
7 > copies.read-from=changeset-only
7 > copies.read-from=changeset-only
8 > [alias]
8 > [alias]
9 > changesetcopies = log -r . -T 'files: {files}
9 > changesetcopies = log -r . -T 'files: {files}
10 > {extras % "{ifcontains("files", key, "{key}: {value}\n")}"}
10 > {extras % "{ifcontains("files", key, "{key}: {value}\n")}"}
11 > {extras % "{ifcontains("copies", key, "{key}: {value}\n")}"}'
11 > {extras % "{ifcontains("copies", key, "{key}: {value}\n")}"}'
12 > EOF
12 > EOF
13 #endif
13 #endif
14
14
15 #if sidedata
15 #if sidedata
16 $ cat >> $HGRCPATH << EOF
16 $ cat >> $HGRCPATH << EOF
17 > [format]
17 > [format]
18 > exp-use-copies-side-data-changeset = yes
18 > exp-use-copies-side-data-changeset = yes
19 > EOF
19 > EOF
20 #endif
20 #endif
21
21
22 $ cat >> $HGRCPATH << EOF
22 $ cat >> $HGRCPATH << EOF
23 > [alias]
23 > [alias]
24 > showcopies = log -r . -T '{file_copies % "{source} -> {name}\n"}'
24 > showcopies = log -r . -T '{file_copies % "{source} -> {name}\n"}'
25 > [extensions]
25 > [extensions]
26 > rebase =
26 > rebase =
27 > split =
27 > split =
28 > EOF
28 > EOF
29
29
30 Check that copies are recorded correctly
30 Check that copies are recorded correctly
31
31
32 $ hg init repo
32 $ hg init repo
33 $ cd repo
33 $ cd repo
34 #if sidedata
34 #if sidedata
35 $ hg debugformat -v
35 $ hg debugformat -v
36 format-variant repo config default
36 format-variant repo config default
37 fncache: yes yes yes
37 fncache: yes yes yes
38 dotencode: yes yes yes
38 dotencode: yes yes yes
39 generaldelta: yes yes yes
39 generaldelta: yes yes yes
40 share-safe: no no no
40 share-safe: no no no
41 sparserevlog: yes yes yes
41 sparserevlog: yes yes yes
42 persistent-nodemap: no no no (no-rust !)
42 persistent-nodemap: no no no (no-rust !)
43 persistent-nodemap: yes yes no (rust !)
43 persistent-nodemap: yes yes no (rust !)
44 copies-sdc: yes yes no
44 copies-sdc: yes yes no
45 revlog-v2: yes yes no
45 revlog-v2: no no no
46 changelog-v2: no no no
46 changelog-v2: yes yes no
47 plain-cl-delta: yes yes yes
47 plain-cl-delta: yes yes yes
48 compression: zlib zlib zlib (no-zstd !)
48 compression: zlib zlib zlib (no-zstd !)
49 compression: zstd zstd zstd (zstd !)
49 compression: zstd zstd zstd (zstd !)
50 compression-level: default default default
50 compression-level: default default default
51 #else
51 #else
52 $ hg debugformat -v
52 $ hg debugformat -v
53 format-variant repo config default
53 format-variant repo config default
54 fncache: yes yes yes
54 fncache: yes yes yes
55 dotencode: yes yes yes
55 dotencode: yes yes yes
56 generaldelta: yes yes yes
56 generaldelta: yes yes yes
57 share-safe: no no no
57 share-safe: no no no
58 sparserevlog: yes yes yes
58 sparserevlog: yes yes yes
59 persistent-nodemap: no no no (no-rust !)
59 persistent-nodemap: no no no (no-rust !)
60 persistent-nodemap: yes yes no (rust !)
60 persistent-nodemap: yes yes no (rust !)
61 copies-sdc: no no no
61 copies-sdc: no no no
62 revlog-v2: no no no
62 revlog-v2: no no no
63 changelog-v2: no no no
63 changelog-v2: no no no
64 plain-cl-delta: yes yes yes
64 plain-cl-delta: yes yes yes
65 compression: zlib zlib zlib (no-zstd !)
65 compression: zlib zlib zlib (no-zstd !)
66 compression: zstd zstd zstd (zstd !)
66 compression: zstd zstd zstd (zstd !)
67 compression-level: default default default
67 compression-level: default default default
68 #endif
68 #endif
69 $ echo a > a
69 $ echo a > a
70 $ hg add a
70 $ hg add a
71 $ hg ci -m initial
71 $ hg ci -m initial
72 $ hg cp a b
72 $ hg cp a b
73 $ hg cp a c
73 $ hg cp a c
74 $ hg cp a d
74 $ hg cp a d
75 $ hg ci -m 'copy a to b, c, and d'
75 $ hg ci -m 'copy a to b, c, and d'
76
76
77 #if extra
77 #if extra
78
78
79 $ hg changesetcopies
79 $ hg changesetcopies
80 files: b c d
80 files: b c d
81 filesadded: 0
81 filesadded: 0
82 1
82 1
83 2
83 2
84
84
85 p1copies: 0\x00a (esc)
85 p1copies: 0\x00a (esc)
86 1\x00a (esc)
86 1\x00a (esc)
87 2\x00a (esc)
87 2\x00a (esc)
88 #else
88 #else
89 $ hg debugsidedata -c -v -- -1
89 $ hg debugsidedata -c -v -- -1
90 1 sidedata entries
90 1 sidedata entries
91 entry-0014 size 44
91 entry-0014 size 44
92 '\x00\x00\x00\x04\x00\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00\x06\x00\x00\x00\x03\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x00abcd'
92 '\x00\x00\x00\x04\x00\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00\x06\x00\x00\x00\x03\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x00abcd'
93 #endif
93 #endif
94
94
95 $ hg showcopies
95 $ hg showcopies
96 a -> b
96 a -> b
97 a -> c
97 a -> c
98 a -> d
98 a -> d
99
99
100 #if extra
100 #if extra
101
101
102 $ hg showcopies --config experimental.copies.read-from=compatibility
102 $ hg showcopies --config experimental.copies.read-from=compatibility
103 a -> b
103 a -> b
104 a -> c
104 a -> c
105 a -> d
105 a -> d
106 $ hg showcopies --config experimental.copies.read-from=filelog-only
106 $ hg showcopies --config experimental.copies.read-from=filelog-only
107
107
108 #endif
108 #endif
109
109
110 Check that renames are recorded correctly
110 Check that renames are recorded correctly
111
111
112 $ hg mv b b2
112 $ hg mv b b2
113 $ hg ci -m 'rename b to b2'
113 $ hg ci -m 'rename b to b2'
114
114
115 #if extra
115 #if extra
116
116
117 $ hg changesetcopies
117 $ hg changesetcopies
118 files: b b2
118 files: b b2
119 filesadded: 1
119 filesadded: 1
120 filesremoved: 0
120 filesremoved: 0
121
121
122 p1copies: 1\x00b (esc)
122 p1copies: 1\x00b (esc)
123
123
124 #else
124 #else
125 $ hg debugsidedata -c -v -- -1
125 $ hg debugsidedata -c -v -- -1
126 1 sidedata entries
126 1 sidedata entries
127 entry-0014 size 25
127 entry-0014 size 25
128 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x03\x00\x00\x00\x00bb2'
128 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x03\x00\x00\x00\x00bb2'
129 #endif
129 #endif
130
130
131 $ hg showcopies
131 $ hg showcopies
132 b -> b2
132 b -> b2
133
133
134
134
135 Rename onto existing file. This should get recorded in the changeset files list and in the extras,
135 Rename onto existing file. This should get recorded in the changeset files list and in the extras,
136 even though there is no filelog entry.
136 even though there is no filelog entry.
137
137
138 $ hg cp b2 c --force
138 $ hg cp b2 c --force
139 $ hg st --copies
139 $ hg st --copies
140 M c
140 M c
141 b2
141 b2
142
142
143 #if extra
143 #if extra
144
144
145 $ hg debugindex c
145 $ hg debugindex c
146 rev linkrev nodeid p1 p2
146 rev linkrev nodeid p1 p2
147 0 1 b789fdd96dc2 000000000000 000000000000
147 0 1 b789fdd96dc2 000000000000 000000000000
148
148
149 #else
149 #else
150
150
151 $ hg debugindex c
151 $ hg debugindex c
152 rev linkrev nodeid p1 p2
152 rev linkrev nodeid p1 p2
153 0 1 37d9b5d994ea 000000000000 000000000000
153 0 1 37d9b5d994ea 000000000000 000000000000
154
154
155 #endif
155 #endif
156
156
157
157
158 $ hg ci -m 'move b onto d'
158 $ hg ci -m 'move b onto d'
159
159
160 #if extra
160 #if extra
161
161
162 $ hg changesetcopies
162 $ hg changesetcopies
163 files: c
163 files: c
164
164
165 p1copies: 0\x00b2 (esc)
165 p1copies: 0\x00b2 (esc)
166
166
167 #else
167 #else
168 $ hg debugsidedata -c -v -- -1
168 $ hg debugsidedata -c -v -- -1
169 1 sidedata entries
169 1 sidedata entries
170 entry-0014 size 25
170 entry-0014 size 25
171 '\x00\x00\x00\x02\x00\x00\x00\x00\x02\x00\x00\x00\x00\x16\x00\x00\x00\x03\x00\x00\x00\x00b2c'
171 '\x00\x00\x00\x02\x00\x00\x00\x00\x02\x00\x00\x00\x00\x16\x00\x00\x00\x03\x00\x00\x00\x00b2c'
172 #endif
172 #endif
173
173
174 $ hg showcopies
174 $ hg showcopies
175 b2 -> c
175 b2 -> c
176
176
177 #if extra
177 #if extra
178
178
179 $ hg debugindex c
179 $ hg debugindex c
180 rev linkrev nodeid p1 p2
180 rev linkrev nodeid p1 p2
181 0 1 b789fdd96dc2 000000000000 000000000000
181 0 1 b789fdd96dc2 000000000000 000000000000
182
182
183 #else
183 #else
184
184
185 $ hg debugindex c
185 $ hg debugindex c
186 rev linkrev nodeid p1 p2
186 rev linkrev nodeid p1 p2
187 0 1 37d9b5d994ea 000000000000 000000000000
187 0 1 37d9b5d994ea 000000000000 000000000000
188 1 3 029625640347 000000000000 000000000000
188 1 3 029625640347 000000000000 000000000000
189
189
190 #endif
190 #endif
191
191
192 Create a merge commit with copying done during merge.
192 Create a merge commit with copying done during merge.
193
193
194 $ hg co 0
194 $ hg co 0
195 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
195 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
196 $ hg cp a e
196 $ hg cp a e
197 $ hg cp a f
197 $ hg cp a f
198 $ hg ci -m 'copy a to e and f'
198 $ hg ci -m 'copy a to e and f'
199 created new head
199 created new head
200 $ hg merge 3
200 $ hg merge 3
201 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
201 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
202 (branch merge, don't forget to commit)
202 (branch merge, don't forget to commit)
203 File 'a' exists on both sides, so 'g' could be recorded as being from p1 or p2, but we currently
203 File 'a' exists on both sides, so 'g' could be recorded as being from p1 or p2, but we currently
204 always record it as being from p1
204 always record it as being from p1
205 $ hg cp a g
205 $ hg cp a g
206 File 'd' exists only in p2, so 'h' should be from p2
206 File 'd' exists only in p2, so 'h' should be from p2
207 $ hg cp d h
207 $ hg cp d h
208 File 'f' exists only in p1, so 'i' should be from p1
208 File 'f' exists only in p1, so 'i' should be from p1
209 $ hg cp f i
209 $ hg cp f i
210 $ hg ci -m 'merge'
210 $ hg ci -m 'merge'
211
211
212 #if extra
212 #if extra
213
213
214 $ hg changesetcopies
214 $ hg changesetcopies
215 files: g h i
215 files: g h i
216 filesadded: 0
216 filesadded: 0
217 1
217 1
218 2
218 2
219
219
220 p1copies: 0\x00a (esc)
220 p1copies: 0\x00a (esc)
221 2\x00f (esc)
221 2\x00f (esc)
222 p2copies: 1\x00d (esc)
222 p2copies: 1\x00d (esc)
223
223
224 #else
224 #else
225 $ hg debugsidedata -c -v -- -1
225 $ hg debugsidedata -c -v -- -1
226 1 sidedata entries
226 1 sidedata entries
227 entry-0014 size 64
227 entry-0014 size 64
228 '\x00\x00\x00\x06\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x00\x07\x00\x00\x00\x05\x00\x00\x00\x01\x06\x00\x00\x00\x06\x00\x00\x00\x02adfghi'
228 '\x00\x00\x00\x06\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x06\x00\x00\x00\x04\x00\x00\x00\x00\x07\x00\x00\x00\x05\x00\x00\x00\x01\x06\x00\x00\x00\x06\x00\x00\x00\x02adfghi'
229 #endif
229 #endif
230
230
231 $ hg showcopies
231 $ hg showcopies
232 a -> g
232 a -> g
233 d -> h
233 d -> h
234 f -> i
234 f -> i
235
235
236 Test writing to both changeset and filelog
236 Test writing to both changeset and filelog
237
237
238 $ hg cp a j
238 $ hg cp a j
239 #if extra
239 #if extra
240 $ hg ci -m 'copy a to j' --config experimental.copies.write-to=compatibility
240 $ hg ci -m 'copy a to j' --config experimental.copies.write-to=compatibility
241 $ hg changesetcopies
241 $ hg changesetcopies
242 files: j
242 files: j
243 filesadded: 0
243 filesadded: 0
244 filesremoved:
244 filesremoved:
245
245
246 p1copies: 0\x00a (esc)
246 p1copies: 0\x00a (esc)
247 p2copies:
247 p2copies:
248 #else
248 #else
249 $ hg ci -m 'copy a to j'
249 $ hg ci -m 'copy a to j'
250 $ hg debugsidedata -c -v -- -1
250 $ hg debugsidedata -c -v -- -1
251 1 sidedata entries
251 1 sidedata entries
252 entry-0014 size 24
252 entry-0014 size 24
253 '\x00\x00\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00aj'
253 '\x00\x00\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00aj'
254 #endif
254 #endif
255 $ hg debugdata j 0
255 $ hg debugdata j 0
256 \x01 (esc)
256 \x01 (esc)
257 copy: a
257 copy: a
258 copyrev: b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
258 copyrev: b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
259 \x01 (esc)
259 \x01 (esc)
260 a
260 a
261 $ hg showcopies
261 $ hg showcopies
262 a -> j
262 a -> j
263 $ hg showcopies --config experimental.copies.read-from=compatibility
263 $ hg showcopies --config experimental.copies.read-from=compatibility
264 a -> j
264 a -> j
265 $ hg showcopies --config experimental.copies.read-from=filelog-only
265 $ hg showcopies --config experimental.copies.read-from=filelog-only
266 a -> j
266 a -> j
267 Existing copy information in the changeset gets removed on amend and writing
267 Existing copy information in the changeset gets removed on amend and writing
268 copy information on to the filelog
268 copy information on to the filelog
269 #if extra
269 #if extra
270 $ hg ci --amend -m 'copy a to j, v2' \
270 $ hg ci --amend -m 'copy a to j, v2' \
271 > --config experimental.copies.write-to=filelog-only
271 > --config experimental.copies.write-to=filelog-only
272 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-*-amend.hg (glob)
272 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-*-amend.hg (glob)
273 $ hg changesetcopies
273 $ hg changesetcopies
274 files: j
274 files: j
275
275
276 #else
276 #else
277 $ hg ci --amend -m 'copy a to j, v2'
277 $ hg ci --amend -m 'copy a to j, v2'
278 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-*-amend.hg (glob)
278 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-*-amend.hg (glob)
279 $ hg debugsidedata -c -v -- -1
279 $ hg debugsidedata -c -v -- -1
280 1 sidedata entries
280 1 sidedata entries
281 entry-0014 size 24
281 entry-0014 size 24
282 '\x00\x00\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00aj'
282 '\x00\x00\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00aj'
283 #endif
283 #endif
284 $ hg showcopies --config experimental.copies.read-from=filelog-only
284 $ hg showcopies --config experimental.copies.read-from=filelog-only
285 a -> j
285 a -> j
286 The entries should be written to extras even if they're empty (so the client
286 The entries should be written to extras even if they're empty (so the client
287 won't have to fall back to reading from filelogs)
287 won't have to fall back to reading from filelogs)
288 $ echo x >> j
288 $ echo x >> j
289 #if extra
289 #if extra
290 $ hg ci -m 'modify j' --config experimental.copies.write-to=compatibility
290 $ hg ci -m 'modify j' --config experimental.copies.write-to=compatibility
291 $ hg changesetcopies
291 $ hg changesetcopies
292 files: j
292 files: j
293 filesadded:
293 filesadded:
294 filesremoved:
294 filesremoved:
295
295
296 p1copies:
296 p1copies:
297 p2copies:
297 p2copies:
298 #else
298 #else
299 $ hg ci -m 'modify j'
299 $ hg ci -m 'modify j'
300 $ hg debugsidedata -c -v -- -1
300 $ hg debugsidedata -c -v -- -1
301 1 sidedata entries
301 1 sidedata entries
302 entry-0014 size 14
302 entry-0014 size 14
303 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00j'
303 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00j'
304 #endif
304 #endif
305
305
306 Test writing only to filelog
306 Test writing only to filelog
307
307
308 $ hg cp a k
308 $ hg cp a k
309 #if extra
309 #if extra
310 $ hg ci -m 'copy a to k' --config experimental.copies.write-to=filelog-only
310 $ hg ci -m 'copy a to k' --config experimental.copies.write-to=filelog-only
311
311
312 $ hg changesetcopies
312 $ hg changesetcopies
313 files: k
313 files: k
314
314
315 #else
315 #else
316 $ hg ci -m 'copy a to k'
316 $ hg ci -m 'copy a to k'
317 $ hg debugsidedata -c -v -- -1
317 $ hg debugsidedata -c -v -- -1
318 1 sidedata entries
318 1 sidedata entries
319 entry-0014 size 24
319 entry-0014 size 24
320 '\x00\x00\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00ak'
320 '\x00\x00\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00ak'
321 #endif
321 #endif
322
322
323 $ hg debugdata k 0
323 $ hg debugdata k 0
324 \x01 (esc)
324 \x01 (esc)
325 copy: a
325 copy: a
326 copyrev: b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
326 copyrev: b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
327 \x01 (esc)
327 \x01 (esc)
328 a
328 a
329 #if extra
329 #if extra
330 $ hg showcopies
330 $ hg showcopies
331
331
332 $ hg showcopies --config experimental.copies.read-from=compatibility
332 $ hg showcopies --config experimental.copies.read-from=compatibility
333 a -> k
333 a -> k
334 $ hg showcopies --config experimental.copies.read-from=filelog-only
334 $ hg showcopies --config experimental.copies.read-from=filelog-only
335 a -> k
335 a -> k
336 #else
336 #else
337 $ hg showcopies
337 $ hg showcopies
338 a -> k
338 a -> k
339 #endif
339 #endif
340
340
341 $ cd ..
341 $ cd ..
342
342
343 Test rebasing a commit with copy information
343 Test rebasing a commit with copy information
344
344
345 $ hg init rebase-rename
345 $ hg init rebase-rename
346 $ cd rebase-rename
346 $ cd rebase-rename
347 $ echo a > a
347 $ echo a > a
348 $ hg ci -Aqm 'add a'
348 $ hg ci -Aqm 'add a'
349 $ echo a2 > a
349 $ echo a2 > a
350 $ hg ci -m 'modify a'
350 $ hg ci -m 'modify a'
351 $ hg co -q 0
351 $ hg co -q 0
352 $ hg mv a b
352 $ hg mv a b
353 $ hg ci -qm 'rename a to b'
353 $ hg ci -qm 'rename a to b'
354 Not only do we want this to run in-memory, it shouldn't fall back to
354 Not only do we want this to run in-memory, it shouldn't fall back to
355 on-disk merge (no conflicts), so we force it to be in-memory
355 on-disk merge (no conflicts), so we force it to be in-memory
356 with no fallback.
356 with no fallback.
357 $ hg rebase -d 1 --config rebase.experimental.inmemory=yes --config devel.rebase.force-in-memory-merge=yes
357 $ hg rebase -d 1 --config rebase.experimental.inmemory=yes --config devel.rebase.force-in-memory-merge=yes
358 rebasing 2:* tip "rename a to b" (glob)
358 rebasing 2:* tip "rename a to b" (glob)
359 merging a and b to b
359 merging a and b to b
360 saved backup bundle to $TESTTMP/rebase-rename/.hg/strip-backup/*-*-rebase.hg (glob)
360 saved backup bundle to $TESTTMP/rebase-rename/.hg/strip-backup/*-*-rebase.hg (glob)
361 $ hg st --change . --copies
361 $ hg st --change . --copies
362 A b
362 A b
363 a
363 a
364 R a
364 R a
365 $ cd ..
365 $ cd ..
366
366
367 Test splitting a commit
367 Test splitting a commit
368
368
369 $ hg init split
369 $ hg init split
370 $ cd split
370 $ cd split
371 $ echo a > a
371 $ echo a > a
372 $ echo b > b
372 $ echo b > b
373 $ hg ci -Aqm 'add a and b'
373 $ hg ci -Aqm 'add a and b'
374 $ echo a2 > a
374 $ echo a2 > a
375 $ hg mv b c
375 $ hg mv b c
376 $ hg ci -m 'modify a, move b to c'
376 $ hg ci -m 'modify a, move b to c'
377 $ hg --config ui.interactive=yes split <<EOF
377 $ hg --config ui.interactive=yes split <<EOF
378 > y
378 > y
379 > y
379 > y
380 > n
380 > n
381 > y
381 > y
382 > EOF
382 > EOF
383 diff --git a/a b/a
383 diff --git a/a b/a
384 1 hunks, 1 lines changed
384 1 hunks, 1 lines changed
385 examine changes to 'a'?
385 examine changes to 'a'?
386 (enter ? for help) [Ynesfdaq?] y
386 (enter ? for help) [Ynesfdaq?] y
387
387
388 @@ -1,1 +1,1 @@
388 @@ -1,1 +1,1 @@
389 -a
389 -a
390 +a2
390 +a2
391 record this change to 'a'?
391 record this change to 'a'?
392 (enter ? for help) [Ynesfdaq?] y
392 (enter ? for help) [Ynesfdaq?] y
393
393
394 diff --git a/b b/c
394 diff --git a/b b/c
395 rename from b
395 rename from b
396 rename to c
396 rename to c
397 examine changes to 'b' and 'c'?
397 examine changes to 'b' and 'c'?
398 (enter ? for help) [Ynesfdaq?] n
398 (enter ? for help) [Ynesfdaq?] n
399
399
400 created new head
400 created new head
401 diff --git a/b b/c
401 diff --git a/b b/c
402 rename from b
402 rename from b
403 rename to c
403 rename to c
404 examine changes to 'b' and 'c'?
404 examine changes to 'b' and 'c'?
405 (enter ? for help) [Ynesfdaq?] y
405 (enter ? for help) [Ynesfdaq?] y
406
406
407 saved backup bundle to $TESTTMP/split/.hg/strip-backup/*-*-split.hg (glob)
407 saved backup bundle to $TESTTMP/split/.hg/strip-backup/*-*-split.hg (glob)
408 $ cd ..
408 $ cd ..
409
409
410 Test committing half a rename
410 Test committing half a rename
411
411
412 $ hg init partial
412 $ hg init partial
413 $ cd partial
413 $ cd partial
414 $ echo a > a
414 $ echo a > a
415 $ hg ci -Aqm 'add a'
415 $ hg ci -Aqm 'add a'
416 $ hg mv a b
416 $ hg mv a b
417 $ hg ci -m 'remove a' a
417 $ hg ci -m 'remove a' a
418
418
419 #if sidedata
419 #if sidedata
420
420
421 Test upgrading/downgrading to sidedata storage
421 Test upgrading/downgrading to sidedata storage
422 ==============================================
422 ==============================================
423
423
424 downgrading
424 downgrading
425
425
426 $ hg debugformat -v
426 $ hg debugformat -v
427 format-variant repo config default
427 format-variant repo config default
428 fncache: yes yes yes
428 fncache: yes yes yes
429 dotencode: yes yes yes
429 dotencode: yes yes yes
430 generaldelta: yes yes yes
430 generaldelta: yes yes yes
431 share-safe: no no no
431 share-safe: no no no
432 sparserevlog: yes yes yes
432 sparserevlog: yes yes yes
433 persistent-nodemap: no no no (no-rust !)
433 persistent-nodemap: no no no (no-rust !)
434 persistent-nodemap: yes yes no (rust !)
434 persistent-nodemap: yes yes no (rust !)
435 copies-sdc: yes yes no
435 copies-sdc: yes yes no
436 revlog-v2: yes yes no
436 revlog-v2: no no no
437 changelog-v2: no no no
437 changelog-v2: yes yes no
438 plain-cl-delta: yes yes yes
438 plain-cl-delta: yes yes yes
439 compression: zlib zlib zlib (no-zstd !)
439 compression: zlib zlib zlib (no-zstd !)
440 compression: zstd zstd zstd (zstd !)
440 compression: zstd zstd zstd (zstd !)
441 compression-level: default default default
441 compression-level: default default default
442 $ hg debugsidedata -c -- 0
442 $ hg debugsidedata -c -- 0
443 1 sidedata entries
443 1 sidedata entries
444 entry-0014 size 14
444 entry-0014 size 14
445 $ hg debugsidedata -c -- 1
445 $ hg debugsidedata -c -- 1
446 1 sidedata entries
446 1 sidedata entries
447 entry-0014 size 14
447 entry-0014 size 14
448 $ hg debugsidedata -m -- 0
448 $ hg debugsidedata -m -- 0
449 $ cat << EOF > .hg/hgrc
449 $ cat << EOF > .hg/hgrc
450 > [format]
450 > [format]
451 > exp-use-copies-side-data-changeset = no
451 > exp-use-copies-side-data-changeset = no
452 > [experimental]
452 > [experimental]
453 > revlogv2 = enable-unstable-format-and-corrupt-my-data
453 > revlogv2 = enable-unstable-format-and-corrupt-my-data
454 > EOF
454 > EOF
455 $ hg debugupgraderepo --run --quiet --no-backup > /dev/null
455 $ hg debugupgraderepo --run --quiet --no-backup > /dev/null
456 $ hg debugformat -v
456 $ hg debugformat -v
457 format-variant repo config default
457 format-variant repo config default
458 fncache: yes yes yes
458 fncache: yes yes yes
459 dotencode: yes yes yes
459 dotencode: yes yes yes
460 generaldelta: yes yes yes
460 generaldelta: yes yes yes
461 share-safe: no no no
461 share-safe: no no no
462 sparserevlog: yes yes yes
462 sparserevlog: yes yes yes
463 persistent-nodemap: no no no (no-rust !)
463 persistent-nodemap: no no no (no-rust !)
464 persistent-nodemap: yes yes no (rust !)
464 persistent-nodemap: yes yes no (rust !)
465 copies-sdc: no no no
465 copies-sdc: no no no
466 revlog-v2: yes yes no
466 revlog-v2: yes yes no
467 changelog-v2: no no no
467 changelog-v2: no no no
468 plain-cl-delta: yes yes yes
468 plain-cl-delta: yes yes yes
469 compression: zlib zlib zlib (no-zstd !)
469 compression: zlib zlib zlib (no-zstd !)
470 compression: zstd zstd zstd (zstd !)
470 compression: zstd zstd zstd (zstd !)
471 compression-level: default default default
471 compression-level: default default default
472 $ hg debugsidedata -c -- 0
472 $ hg debugsidedata -c -- 0
473 $ hg debugsidedata -c -- 1
473 $ hg debugsidedata -c -- 1
474 $ hg debugsidedata -m -- 0
474 $ hg debugsidedata -m -- 0
475
475
476 upgrading
476 upgrading
477
477
478 $ cat << EOF > .hg/hgrc
478 $ cat << EOF > .hg/hgrc
479 > [format]
479 > [format]
480 > exp-use-copies-side-data-changeset = yes
480 > exp-use-copies-side-data-changeset = yes
481 > EOF
481 > EOF
482 $ hg debugupgraderepo --run --quiet --no-backup > /dev/null
482 $ hg debugupgraderepo --run --quiet --no-backup > /dev/null
483 $ hg debugformat -v
483 $ hg debugformat -v
484 format-variant repo config default
484 format-variant repo config default
485 fncache: yes yes yes
485 fncache: yes yes yes
486 dotencode: yes yes yes
486 dotencode: yes yes yes
487 generaldelta: yes yes yes
487 generaldelta: yes yes yes
488 share-safe: no no no
488 share-safe: no no no
489 sparserevlog: yes yes yes
489 sparserevlog: yes yes yes
490 persistent-nodemap: no no no (no-rust !)
490 persistent-nodemap: no no no (no-rust !)
491 persistent-nodemap: yes yes no (rust !)
491 persistent-nodemap: yes yes no (rust !)
492 copies-sdc: yes yes no
492 copies-sdc: yes yes no
493 revlog-v2: yes yes no
493 revlog-v2: no no no
494 changelog-v2: no no no
494 changelog-v2: yes yes no
495 plain-cl-delta: yes yes yes
495 plain-cl-delta: yes yes yes
496 compression: zlib zlib zlib (no-zstd !)
496 compression: zlib zlib zlib (no-zstd !)
497 compression: zstd zstd zstd (zstd !)
497 compression: zstd zstd zstd (zstd !)
498 compression-level: default default default
498 compression-level: default default default
499 $ hg debugsidedata -c -- 0
499 $ hg debugsidedata -c -- 0
500 1 sidedata entries
500 1 sidedata entries
501 entry-0014 size 14
501 entry-0014 size 14
502 $ hg debugsidedata -c -- 1
502 $ hg debugsidedata -c -- 1
503 1 sidedata entries
503 1 sidedata entries
504 entry-0014 size 14
504 entry-0014 size 14
505 $ hg debugsidedata -m -- 0
505 $ hg debugsidedata -m -- 0
506
506
507 #endif
507 #endif
508
508
509 $ cd ..
509 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now