##// END OF EJS Templates
revlog: backed out changeset e9d325cfe071...
Augie Fackler -
r33203:664d6f6c stable
parent child Browse files
Show More
@@ -1,2061 +1,2058 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 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@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 hashlib
11 import hashlib
12 import inspect
12 import inspect
13 import os
13 import os
14 import random
14 import random
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 hex,
20 hex,
21 nullid,
21 nullid,
22 short,
22 short,
23 wdirrev,
23 wdirrev,
24 )
24 )
25 from . import (
25 from . import (
26 bookmarks,
26 bookmarks,
27 branchmap,
27 branchmap,
28 bundle2,
28 bundle2,
29 changegroup,
29 changegroup,
30 changelog,
30 changelog,
31 color,
31 color,
32 context,
32 context,
33 dirstate,
33 dirstate,
34 dirstateguard,
34 dirstateguard,
35 encoding,
35 encoding,
36 error,
36 error,
37 exchange,
37 exchange,
38 extensions,
38 extensions,
39 filelog,
39 filelog,
40 hook,
40 hook,
41 lock as lockmod,
41 lock as lockmod,
42 manifest,
42 manifest,
43 match as matchmod,
43 match as matchmod,
44 merge as mergemod,
44 merge as mergemod,
45 mergeutil,
45 mergeutil,
46 namespaces,
46 namespaces,
47 obsolete,
47 obsolete,
48 pathutil,
48 pathutil,
49 peer,
49 peer,
50 phases,
50 phases,
51 pushkey,
51 pushkey,
52 pycompat,
52 pycompat,
53 repoview,
53 repoview,
54 revset,
54 revset,
55 revsetlang,
55 revsetlang,
56 scmutil,
56 scmutil,
57 store,
57 store,
58 subrepo,
58 subrepo,
59 tags as tagsmod,
59 tags as tagsmod,
60 transaction,
60 transaction,
61 txnutil,
61 txnutil,
62 util,
62 util,
63 vfs as vfsmod,
63 vfs as vfsmod,
64 )
64 )
65
65
66 release = lockmod.release
66 release = lockmod.release
67 urlerr = util.urlerr
67 urlerr = util.urlerr
68 urlreq = util.urlreq
68 urlreq = util.urlreq
69
69
70 class repofilecache(scmutil.filecache):
70 class repofilecache(scmutil.filecache):
71 """All filecache usage on repo are done for logic that should be unfiltered
71 """All filecache usage on repo are done for logic that should be unfiltered
72 """
72 """
73
73
74 def join(self, obj, fname):
74 def join(self, obj, fname):
75 return obj.vfs.join(fname)
75 return obj.vfs.join(fname)
76 def __get__(self, repo, type=None):
76 def __get__(self, repo, type=None):
77 if repo is None:
77 if repo is None:
78 return self
78 return self
79 return super(repofilecache, self).__get__(repo.unfiltered(), type)
79 return super(repofilecache, self).__get__(repo.unfiltered(), type)
80 def __set__(self, repo, value):
80 def __set__(self, repo, value):
81 return super(repofilecache, self).__set__(repo.unfiltered(), value)
81 return super(repofilecache, self).__set__(repo.unfiltered(), value)
82 def __delete__(self, repo):
82 def __delete__(self, repo):
83 return super(repofilecache, self).__delete__(repo.unfiltered())
83 return super(repofilecache, self).__delete__(repo.unfiltered())
84
84
85 class storecache(repofilecache):
85 class storecache(repofilecache):
86 """filecache for files in the store"""
86 """filecache for files in the store"""
87 def join(self, obj, fname):
87 def join(self, obj, fname):
88 return obj.sjoin(fname)
88 return obj.sjoin(fname)
89
89
90 class unfilteredpropertycache(util.propertycache):
90 class unfilteredpropertycache(util.propertycache):
91 """propertycache that apply to unfiltered repo only"""
91 """propertycache that apply to unfiltered repo only"""
92
92
93 def __get__(self, repo, type=None):
93 def __get__(self, repo, type=None):
94 unfi = repo.unfiltered()
94 unfi = repo.unfiltered()
95 if unfi is repo:
95 if unfi is repo:
96 return super(unfilteredpropertycache, self).__get__(unfi)
96 return super(unfilteredpropertycache, self).__get__(unfi)
97 return getattr(unfi, self.name)
97 return getattr(unfi, self.name)
98
98
99 class filteredpropertycache(util.propertycache):
99 class filteredpropertycache(util.propertycache):
100 """propertycache that must take filtering in account"""
100 """propertycache that must take filtering in account"""
101
101
102 def cachevalue(self, obj, value):
102 def cachevalue(self, obj, value):
103 object.__setattr__(obj, self.name, value)
103 object.__setattr__(obj, self.name, value)
104
104
105
105
106 def hasunfilteredcache(repo, name):
106 def hasunfilteredcache(repo, name):
107 """check if a repo has an unfilteredpropertycache value for <name>"""
107 """check if a repo has an unfilteredpropertycache value for <name>"""
108 return name in vars(repo.unfiltered())
108 return name in vars(repo.unfiltered())
109
109
110 def unfilteredmethod(orig):
110 def unfilteredmethod(orig):
111 """decorate method that always need to be run on unfiltered version"""
111 """decorate method that always need to be run on unfiltered version"""
112 def wrapper(repo, *args, **kwargs):
112 def wrapper(repo, *args, **kwargs):
113 return orig(repo.unfiltered(), *args, **kwargs)
113 return orig(repo.unfiltered(), *args, **kwargs)
114 return wrapper
114 return wrapper
115
115
116 moderncaps = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle',
116 moderncaps = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle',
117 'unbundle'))
117 'unbundle'))
118 legacycaps = moderncaps.union(set(['changegroupsubset']))
118 legacycaps = moderncaps.union(set(['changegroupsubset']))
119
119
120 class localpeer(peer.peerrepository):
120 class localpeer(peer.peerrepository):
121 '''peer for a local repo; reflects only the most recent API'''
121 '''peer for a local repo; reflects only the most recent API'''
122
122
123 def __init__(self, repo, caps=None):
123 def __init__(self, repo, caps=None):
124 if caps is None:
124 if caps is None:
125 caps = moderncaps.copy()
125 caps = moderncaps.copy()
126 peer.peerrepository.__init__(self)
126 peer.peerrepository.__init__(self)
127 self._repo = repo.filtered('served')
127 self._repo = repo.filtered('served')
128 self.ui = repo.ui
128 self.ui = repo.ui
129 self._caps = repo._restrictcapabilities(caps)
129 self._caps = repo._restrictcapabilities(caps)
130 self.requirements = repo.requirements
130 self.requirements = repo.requirements
131 self.supportedformats = repo.supportedformats
131 self.supportedformats = repo.supportedformats
132
132
133 def close(self):
133 def close(self):
134 self._repo.close()
134 self._repo.close()
135
135
136 def _capabilities(self):
136 def _capabilities(self):
137 return self._caps
137 return self._caps
138
138
139 def local(self):
139 def local(self):
140 return self._repo
140 return self._repo
141
141
142 def canpush(self):
142 def canpush(self):
143 return True
143 return True
144
144
145 def url(self):
145 def url(self):
146 return self._repo.url()
146 return self._repo.url()
147
147
148 def lookup(self, key):
148 def lookup(self, key):
149 return self._repo.lookup(key)
149 return self._repo.lookup(key)
150
150
151 def branchmap(self):
151 def branchmap(self):
152 return self._repo.branchmap()
152 return self._repo.branchmap()
153
153
154 def heads(self):
154 def heads(self):
155 return self._repo.heads()
155 return self._repo.heads()
156
156
157 def known(self, nodes):
157 def known(self, nodes):
158 return self._repo.known(nodes)
158 return self._repo.known(nodes)
159
159
160 def getbundle(self, source, heads=None, common=None, bundlecaps=None,
160 def getbundle(self, source, heads=None, common=None, bundlecaps=None,
161 **kwargs):
161 **kwargs):
162 chunks = exchange.getbundlechunks(self._repo, source, heads=heads,
162 chunks = exchange.getbundlechunks(self._repo, source, heads=heads,
163 common=common, bundlecaps=bundlecaps,
163 common=common, bundlecaps=bundlecaps,
164 **kwargs)
164 **kwargs)
165 cb = util.chunkbuffer(chunks)
165 cb = util.chunkbuffer(chunks)
166
166
167 if bundlecaps is not None and 'HG20' in bundlecaps:
167 if bundlecaps is not None and 'HG20' in bundlecaps:
168 # When requesting a bundle2, getbundle returns a stream to make the
168 # When requesting a bundle2, getbundle returns a stream to make the
169 # wire level function happier. We need to build a proper object
169 # wire level function happier. We need to build a proper object
170 # from it in local peer.
170 # from it in local peer.
171 return bundle2.getunbundler(self.ui, cb)
171 return bundle2.getunbundler(self.ui, cb)
172 else:
172 else:
173 return changegroup.getunbundler('01', cb, None)
173 return changegroup.getunbundler('01', cb, None)
174
174
175 # TODO We might want to move the next two calls into legacypeer and add
175 # TODO We might want to move the next two calls into legacypeer and add
176 # unbundle instead.
176 # unbundle instead.
177
177
178 def unbundle(self, cg, heads, url):
178 def unbundle(self, cg, heads, url):
179 """apply a bundle on a repo
179 """apply a bundle on a repo
180
180
181 This function handles the repo locking itself."""
181 This function handles the repo locking itself."""
182 try:
182 try:
183 try:
183 try:
184 cg = exchange.readbundle(self.ui, cg, None)
184 cg = exchange.readbundle(self.ui, cg, None)
185 ret = exchange.unbundle(self._repo, cg, heads, 'push', url)
185 ret = exchange.unbundle(self._repo, cg, heads, 'push', url)
186 if util.safehasattr(ret, 'getchunks'):
186 if util.safehasattr(ret, 'getchunks'):
187 # This is a bundle20 object, turn it into an unbundler.
187 # This is a bundle20 object, turn it into an unbundler.
188 # This little dance should be dropped eventually when the
188 # This little dance should be dropped eventually when the
189 # API is finally improved.
189 # API is finally improved.
190 stream = util.chunkbuffer(ret.getchunks())
190 stream = util.chunkbuffer(ret.getchunks())
191 ret = bundle2.getunbundler(self.ui, stream)
191 ret = bundle2.getunbundler(self.ui, stream)
192 return ret
192 return ret
193 except Exception as exc:
193 except Exception as exc:
194 # If the exception contains output salvaged from a bundle2
194 # If the exception contains output salvaged from a bundle2
195 # reply, we need to make sure it is printed before continuing
195 # reply, we need to make sure it is printed before continuing
196 # to fail. So we build a bundle2 with such output and consume
196 # to fail. So we build a bundle2 with such output and consume
197 # it directly.
197 # it directly.
198 #
198 #
199 # This is not very elegant but allows a "simple" solution for
199 # This is not very elegant but allows a "simple" solution for
200 # issue4594
200 # issue4594
201 output = getattr(exc, '_bundle2salvagedoutput', ())
201 output = getattr(exc, '_bundle2salvagedoutput', ())
202 if output:
202 if output:
203 bundler = bundle2.bundle20(self._repo.ui)
203 bundler = bundle2.bundle20(self._repo.ui)
204 for out in output:
204 for out in output:
205 bundler.addpart(out)
205 bundler.addpart(out)
206 stream = util.chunkbuffer(bundler.getchunks())
206 stream = util.chunkbuffer(bundler.getchunks())
207 b = bundle2.getunbundler(self.ui, stream)
207 b = bundle2.getunbundler(self.ui, stream)
208 bundle2.processbundle(self._repo, b)
208 bundle2.processbundle(self._repo, b)
209 raise
209 raise
210 except error.PushRaced as exc:
210 except error.PushRaced as exc:
211 raise error.ResponseError(_('push failed:'), str(exc))
211 raise error.ResponseError(_('push failed:'), str(exc))
212
212
213 def lock(self):
213 def lock(self):
214 return self._repo.lock()
214 return self._repo.lock()
215
215
216 def addchangegroup(self, cg, source, url):
216 def addchangegroup(self, cg, source, url):
217 return cg.apply(self._repo, source, url)
217 return cg.apply(self._repo, source, url)
218
218
219 def pushkey(self, namespace, key, old, new):
219 def pushkey(self, namespace, key, old, new):
220 return self._repo.pushkey(namespace, key, old, new)
220 return self._repo.pushkey(namespace, key, old, new)
221
221
222 def listkeys(self, namespace):
222 def listkeys(self, namespace):
223 return self._repo.listkeys(namespace)
223 return self._repo.listkeys(namespace)
224
224
225 def debugwireargs(self, one, two, three=None, four=None, five=None):
225 def debugwireargs(self, one, two, three=None, four=None, five=None):
226 '''used to test argument passing over the wire'''
226 '''used to test argument passing over the wire'''
227 return "%s %s %s %s %s" % (one, two, three, four, five)
227 return "%s %s %s %s %s" % (one, two, three, four, five)
228
228
229 class locallegacypeer(localpeer):
229 class locallegacypeer(localpeer):
230 '''peer extension which implements legacy methods too; used for tests with
230 '''peer extension which implements legacy methods too; used for tests with
231 restricted capabilities'''
231 restricted capabilities'''
232
232
233 def __init__(self, repo):
233 def __init__(self, repo):
234 localpeer.__init__(self, repo, caps=legacycaps)
234 localpeer.__init__(self, repo, caps=legacycaps)
235
235
236 def branches(self, nodes):
236 def branches(self, nodes):
237 return self._repo.branches(nodes)
237 return self._repo.branches(nodes)
238
238
239 def between(self, pairs):
239 def between(self, pairs):
240 return self._repo.between(pairs)
240 return self._repo.between(pairs)
241
241
242 def changegroup(self, basenodes, source):
242 def changegroup(self, basenodes, source):
243 return changegroup.changegroup(self._repo, basenodes, source)
243 return changegroup.changegroup(self._repo, basenodes, source)
244
244
245 def changegroupsubset(self, bases, heads, source):
245 def changegroupsubset(self, bases, heads, source):
246 return changegroup.changegroupsubset(self._repo, bases, heads, source)
246 return changegroup.changegroupsubset(self._repo, bases, heads, source)
247
247
248 class localrepository(object):
248 class localrepository(object):
249
249
250 supportedformats = set(('revlogv1', 'generaldelta', 'treemanifest',
250 supportedformats = set(('revlogv1', 'generaldelta', 'treemanifest',
251 'manifestv2'))
251 'manifestv2'))
252 _basesupported = supportedformats | set(('store', 'fncache', 'shared',
252 _basesupported = supportedformats | set(('store', 'fncache', 'shared',
253 'relshared', 'dotencode'))
253 'relshared', 'dotencode'))
254 openerreqs = set(('revlogv1', 'generaldelta', 'treemanifest', 'manifestv2'))
254 openerreqs = set(('revlogv1', 'generaldelta', 'treemanifest', 'manifestv2'))
255 filtername = None
255 filtername = None
256
256
257 # a list of (ui, featureset) functions.
257 # a list of (ui, featureset) functions.
258 # only functions defined in module of enabled extensions are invoked
258 # only functions defined in module of enabled extensions are invoked
259 featuresetupfuncs = set()
259 featuresetupfuncs = set()
260
260
261 def __init__(self, baseui, path, create=False):
261 def __init__(self, baseui, path, create=False):
262 self.requirements = set()
262 self.requirements = set()
263 # wvfs: rooted at the repository root, used to access the working copy
263 # wvfs: rooted at the repository root, used to access the working copy
264 self.wvfs = vfsmod.vfs(path, expandpath=True, realpath=True)
264 self.wvfs = vfsmod.vfs(path, expandpath=True, realpath=True)
265 # vfs: rooted at .hg, used to access repo files outside of .hg/store
265 # vfs: rooted at .hg, used to access repo files outside of .hg/store
266 self.vfs = None
266 self.vfs = None
267 # svfs: usually rooted at .hg/store, used to access repository history
267 # svfs: usually rooted at .hg/store, used to access repository history
268 # If this is a shared repository, this vfs may point to another
268 # If this is a shared repository, this vfs may point to another
269 # repository's .hg/store directory.
269 # repository's .hg/store directory.
270 self.svfs = None
270 self.svfs = None
271 self.root = self.wvfs.base
271 self.root = self.wvfs.base
272 self.path = self.wvfs.join(".hg")
272 self.path = self.wvfs.join(".hg")
273 self.origroot = path
273 self.origroot = path
274 self.auditor = pathutil.pathauditor(self.root, self._checknested)
274 self.auditor = pathutil.pathauditor(self.root, self._checknested)
275 self.nofsauditor = pathutil.pathauditor(self.root, self._checknested,
275 self.nofsauditor = pathutil.pathauditor(self.root, self._checknested,
276 realfs=False)
276 realfs=False)
277 self.vfs = vfsmod.vfs(self.path)
277 self.vfs = vfsmod.vfs(self.path)
278 self.baseui = baseui
278 self.baseui = baseui
279 self.ui = baseui.copy()
279 self.ui = baseui.copy()
280 self.ui.copy = baseui.copy # prevent copying repo configuration
280 self.ui.copy = baseui.copy # prevent copying repo configuration
281 # A list of callback to shape the phase if no data were found.
281 # A list of callback to shape the phase if no data were found.
282 # Callback are in the form: func(repo, roots) --> processed root.
282 # Callback are in the form: func(repo, roots) --> processed root.
283 # This list it to be filled by extension during repo setup
283 # This list it to be filled by extension during repo setup
284 self._phasedefaults = []
284 self._phasedefaults = []
285 try:
285 try:
286 self.ui.readconfig(self.vfs.join("hgrc"), self.root)
286 self.ui.readconfig(self.vfs.join("hgrc"), self.root)
287 self._loadextensions()
287 self._loadextensions()
288 except IOError:
288 except IOError:
289 pass
289 pass
290
290
291 if self.featuresetupfuncs:
291 if self.featuresetupfuncs:
292 self.supported = set(self._basesupported) # use private copy
292 self.supported = set(self._basesupported) # use private copy
293 extmods = set(m.__name__ for n, m
293 extmods = set(m.__name__ for n, m
294 in extensions.extensions(self.ui))
294 in extensions.extensions(self.ui))
295 for setupfunc in self.featuresetupfuncs:
295 for setupfunc in self.featuresetupfuncs:
296 if setupfunc.__module__ in extmods:
296 if setupfunc.__module__ in extmods:
297 setupfunc(self.ui, self.supported)
297 setupfunc(self.ui, self.supported)
298 else:
298 else:
299 self.supported = self._basesupported
299 self.supported = self._basesupported
300 color.setup(self.ui)
300 color.setup(self.ui)
301
301
302 # Add compression engines.
302 # Add compression engines.
303 for name in util.compengines:
303 for name in util.compengines:
304 engine = util.compengines[name]
304 engine = util.compengines[name]
305 if engine.revlogheader():
305 if engine.revlogheader():
306 self.supported.add('exp-compression-%s' % name)
306 self.supported.add('exp-compression-%s' % name)
307
307
308 if not self.vfs.isdir():
308 if not self.vfs.isdir():
309 if create:
309 if create:
310 self.requirements = newreporequirements(self)
310 self.requirements = newreporequirements(self)
311
311
312 if not self.wvfs.exists():
312 if not self.wvfs.exists():
313 self.wvfs.makedirs()
313 self.wvfs.makedirs()
314 self.vfs.makedir(notindexed=True)
314 self.vfs.makedir(notindexed=True)
315
315
316 if 'store' in self.requirements:
316 if 'store' in self.requirements:
317 self.vfs.mkdir("store")
317 self.vfs.mkdir("store")
318
318
319 # create an invalid changelog
319 # create an invalid changelog
320 self.vfs.append(
320 self.vfs.append(
321 "00changelog.i",
321 "00changelog.i",
322 '\0\0\0\2' # represents revlogv2
322 '\0\0\0\2' # represents revlogv2
323 ' dummy changelog to prevent using the old repo layout'
323 ' dummy changelog to prevent using the old repo layout'
324 )
324 )
325 else:
325 else:
326 raise error.RepoError(_("repository %s not found") % path)
326 raise error.RepoError(_("repository %s not found") % path)
327 elif create:
327 elif create:
328 raise error.RepoError(_("repository %s already exists") % path)
328 raise error.RepoError(_("repository %s already exists") % path)
329 else:
329 else:
330 try:
330 try:
331 self.requirements = scmutil.readrequires(
331 self.requirements = scmutil.readrequires(
332 self.vfs, self.supported)
332 self.vfs, self.supported)
333 except IOError as inst:
333 except IOError as inst:
334 if inst.errno != errno.ENOENT:
334 if inst.errno != errno.ENOENT:
335 raise
335 raise
336
336
337 self.sharedpath = self.path
337 self.sharedpath = self.path
338 try:
338 try:
339 sharedpath = self.vfs.read("sharedpath").rstrip('\n')
339 sharedpath = self.vfs.read("sharedpath").rstrip('\n')
340 if 'relshared' in self.requirements:
340 if 'relshared' in self.requirements:
341 sharedpath = self.vfs.join(sharedpath)
341 sharedpath = self.vfs.join(sharedpath)
342 vfs = vfsmod.vfs(sharedpath, realpath=True)
342 vfs = vfsmod.vfs(sharedpath, realpath=True)
343 s = vfs.base
343 s = vfs.base
344 if not vfs.exists():
344 if not vfs.exists():
345 raise error.RepoError(
345 raise error.RepoError(
346 _('.hg/sharedpath points to nonexistent directory %s') % s)
346 _('.hg/sharedpath points to nonexistent directory %s') % s)
347 self.sharedpath = s
347 self.sharedpath = s
348 except IOError as inst:
348 except IOError as inst:
349 if inst.errno != errno.ENOENT:
349 if inst.errno != errno.ENOENT:
350 raise
350 raise
351
351
352 self.store = store.store(
352 self.store = store.store(
353 self.requirements, self.sharedpath, vfsmod.vfs)
353 self.requirements, self.sharedpath, vfsmod.vfs)
354 self.spath = self.store.path
354 self.spath = self.store.path
355 self.svfs = self.store.vfs
355 self.svfs = self.store.vfs
356 self.sjoin = self.store.join
356 self.sjoin = self.store.join
357 self.vfs.createmode = self.store.createmode
357 self.vfs.createmode = self.store.createmode
358 self._applyopenerreqs()
358 self._applyopenerreqs()
359 if create:
359 if create:
360 self._writerequirements()
360 self._writerequirements()
361
361
362 self._dirstatevalidatewarned = False
362 self._dirstatevalidatewarned = False
363
363
364 self._branchcaches = {}
364 self._branchcaches = {}
365 self._revbranchcache = None
365 self._revbranchcache = None
366 self.filterpats = {}
366 self.filterpats = {}
367 self._datafilters = {}
367 self._datafilters = {}
368 self._transref = self._lockref = self._wlockref = None
368 self._transref = self._lockref = self._wlockref = None
369
369
370 # A cache for various files under .hg/ that tracks file changes,
370 # A cache for various files under .hg/ that tracks file changes,
371 # (used by the filecache decorator)
371 # (used by the filecache decorator)
372 #
372 #
373 # Maps a property name to its util.filecacheentry
373 # Maps a property name to its util.filecacheentry
374 self._filecache = {}
374 self._filecache = {}
375
375
376 # hold sets of revision to be filtered
376 # hold sets of revision to be filtered
377 # should be cleared when something might have changed the filter value:
377 # should be cleared when something might have changed the filter value:
378 # - new changesets,
378 # - new changesets,
379 # - phase change,
379 # - phase change,
380 # - new obsolescence marker,
380 # - new obsolescence marker,
381 # - working directory parent change,
381 # - working directory parent change,
382 # - bookmark changes
382 # - bookmark changes
383 self.filteredrevcache = {}
383 self.filteredrevcache = {}
384
384
385 # generic mapping between names and nodes
385 # generic mapping between names and nodes
386 self.names = namespaces.namespaces()
386 self.names = namespaces.namespaces()
387
387
388 @property
388 @property
389 def wopener(self):
389 def wopener(self):
390 self.ui.deprecwarn("use 'repo.wvfs' instead of 'repo.wopener'", '4.2')
390 self.ui.deprecwarn("use 'repo.wvfs' instead of 'repo.wopener'", '4.2')
391 return self.wvfs
391 return self.wvfs
392
392
393 @property
393 @property
394 def opener(self):
394 def opener(self):
395 self.ui.deprecwarn("use 'repo.vfs' instead of 'repo.opener'", '4.2')
395 self.ui.deprecwarn("use 'repo.vfs' instead of 'repo.opener'", '4.2')
396 return self.vfs
396 return self.vfs
397
397
398 def close(self):
398 def close(self):
399 self._writecaches()
399 self._writecaches()
400
400
401 def _loadextensions(self):
401 def _loadextensions(self):
402 extensions.loadall(self.ui)
402 extensions.loadall(self.ui)
403
403
404 def _writecaches(self):
404 def _writecaches(self):
405 if self._revbranchcache:
405 if self._revbranchcache:
406 self._revbranchcache.write()
406 self._revbranchcache.write()
407
407
408 def _restrictcapabilities(self, caps):
408 def _restrictcapabilities(self, caps):
409 if self.ui.configbool('experimental', 'bundle2-advertise', True):
409 if self.ui.configbool('experimental', 'bundle2-advertise', True):
410 caps = set(caps)
410 caps = set(caps)
411 capsblob = bundle2.encodecaps(bundle2.getrepocaps(self))
411 capsblob = bundle2.encodecaps(bundle2.getrepocaps(self))
412 caps.add('bundle2=' + urlreq.quote(capsblob))
412 caps.add('bundle2=' + urlreq.quote(capsblob))
413 return caps
413 return caps
414
414
415 def _applyopenerreqs(self):
415 def _applyopenerreqs(self):
416 self.svfs.options = dict((r, 1) for r in self.requirements
416 self.svfs.options = dict((r, 1) for r in self.requirements
417 if r in self.openerreqs)
417 if r in self.openerreqs)
418 # experimental config: format.chunkcachesize
418 # experimental config: format.chunkcachesize
419 chunkcachesize = self.ui.configint('format', 'chunkcachesize')
419 chunkcachesize = self.ui.configint('format', 'chunkcachesize')
420 if chunkcachesize is not None:
420 if chunkcachesize is not None:
421 self.svfs.options['chunkcachesize'] = chunkcachesize
421 self.svfs.options['chunkcachesize'] = chunkcachesize
422 # experimental config: format.maxchainlen
422 # experimental config: format.maxchainlen
423 maxchainlen = self.ui.configint('format', 'maxchainlen')
423 maxchainlen = self.ui.configint('format', 'maxchainlen')
424 if maxchainlen is not None:
424 if maxchainlen is not None:
425 self.svfs.options['maxchainlen'] = maxchainlen
425 self.svfs.options['maxchainlen'] = maxchainlen
426 # experimental config: format.manifestcachesize
426 # experimental config: format.manifestcachesize
427 manifestcachesize = self.ui.configint('format', 'manifestcachesize')
427 manifestcachesize = self.ui.configint('format', 'manifestcachesize')
428 if manifestcachesize is not None:
428 if manifestcachesize is not None:
429 self.svfs.options['manifestcachesize'] = manifestcachesize
429 self.svfs.options['manifestcachesize'] = manifestcachesize
430 # experimental config: format.aggressivemergedeltas
430 # experimental config: format.aggressivemergedeltas
431 aggressivemergedeltas = self.ui.configbool('format',
431 aggressivemergedeltas = self.ui.configbool('format',
432 'aggressivemergedeltas', False)
432 'aggressivemergedeltas', False)
433 self.svfs.options['aggressivemergedeltas'] = aggressivemergedeltas
433 self.svfs.options['aggressivemergedeltas'] = aggressivemergedeltas
434 self.svfs.options['lazydeltabase'] = not scmutil.gddeltaconfig(self.ui)
434 self.svfs.options['lazydeltabase'] = not scmutil.gddeltaconfig(self.ui)
435 chainspan = self.ui.configbytes('experimental', 'maxdeltachainspan', -1)
436 if 0 <= chainspan:
437 self.svfs.options['maxdeltachainspan'] = chainspan
438
435
439 for r in self.requirements:
436 for r in self.requirements:
440 if r.startswith('exp-compression-'):
437 if r.startswith('exp-compression-'):
441 self.svfs.options['compengine'] = r[len('exp-compression-'):]
438 self.svfs.options['compengine'] = r[len('exp-compression-'):]
442
439
443 def _writerequirements(self):
440 def _writerequirements(self):
444 scmutil.writerequires(self.vfs, self.requirements)
441 scmutil.writerequires(self.vfs, self.requirements)
445
442
446 def _checknested(self, path):
443 def _checknested(self, path):
447 """Determine if path is a legal nested repository."""
444 """Determine if path is a legal nested repository."""
448 if not path.startswith(self.root):
445 if not path.startswith(self.root):
449 return False
446 return False
450 subpath = path[len(self.root) + 1:]
447 subpath = path[len(self.root) + 1:]
451 normsubpath = util.pconvert(subpath)
448 normsubpath = util.pconvert(subpath)
452
449
453 # XXX: Checking against the current working copy is wrong in
450 # XXX: Checking against the current working copy is wrong in
454 # the sense that it can reject things like
451 # the sense that it can reject things like
455 #
452 #
456 # $ hg cat -r 10 sub/x.txt
453 # $ hg cat -r 10 sub/x.txt
457 #
454 #
458 # if sub/ is no longer a subrepository in the working copy
455 # if sub/ is no longer a subrepository in the working copy
459 # parent revision.
456 # parent revision.
460 #
457 #
461 # However, it can of course also allow things that would have
458 # However, it can of course also allow things that would have
462 # been rejected before, such as the above cat command if sub/
459 # been rejected before, such as the above cat command if sub/
463 # is a subrepository now, but was a normal directory before.
460 # is a subrepository now, but was a normal directory before.
464 # The old path auditor would have rejected by mistake since it
461 # The old path auditor would have rejected by mistake since it
465 # panics when it sees sub/.hg/.
462 # panics when it sees sub/.hg/.
466 #
463 #
467 # All in all, checking against the working copy seems sensible
464 # All in all, checking against the working copy seems sensible
468 # since we want to prevent access to nested repositories on
465 # since we want to prevent access to nested repositories on
469 # the filesystem *now*.
466 # the filesystem *now*.
470 ctx = self[None]
467 ctx = self[None]
471 parts = util.splitpath(subpath)
468 parts = util.splitpath(subpath)
472 while parts:
469 while parts:
473 prefix = '/'.join(parts)
470 prefix = '/'.join(parts)
474 if prefix in ctx.substate:
471 if prefix in ctx.substate:
475 if prefix == normsubpath:
472 if prefix == normsubpath:
476 return True
473 return True
477 else:
474 else:
478 sub = ctx.sub(prefix)
475 sub = ctx.sub(prefix)
479 return sub.checknested(subpath[len(prefix) + 1:])
476 return sub.checknested(subpath[len(prefix) + 1:])
480 else:
477 else:
481 parts.pop()
478 parts.pop()
482 return False
479 return False
483
480
484 def peer(self):
481 def peer(self):
485 return localpeer(self) # not cached to avoid reference cycle
482 return localpeer(self) # not cached to avoid reference cycle
486
483
487 def unfiltered(self):
484 def unfiltered(self):
488 """Return unfiltered version of the repository
485 """Return unfiltered version of the repository
489
486
490 Intended to be overwritten by filtered repo."""
487 Intended to be overwritten by filtered repo."""
491 return self
488 return self
492
489
493 def filtered(self, name):
490 def filtered(self, name):
494 """Return a filtered version of a repository"""
491 """Return a filtered version of a repository"""
495 # build a new class with the mixin and the current class
492 # build a new class with the mixin and the current class
496 # (possibly subclass of the repo)
493 # (possibly subclass of the repo)
497 class filteredrepo(repoview.repoview, self.unfiltered().__class__):
494 class filteredrepo(repoview.repoview, self.unfiltered().__class__):
498 pass
495 pass
499 return filteredrepo(self, name)
496 return filteredrepo(self, name)
500
497
501 @repofilecache('bookmarks', 'bookmarks.current')
498 @repofilecache('bookmarks', 'bookmarks.current')
502 def _bookmarks(self):
499 def _bookmarks(self):
503 return bookmarks.bmstore(self)
500 return bookmarks.bmstore(self)
504
501
505 @property
502 @property
506 def _activebookmark(self):
503 def _activebookmark(self):
507 return self._bookmarks.active
504 return self._bookmarks.active
508
505
509 def bookmarkheads(self, bookmark):
506 def bookmarkheads(self, bookmark):
510 name = bookmark.split('@', 1)[0]
507 name = bookmark.split('@', 1)[0]
511 heads = []
508 heads = []
512 for mark, n in self._bookmarks.iteritems():
509 for mark, n in self._bookmarks.iteritems():
513 if mark.split('@', 1)[0] == name:
510 if mark.split('@', 1)[0] == name:
514 heads.append(n)
511 heads.append(n)
515 return heads
512 return heads
516
513
517 # _phaserevs and _phasesets depend on changelog. what we need is to
514 # _phaserevs and _phasesets depend on changelog. what we need is to
518 # call _phasecache.invalidate() if '00changelog.i' was changed, but it
515 # call _phasecache.invalidate() if '00changelog.i' was changed, but it
519 # can't be easily expressed in filecache mechanism.
516 # can't be easily expressed in filecache mechanism.
520 @storecache('phaseroots', '00changelog.i')
517 @storecache('phaseroots', '00changelog.i')
521 def _phasecache(self):
518 def _phasecache(self):
522 return phases.phasecache(self, self._phasedefaults)
519 return phases.phasecache(self, self._phasedefaults)
523
520
524 @storecache('obsstore')
521 @storecache('obsstore')
525 def obsstore(self):
522 def obsstore(self):
526 # read default format for new obsstore.
523 # read default format for new obsstore.
527 # developer config: format.obsstore-version
524 # developer config: format.obsstore-version
528 defaultformat = self.ui.configint('format', 'obsstore-version', None)
525 defaultformat = self.ui.configint('format', 'obsstore-version', None)
529 # rely on obsstore class default when possible.
526 # rely on obsstore class default when possible.
530 kwargs = {}
527 kwargs = {}
531 if defaultformat is not None:
528 if defaultformat is not None:
532 kwargs['defaultformat'] = defaultformat
529 kwargs['defaultformat'] = defaultformat
533 readonly = not obsolete.isenabled(self, obsolete.createmarkersopt)
530 readonly = not obsolete.isenabled(self, obsolete.createmarkersopt)
534 store = obsolete.obsstore(self.svfs, readonly=readonly,
531 store = obsolete.obsstore(self.svfs, readonly=readonly,
535 **kwargs)
532 **kwargs)
536 if store and readonly:
533 if store and readonly:
537 self.ui.warn(
534 self.ui.warn(
538 _('obsolete feature not enabled but %i markers found!\n')
535 _('obsolete feature not enabled but %i markers found!\n')
539 % len(list(store)))
536 % len(list(store)))
540 return store
537 return store
541
538
542 @storecache('00changelog.i')
539 @storecache('00changelog.i')
543 def changelog(self):
540 def changelog(self):
544 c = changelog.changelog(self.svfs)
541 c = changelog.changelog(self.svfs)
545 if txnutil.mayhavepending(self.root):
542 if txnutil.mayhavepending(self.root):
546 c.readpending('00changelog.i.a')
543 c.readpending('00changelog.i.a')
547 return c
544 return c
548
545
549 def _constructmanifest(self):
546 def _constructmanifest(self):
550 # This is a temporary function while we migrate from manifest to
547 # This is a temporary function while we migrate from manifest to
551 # manifestlog. It allows bundlerepo and unionrepo to intercept the
548 # manifestlog. It allows bundlerepo and unionrepo to intercept the
552 # manifest creation.
549 # manifest creation.
553 return manifest.manifestrevlog(self.svfs)
550 return manifest.manifestrevlog(self.svfs)
554
551
555 @storecache('00manifest.i')
552 @storecache('00manifest.i')
556 def manifestlog(self):
553 def manifestlog(self):
557 return manifest.manifestlog(self.svfs, self)
554 return manifest.manifestlog(self.svfs, self)
558
555
559 @repofilecache('dirstate')
556 @repofilecache('dirstate')
560 def dirstate(self):
557 def dirstate(self):
561 return dirstate.dirstate(self.vfs, self.ui, self.root,
558 return dirstate.dirstate(self.vfs, self.ui, self.root,
562 self._dirstatevalidate)
559 self._dirstatevalidate)
563
560
564 def _dirstatevalidate(self, node):
561 def _dirstatevalidate(self, node):
565 try:
562 try:
566 self.changelog.rev(node)
563 self.changelog.rev(node)
567 return node
564 return node
568 except error.LookupError:
565 except error.LookupError:
569 if not self._dirstatevalidatewarned:
566 if not self._dirstatevalidatewarned:
570 self._dirstatevalidatewarned = True
567 self._dirstatevalidatewarned = True
571 self.ui.warn(_("warning: ignoring unknown"
568 self.ui.warn(_("warning: ignoring unknown"
572 " working parent %s!\n") % short(node))
569 " working parent %s!\n") % short(node))
573 return nullid
570 return nullid
574
571
575 def __getitem__(self, changeid):
572 def __getitem__(self, changeid):
576 if changeid is None or changeid == wdirrev:
573 if changeid is None or changeid == wdirrev:
577 return context.workingctx(self)
574 return context.workingctx(self)
578 if isinstance(changeid, slice):
575 if isinstance(changeid, slice):
579 return [context.changectx(self, i)
576 return [context.changectx(self, i)
580 for i in xrange(*changeid.indices(len(self)))
577 for i in xrange(*changeid.indices(len(self)))
581 if i not in self.changelog.filteredrevs]
578 if i not in self.changelog.filteredrevs]
582 return context.changectx(self, changeid)
579 return context.changectx(self, changeid)
583
580
584 def __contains__(self, changeid):
581 def __contains__(self, changeid):
585 try:
582 try:
586 self[changeid]
583 self[changeid]
587 return True
584 return True
588 except error.RepoLookupError:
585 except error.RepoLookupError:
589 return False
586 return False
590
587
591 def __nonzero__(self):
588 def __nonzero__(self):
592 return True
589 return True
593
590
594 __bool__ = __nonzero__
591 __bool__ = __nonzero__
595
592
596 def __len__(self):
593 def __len__(self):
597 return len(self.changelog)
594 return len(self.changelog)
598
595
599 def __iter__(self):
596 def __iter__(self):
600 return iter(self.changelog)
597 return iter(self.changelog)
601
598
602 def revs(self, expr, *args):
599 def revs(self, expr, *args):
603 '''Find revisions matching a revset.
600 '''Find revisions matching a revset.
604
601
605 The revset is specified as a string ``expr`` that may contain
602 The revset is specified as a string ``expr`` that may contain
606 %-formatting to escape certain types. See ``revsetlang.formatspec``.
603 %-formatting to escape certain types. See ``revsetlang.formatspec``.
607
604
608 Revset aliases from the configuration are not expanded. To expand
605 Revset aliases from the configuration are not expanded. To expand
609 user aliases, consider calling ``scmutil.revrange()`` or
606 user aliases, consider calling ``scmutil.revrange()`` or
610 ``repo.anyrevs([expr], user=True)``.
607 ``repo.anyrevs([expr], user=True)``.
611
608
612 Returns a revset.abstractsmartset, which is a list-like interface
609 Returns a revset.abstractsmartset, which is a list-like interface
613 that contains integer revisions.
610 that contains integer revisions.
614 '''
611 '''
615 expr = revsetlang.formatspec(expr, *args)
612 expr = revsetlang.formatspec(expr, *args)
616 m = revset.match(None, expr)
613 m = revset.match(None, expr)
617 return m(self)
614 return m(self)
618
615
619 def set(self, expr, *args):
616 def set(self, expr, *args):
620 '''Find revisions matching a revset and emit changectx instances.
617 '''Find revisions matching a revset and emit changectx instances.
621
618
622 This is a convenience wrapper around ``revs()`` that iterates the
619 This is a convenience wrapper around ``revs()`` that iterates the
623 result and is a generator of changectx instances.
620 result and is a generator of changectx instances.
624
621
625 Revset aliases from the configuration are not expanded. To expand
622 Revset aliases from the configuration are not expanded. To expand
626 user aliases, consider calling ``scmutil.revrange()``.
623 user aliases, consider calling ``scmutil.revrange()``.
627 '''
624 '''
628 for r in self.revs(expr, *args):
625 for r in self.revs(expr, *args):
629 yield self[r]
626 yield self[r]
630
627
631 def anyrevs(self, specs, user=False):
628 def anyrevs(self, specs, user=False):
632 '''Find revisions matching one of the given revsets.
629 '''Find revisions matching one of the given revsets.
633
630
634 Revset aliases from the configuration are not expanded by default. To
631 Revset aliases from the configuration are not expanded by default. To
635 expand user aliases, specify ``user=True``.
632 expand user aliases, specify ``user=True``.
636 '''
633 '''
637 if user:
634 if user:
638 m = revset.matchany(self.ui, specs, repo=self)
635 m = revset.matchany(self.ui, specs, repo=self)
639 else:
636 else:
640 m = revset.matchany(None, specs)
637 m = revset.matchany(None, specs)
641 return m(self)
638 return m(self)
642
639
643 def url(self):
640 def url(self):
644 return 'file:' + self.root
641 return 'file:' + self.root
645
642
646 def hook(self, name, throw=False, **args):
643 def hook(self, name, throw=False, **args):
647 """Call a hook, passing this repo instance.
644 """Call a hook, passing this repo instance.
648
645
649 This a convenience method to aid invoking hooks. Extensions likely
646 This a convenience method to aid invoking hooks. Extensions likely
650 won't call this unless they have registered a custom hook or are
647 won't call this unless they have registered a custom hook or are
651 replacing code that is expected to call a hook.
648 replacing code that is expected to call a hook.
652 """
649 """
653 return hook.hook(self.ui, self, name, throw, **args)
650 return hook.hook(self.ui, self, name, throw, **args)
654
651
655 def tag(self, names, node, message, local, user, date, editor=False):
652 def tag(self, names, node, message, local, user, date, editor=False):
656 self.ui.deprecwarn("use 'tagsmod.tag' instead of 'repo.tag'", '4.2')
653 self.ui.deprecwarn("use 'tagsmod.tag' instead of 'repo.tag'", '4.2')
657 tagsmod.tag(self, names, node, message, local, user, date,
654 tagsmod.tag(self, names, node, message, local, user, date,
658 editor=editor)
655 editor=editor)
659
656
660 @filteredpropertycache
657 @filteredpropertycache
661 def _tagscache(self):
658 def _tagscache(self):
662 '''Returns a tagscache object that contains various tags related
659 '''Returns a tagscache object that contains various tags related
663 caches.'''
660 caches.'''
664
661
665 # This simplifies its cache management by having one decorated
662 # This simplifies its cache management by having one decorated
666 # function (this one) and the rest simply fetch things from it.
663 # function (this one) and the rest simply fetch things from it.
667 class tagscache(object):
664 class tagscache(object):
668 def __init__(self):
665 def __init__(self):
669 # These two define the set of tags for this repository. tags
666 # These two define the set of tags for this repository. tags
670 # maps tag name to node; tagtypes maps tag name to 'global' or
667 # maps tag name to node; tagtypes maps tag name to 'global' or
671 # 'local'. (Global tags are defined by .hgtags across all
668 # 'local'. (Global tags are defined by .hgtags across all
672 # heads, and local tags are defined in .hg/localtags.)
669 # heads, and local tags are defined in .hg/localtags.)
673 # They constitute the in-memory cache of tags.
670 # They constitute the in-memory cache of tags.
674 self.tags = self.tagtypes = None
671 self.tags = self.tagtypes = None
675
672
676 self.nodetagscache = self.tagslist = None
673 self.nodetagscache = self.tagslist = None
677
674
678 cache = tagscache()
675 cache = tagscache()
679 cache.tags, cache.tagtypes = self._findtags()
676 cache.tags, cache.tagtypes = self._findtags()
680
677
681 return cache
678 return cache
682
679
683 def tags(self):
680 def tags(self):
684 '''return a mapping of tag to node'''
681 '''return a mapping of tag to node'''
685 t = {}
682 t = {}
686 if self.changelog.filteredrevs:
683 if self.changelog.filteredrevs:
687 tags, tt = self._findtags()
684 tags, tt = self._findtags()
688 else:
685 else:
689 tags = self._tagscache.tags
686 tags = self._tagscache.tags
690 for k, v in tags.iteritems():
687 for k, v in tags.iteritems():
691 try:
688 try:
692 # ignore tags to unknown nodes
689 # ignore tags to unknown nodes
693 self.changelog.rev(v)
690 self.changelog.rev(v)
694 t[k] = v
691 t[k] = v
695 except (error.LookupError, ValueError):
692 except (error.LookupError, ValueError):
696 pass
693 pass
697 return t
694 return t
698
695
699 def _findtags(self):
696 def _findtags(self):
700 '''Do the hard work of finding tags. Return a pair of dicts
697 '''Do the hard work of finding tags. Return a pair of dicts
701 (tags, tagtypes) where tags maps tag name to node, and tagtypes
698 (tags, tagtypes) where tags maps tag name to node, and tagtypes
702 maps tag name to a string like \'global\' or \'local\'.
699 maps tag name to a string like \'global\' or \'local\'.
703 Subclasses or extensions are free to add their own tags, but
700 Subclasses or extensions are free to add their own tags, but
704 should be aware that the returned dicts will be retained for the
701 should be aware that the returned dicts will be retained for the
705 duration of the localrepo object.'''
702 duration of the localrepo object.'''
706
703
707 # XXX what tagtype should subclasses/extensions use? Currently
704 # XXX what tagtype should subclasses/extensions use? Currently
708 # mq and bookmarks add tags, but do not set the tagtype at all.
705 # mq and bookmarks add tags, but do not set the tagtype at all.
709 # Should each extension invent its own tag type? Should there
706 # Should each extension invent its own tag type? Should there
710 # be one tagtype for all such "virtual" tags? Or is the status
707 # be one tagtype for all such "virtual" tags? Or is the status
711 # quo fine?
708 # quo fine?
712
709
713
710
714 # map tag name to (node, hist)
711 # map tag name to (node, hist)
715 alltags = tagsmod.findglobaltags(self.ui, self)
712 alltags = tagsmod.findglobaltags(self.ui, self)
716 # map tag name to tag type
713 # map tag name to tag type
717 tagtypes = dict((tag, 'global') for tag in alltags)
714 tagtypes = dict((tag, 'global') for tag in alltags)
718
715
719 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
716 tagsmod.readlocaltags(self.ui, self, alltags, tagtypes)
720
717
721 # Build the return dicts. Have to re-encode tag names because
718 # Build the return dicts. Have to re-encode tag names because
722 # the tags module always uses UTF-8 (in order not to lose info
719 # the tags module always uses UTF-8 (in order not to lose info
723 # writing to the cache), but the rest of Mercurial wants them in
720 # writing to the cache), but the rest of Mercurial wants them in
724 # local encoding.
721 # local encoding.
725 tags = {}
722 tags = {}
726 for (name, (node, hist)) in alltags.iteritems():
723 for (name, (node, hist)) in alltags.iteritems():
727 if node != nullid:
724 if node != nullid:
728 tags[encoding.tolocal(name)] = node
725 tags[encoding.tolocal(name)] = node
729 tags['tip'] = self.changelog.tip()
726 tags['tip'] = self.changelog.tip()
730 tagtypes = dict([(encoding.tolocal(name), value)
727 tagtypes = dict([(encoding.tolocal(name), value)
731 for (name, value) in tagtypes.iteritems()])
728 for (name, value) in tagtypes.iteritems()])
732 return (tags, tagtypes)
729 return (tags, tagtypes)
733
730
734 def tagtype(self, tagname):
731 def tagtype(self, tagname):
735 '''
732 '''
736 return the type of the given tag. result can be:
733 return the type of the given tag. result can be:
737
734
738 'local' : a local tag
735 'local' : a local tag
739 'global' : a global tag
736 'global' : a global tag
740 None : tag does not exist
737 None : tag does not exist
741 '''
738 '''
742
739
743 return self._tagscache.tagtypes.get(tagname)
740 return self._tagscache.tagtypes.get(tagname)
744
741
745 def tagslist(self):
742 def tagslist(self):
746 '''return a list of tags ordered by revision'''
743 '''return a list of tags ordered by revision'''
747 if not self._tagscache.tagslist:
744 if not self._tagscache.tagslist:
748 l = []
745 l = []
749 for t, n in self.tags().iteritems():
746 for t, n in self.tags().iteritems():
750 l.append((self.changelog.rev(n), t, n))
747 l.append((self.changelog.rev(n), t, n))
751 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
748 self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
752
749
753 return self._tagscache.tagslist
750 return self._tagscache.tagslist
754
751
755 def nodetags(self, node):
752 def nodetags(self, node):
756 '''return the tags associated with a node'''
753 '''return the tags associated with a node'''
757 if not self._tagscache.nodetagscache:
754 if not self._tagscache.nodetagscache:
758 nodetagscache = {}
755 nodetagscache = {}
759 for t, n in self._tagscache.tags.iteritems():
756 for t, n in self._tagscache.tags.iteritems():
760 nodetagscache.setdefault(n, []).append(t)
757 nodetagscache.setdefault(n, []).append(t)
761 for tags in nodetagscache.itervalues():
758 for tags in nodetagscache.itervalues():
762 tags.sort()
759 tags.sort()
763 self._tagscache.nodetagscache = nodetagscache
760 self._tagscache.nodetagscache = nodetagscache
764 return self._tagscache.nodetagscache.get(node, [])
761 return self._tagscache.nodetagscache.get(node, [])
765
762
766 def nodebookmarks(self, node):
763 def nodebookmarks(self, node):
767 """return the list of bookmarks pointing to the specified node"""
764 """return the list of bookmarks pointing to the specified node"""
768 marks = []
765 marks = []
769 for bookmark, n in self._bookmarks.iteritems():
766 for bookmark, n in self._bookmarks.iteritems():
770 if n == node:
767 if n == node:
771 marks.append(bookmark)
768 marks.append(bookmark)
772 return sorted(marks)
769 return sorted(marks)
773
770
774 def branchmap(self):
771 def branchmap(self):
775 '''returns a dictionary {branch: [branchheads]} with branchheads
772 '''returns a dictionary {branch: [branchheads]} with branchheads
776 ordered by increasing revision number'''
773 ordered by increasing revision number'''
777 branchmap.updatecache(self)
774 branchmap.updatecache(self)
778 return self._branchcaches[self.filtername]
775 return self._branchcaches[self.filtername]
779
776
780 @unfilteredmethod
777 @unfilteredmethod
781 def revbranchcache(self):
778 def revbranchcache(self):
782 if not self._revbranchcache:
779 if not self._revbranchcache:
783 self._revbranchcache = branchmap.revbranchcache(self.unfiltered())
780 self._revbranchcache = branchmap.revbranchcache(self.unfiltered())
784 return self._revbranchcache
781 return self._revbranchcache
785
782
786 def branchtip(self, branch, ignoremissing=False):
783 def branchtip(self, branch, ignoremissing=False):
787 '''return the tip node for a given branch
784 '''return the tip node for a given branch
788
785
789 If ignoremissing is True, then this method will not raise an error.
786 If ignoremissing is True, then this method will not raise an error.
790 This is helpful for callers that only expect None for a missing branch
787 This is helpful for callers that only expect None for a missing branch
791 (e.g. namespace).
788 (e.g. namespace).
792
789
793 '''
790 '''
794 try:
791 try:
795 return self.branchmap().branchtip(branch)
792 return self.branchmap().branchtip(branch)
796 except KeyError:
793 except KeyError:
797 if not ignoremissing:
794 if not ignoremissing:
798 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
795 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
799 else:
796 else:
800 pass
797 pass
801
798
802 def lookup(self, key):
799 def lookup(self, key):
803 return self[key].node()
800 return self[key].node()
804
801
805 def lookupbranch(self, key, remote=None):
802 def lookupbranch(self, key, remote=None):
806 repo = remote or self
803 repo = remote or self
807 if key in repo.branchmap():
804 if key in repo.branchmap():
808 return key
805 return key
809
806
810 repo = (remote and remote.local()) and remote or self
807 repo = (remote and remote.local()) and remote or self
811 return repo[key].branch()
808 return repo[key].branch()
812
809
813 def known(self, nodes):
810 def known(self, nodes):
814 cl = self.changelog
811 cl = self.changelog
815 nm = cl.nodemap
812 nm = cl.nodemap
816 filtered = cl.filteredrevs
813 filtered = cl.filteredrevs
817 result = []
814 result = []
818 for n in nodes:
815 for n in nodes:
819 r = nm.get(n)
816 r = nm.get(n)
820 resp = not (r is None or r in filtered)
817 resp = not (r is None or r in filtered)
821 result.append(resp)
818 result.append(resp)
822 return result
819 return result
823
820
824 def local(self):
821 def local(self):
825 return self
822 return self
826
823
827 def publishing(self):
824 def publishing(self):
828 # it's safe (and desirable) to trust the publish flag unconditionally
825 # it's safe (and desirable) to trust the publish flag unconditionally
829 # so that we don't finalize changes shared between users via ssh or nfs
826 # so that we don't finalize changes shared between users via ssh or nfs
830 return self.ui.configbool('phases', 'publish', True, untrusted=True)
827 return self.ui.configbool('phases', 'publish', True, untrusted=True)
831
828
832 def cancopy(self):
829 def cancopy(self):
833 # so statichttprepo's override of local() works
830 # so statichttprepo's override of local() works
834 if not self.local():
831 if not self.local():
835 return False
832 return False
836 if not self.publishing():
833 if not self.publishing():
837 return True
834 return True
838 # if publishing we can't copy if there is filtered content
835 # if publishing we can't copy if there is filtered content
839 return not self.filtered('visible').changelog.filteredrevs
836 return not self.filtered('visible').changelog.filteredrevs
840
837
841 def shared(self):
838 def shared(self):
842 '''the type of shared repository (None if not shared)'''
839 '''the type of shared repository (None if not shared)'''
843 if self.sharedpath != self.path:
840 if self.sharedpath != self.path:
844 return 'store'
841 return 'store'
845 return None
842 return None
846
843
847 def join(self, f, *insidef):
844 def join(self, f, *insidef):
848 self.ui.deprecwarn("use 'repo.vfs.join' instead of 'repo.join'", '4.2')
845 self.ui.deprecwarn("use 'repo.vfs.join' instead of 'repo.join'", '4.2')
849 return self.vfs.join(os.path.join(f, *insidef))
846 return self.vfs.join(os.path.join(f, *insidef))
850
847
851 def wjoin(self, f, *insidef):
848 def wjoin(self, f, *insidef):
852 return self.vfs.reljoin(self.root, f, *insidef)
849 return self.vfs.reljoin(self.root, f, *insidef)
853
850
854 def file(self, f):
851 def file(self, f):
855 if f[0] == '/':
852 if f[0] == '/':
856 f = f[1:]
853 f = f[1:]
857 return filelog.filelog(self.svfs, f)
854 return filelog.filelog(self.svfs, f)
858
855
859 def changectx(self, changeid):
856 def changectx(self, changeid):
860 return self[changeid]
857 return self[changeid]
861
858
862 def setparents(self, p1, p2=nullid):
859 def setparents(self, p1, p2=nullid):
863 self.dirstate.beginparentchange()
860 self.dirstate.beginparentchange()
864 copies = self.dirstate.setparents(p1, p2)
861 copies = self.dirstate.setparents(p1, p2)
865 pctx = self[p1]
862 pctx = self[p1]
866 if copies:
863 if copies:
867 # Adjust copy records, the dirstate cannot do it, it
864 # Adjust copy records, the dirstate cannot do it, it
868 # requires access to parents manifests. Preserve them
865 # requires access to parents manifests. Preserve them
869 # only for entries added to first parent.
866 # only for entries added to first parent.
870 for f in copies:
867 for f in copies:
871 if f not in pctx and copies[f] in pctx:
868 if f not in pctx and copies[f] in pctx:
872 self.dirstate.copy(copies[f], f)
869 self.dirstate.copy(copies[f], f)
873 if p2 == nullid:
870 if p2 == nullid:
874 for f, s in sorted(self.dirstate.copies().items()):
871 for f, s in sorted(self.dirstate.copies().items()):
875 if f not in pctx and s not in pctx:
872 if f not in pctx and s not in pctx:
876 self.dirstate.copy(None, f)
873 self.dirstate.copy(None, f)
877 self.dirstate.endparentchange()
874 self.dirstate.endparentchange()
878
875
879 def filectx(self, path, changeid=None, fileid=None):
876 def filectx(self, path, changeid=None, fileid=None):
880 """changeid can be a changeset revision, node, or tag.
877 """changeid can be a changeset revision, node, or tag.
881 fileid can be a file revision or node."""
878 fileid can be a file revision or node."""
882 return context.filectx(self, path, changeid, fileid)
879 return context.filectx(self, path, changeid, fileid)
883
880
884 def getcwd(self):
881 def getcwd(self):
885 return self.dirstate.getcwd()
882 return self.dirstate.getcwd()
886
883
887 def pathto(self, f, cwd=None):
884 def pathto(self, f, cwd=None):
888 return self.dirstate.pathto(f, cwd)
885 return self.dirstate.pathto(f, cwd)
889
886
890 def wfile(self, f, mode='r'):
887 def wfile(self, f, mode='r'):
891 self.ui.deprecwarn("use 'repo.wvfs' instead of 'repo.wfile'", '4.2')
888 self.ui.deprecwarn("use 'repo.wvfs' instead of 'repo.wfile'", '4.2')
892 return self.wvfs(f, mode)
889 return self.wvfs(f, mode)
893
890
894 def _link(self, f):
891 def _link(self, f):
895 self.ui.deprecwarn("use 'repo.wvfs.islink' instead of 'repo._link'",
892 self.ui.deprecwarn("use 'repo.wvfs.islink' instead of 'repo._link'",
896 '4.2')
893 '4.2')
897 return self.wvfs.islink(f)
894 return self.wvfs.islink(f)
898
895
899 def _loadfilter(self, filter):
896 def _loadfilter(self, filter):
900 if filter not in self.filterpats:
897 if filter not in self.filterpats:
901 l = []
898 l = []
902 for pat, cmd in self.ui.configitems(filter):
899 for pat, cmd in self.ui.configitems(filter):
903 if cmd == '!':
900 if cmd == '!':
904 continue
901 continue
905 mf = matchmod.match(self.root, '', [pat])
902 mf = matchmod.match(self.root, '', [pat])
906 fn = None
903 fn = None
907 params = cmd
904 params = cmd
908 for name, filterfn in self._datafilters.iteritems():
905 for name, filterfn in self._datafilters.iteritems():
909 if cmd.startswith(name):
906 if cmd.startswith(name):
910 fn = filterfn
907 fn = filterfn
911 params = cmd[len(name):].lstrip()
908 params = cmd[len(name):].lstrip()
912 break
909 break
913 if not fn:
910 if not fn:
914 fn = lambda s, c, **kwargs: util.filter(s, c)
911 fn = lambda s, c, **kwargs: util.filter(s, c)
915 # Wrap old filters not supporting keyword arguments
912 # Wrap old filters not supporting keyword arguments
916 if not inspect.getargspec(fn)[2]:
913 if not inspect.getargspec(fn)[2]:
917 oldfn = fn
914 oldfn = fn
918 fn = lambda s, c, **kwargs: oldfn(s, c)
915 fn = lambda s, c, **kwargs: oldfn(s, c)
919 l.append((mf, fn, params))
916 l.append((mf, fn, params))
920 self.filterpats[filter] = l
917 self.filterpats[filter] = l
921 return self.filterpats[filter]
918 return self.filterpats[filter]
922
919
923 def _filter(self, filterpats, filename, data):
920 def _filter(self, filterpats, filename, data):
924 for mf, fn, cmd in filterpats:
921 for mf, fn, cmd in filterpats:
925 if mf(filename):
922 if mf(filename):
926 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
923 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
927 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
924 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
928 break
925 break
929
926
930 return data
927 return data
931
928
932 @unfilteredpropertycache
929 @unfilteredpropertycache
933 def _encodefilterpats(self):
930 def _encodefilterpats(self):
934 return self._loadfilter('encode')
931 return self._loadfilter('encode')
935
932
936 @unfilteredpropertycache
933 @unfilteredpropertycache
937 def _decodefilterpats(self):
934 def _decodefilterpats(self):
938 return self._loadfilter('decode')
935 return self._loadfilter('decode')
939
936
940 def adddatafilter(self, name, filter):
937 def adddatafilter(self, name, filter):
941 self._datafilters[name] = filter
938 self._datafilters[name] = filter
942
939
943 def wread(self, filename):
940 def wread(self, filename):
944 if self.wvfs.islink(filename):
941 if self.wvfs.islink(filename):
945 data = self.wvfs.readlink(filename)
942 data = self.wvfs.readlink(filename)
946 else:
943 else:
947 data = self.wvfs.read(filename)
944 data = self.wvfs.read(filename)
948 return self._filter(self._encodefilterpats, filename, data)
945 return self._filter(self._encodefilterpats, filename, data)
949
946
950 def wwrite(self, filename, data, flags, backgroundclose=False):
947 def wwrite(self, filename, data, flags, backgroundclose=False):
951 """write ``data`` into ``filename`` in the working directory
948 """write ``data`` into ``filename`` in the working directory
952
949
953 This returns length of written (maybe decoded) data.
950 This returns length of written (maybe decoded) data.
954 """
951 """
955 data = self._filter(self._decodefilterpats, filename, data)
952 data = self._filter(self._decodefilterpats, filename, data)
956 if 'l' in flags:
953 if 'l' in flags:
957 self.wvfs.symlink(data, filename)
954 self.wvfs.symlink(data, filename)
958 else:
955 else:
959 self.wvfs.write(filename, data, backgroundclose=backgroundclose)
956 self.wvfs.write(filename, data, backgroundclose=backgroundclose)
960 if 'x' in flags:
957 if 'x' in flags:
961 self.wvfs.setflags(filename, False, True)
958 self.wvfs.setflags(filename, False, True)
962 return len(data)
959 return len(data)
963
960
964 def wwritedata(self, filename, data):
961 def wwritedata(self, filename, data):
965 return self._filter(self._decodefilterpats, filename, data)
962 return self._filter(self._decodefilterpats, filename, data)
966
963
967 def currenttransaction(self):
964 def currenttransaction(self):
968 """return the current transaction or None if non exists"""
965 """return the current transaction or None if non exists"""
969 if self._transref:
966 if self._transref:
970 tr = self._transref()
967 tr = self._transref()
971 else:
968 else:
972 tr = None
969 tr = None
973
970
974 if tr and tr.running():
971 if tr and tr.running():
975 return tr
972 return tr
976 return None
973 return None
977
974
978 def transaction(self, desc, report=None):
975 def transaction(self, desc, report=None):
979 if (self.ui.configbool('devel', 'all-warnings')
976 if (self.ui.configbool('devel', 'all-warnings')
980 or self.ui.configbool('devel', 'check-locks')):
977 or self.ui.configbool('devel', 'check-locks')):
981 if self._currentlock(self._lockref) is None:
978 if self._currentlock(self._lockref) is None:
982 raise error.ProgrammingError('transaction requires locking')
979 raise error.ProgrammingError('transaction requires locking')
983 tr = self.currenttransaction()
980 tr = self.currenttransaction()
984 if tr is not None:
981 if tr is not None:
985 return tr.nest()
982 return tr.nest()
986
983
987 # abort here if the journal already exists
984 # abort here if the journal already exists
988 if self.svfs.exists("journal"):
985 if self.svfs.exists("journal"):
989 raise error.RepoError(
986 raise error.RepoError(
990 _("abandoned transaction found"),
987 _("abandoned transaction found"),
991 hint=_("run 'hg recover' to clean up transaction"))
988 hint=_("run 'hg recover' to clean up transaction"))
992
989
993 idbase = "%.40f#%f" % (random.random(), time.time())
990 idbase = "%.40f#%f" % (random.random(), time.time())
994 ha = hex(hashlib.sha1(idbase).digest())
991 ha = hex(hashlib.sha1(idbase).digest())
995 txnid = 'TXN:' + ha
992 txnid = 'TXN:' + ha
996 self.hook('pretxnopen', throw=True, txnname=desc, txnid=txnid)
993 self.hook('pretxnopen', throw=True, txnname=desc, txnid=txnid)
997
994
998 self._writejournal(desc)
995 self._writejournal(desc)
999 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
996 renames = [(vfs, x, undoname(x)) for vfs, x in self._journalfiles()]
1000 if report:
997 if report:
1001 rp = report
998 rp = report
1002 else:
999 else:
1003 rp = self.ui.warn
1000 rp = self.ui.warn
1004 vfsmap = {'plain': self.vfs} # root of .hg/
1001 vfsmap = {'plain': self.vfs} # root of .hg/
1005 # we must avoid cyclic reference between repo and transaction.
1002 # we must avoid cyclic reference between repo and transaction.
1006 reporef = weakref.ref(self)
1003 reporef = weakref.ref(self)
1007 # Code to track tag movement
1004 # Code to track tag movement
1008 #
1005 #
1009 # Since tags are all handled as file content, it is actually quite hard
1006 # Since tags are all handled as file content, it is actually quite hard
1010 # to track these movement from a code perspective. So we fallback to a
1007 # to track these movement from a code perspective. So we fallback to a
1011 # tracking at the repository level. One could envision to track changes
1008 # tracking at the repository level. One could envision to track changes
1012 # to the '.hgtags' file through changegroup apply but that fails to
1009 # to the '.hgtags' file through changegroup apply but that fails to
1013 # cope with case where transaction expose new heads without changegroup
1010 # cope with case where transaction expose new heads without changegroup
1014 # being involved (eg: phase movement).
1011 # being involved (eg: phase movement).
1015 #
1012 #
1016 # For now, We gate the feature behind a flag since this likely comes
1013 # For now, We gate the feature behind a flag since this likely comes
1017 # with performance impacts. The current code run more often than needed
1014 # with performance impacts. The current code run more often than needed
1018 # and do not use caches as much as it could. The current focus is on
1015 # and do not use caches as much as it could. The current focus is on
1019 # the behavior of the feature so we disable it by default. The flag
1016 # the behavior of the feature so we disable it by default. The flag
1020 # will be removed when we are happy with the performance impact.
1017 # will be removed when we are happy with the performance impact.
1021 #
1018 #
1022 # Once this feature is no longer experimental move the following
1019 # Once this feature is no longer experimental move the following
1023 # documentation to the appropriate help section:
1020 # documentation to the appropriate help section:
1024 #
1021 #
1025 # The ``HG_TAG_MOVED`` variable will be set if the transaction touched
1022 # The ``HG_TAG_MOVED`` variable will be set if the transaction touched
1026 # tags (new or changed or deleted tags). In addition the details of
1023 # tags (new or changed or deleted tags). In addition the details of
1027 # these changes are made available in a file at:
1024 # these changes are made available in a file at:
1028 # ``REPOROOT/.hg/changes/tags.changes``.
1025 # ``REPOROOT/.hg/changes/tags.changes``.
1029 # Make sure you check for HG_TAG_MOVED before reading that file as it
1026 # Make sure you check for HG_TAG_MOVED before reading that file as it
1030 # might exist from a previous transaction even if no tag were touched
1027 # might exist from a previous transaction even if no tag were touched
1031 # in this one. Changes are recorded in a line base format::
1028 # in this one. Changes are recorded in a line base format::
1032 #
1029 #
1033 # <action> <hex-node> <tag-name>\n
1030 # <action> <hex-node> <tag-name>\n
1034 #
1031 #
1035 # Actions are defined as follow:
1032 # Actions are defined as follow:
1036 # "-R": tag is removed,
1033 # "-R": tag is removed,
1037 # "+A": tag is added,
1034 # "+A": tag is added,
1038 # "-M": tag is moved (old value),
1035 # "-M": tag is moved (old value),
1039 # "+M": tag is moved (new value),
1036 # "+M": tag is moved (new value),
1040 tracktags = lambda x: None
1037 tracktags = lambda x: None
1041 # experimental config: experimental.hook-track-tags
1038 # experimental config: experimental.hook-track-tags
1042 shouldtracktags = self.ui.configbool('experimental', 'hook-track-tags',
1039 shouldtracktags = self.ui.configbool('experimental', 'hook-track-tags',
1043 False)
1040 False)
1044 if desc != 'strip' and shouldtracktags:
1041 if desc != 'strip' and shouldtracktags:
1045 oldheads = self.changelog.headrevs()
1042 oldheads = self.changelog.headrevs()
1046 def tracktags(tr2):
1043 def tracktags(tr2):
1047 repo = reporef()
1044 repo = reporef()
1048 oldfnodes = tagsmod.fnoderevs(repo.ui, repo, oldheads)
1045 oldfnodes = tagsmod.fnoderevs(repo.ui, repo, oldheads)
1049 newheads = repo.changelog.headrevs()
1046 newheads = repo.changelog.headrevs()
1050 newfnodes = tagsmod.fnoderevs(repo.ui, repo, newheads)
1047 newfnodes = tagsmod.fnoderevs(repo.ui, repo, newheads)
1051 # notes: we compare lists here.
1048 # notes: we compare lists here.
1052 # As we do it only once buiding set would not be cheaper
1049 # As we do it only once buiding set would not be cheaper
1053 changes = tagsmod.difftags(repo.ui, repo, oldfnodes, newfnodes)
1050 changes = tagsmod.difftags(repo.ui, repo, oldfnodes, newfnodes)
1054 if changes:
1051 if changes:
1055 tr2.hookargs['tag_moved'] = '1'
1052 tr2.hookargs['tag_moved'] = '1'
1056 with repo.vfs('changes/tags.changes', 'w',
1053 with repo.vfs('changes/tags.changes', 'w',
1057 atomictemp=True) as changesfile:
1054 atomictemp=True) as changesfile:
1058 # note: we do not register the file to the transaction
1055 # note: we do not register the file to the transaction
1059 # because we needs it to still exist on the transaction
1056 # because we needs it to still exist on the transaction
1060 # is close (for txnclose hooks)
1057 # is close (for txnclose hooks)
1061 tagsmod.writediff(changesfile, changes)
1058 tagsmod.writediff(changesfile, changes)
1062 def validate(tr2):
1059 def validate(tr2):
1063 """will run pre-closing hooks"""
1060 """will run pre-closing hooks"""
1064 # XXX the transaction API is a bit lacking here so we take a hacky
1061 # XXX the transaction API is a bit lacking here so we take a hacky
1065 # path for now
1062 # path for now
1066 #
1063 #
1067 # We cannot add this as a "pending" hooks since the 'tr.hookargs'
1064 # We cannot add this as a "pending" hooks since the 'tr.hookargs'
1068 # dict is copied before these run. In addition we needs the data
1065 # dict is copied before these run. In addition we needs the data
1069 # available to in memory hooks too.
1066 # available to in memory hooks too.
1070 #
1067 #
1071 # Moreover, we also need to make sure this runs before txnclose
1068 # Moreover, we also need to make sure this runs before txnclose
1072 # hooks and there is no "pending" mechanism that would execute
1069 # hooks and there is no "pending" mechanism that would execute
1073 # logic only if hooks are about to run.
1070 # logic only if hooks are about to run.
1074 #
1071 #
1075 # Fixing this limitation of the transaction is also needed to track
1072 # Fixing this limitation of the transaction is also needed to track
1076 # other families of changes (bookmarks, phases, obsolescence).
1073 # other families of changes (bookmarks, phases, obsolescence).
1077 #
1074 #
1078 # This will have to be fixed before we remove the experimental
1075 # This will have to be fixed before we remove the experimental
1079 # gating.
1076 # gating.
1080 tracktags(tr2)
1077 tracktags(tr2)
1081 reporef().hook('pretxnclose', throw=True,
1078 reporef().hook('pretxnclose', throw=True,
1082 txnname=desc, **pycompat.strkwargs(tr.hookargs))
1079 txnname=desc, **pycompat.strkwargs(tr.hookargs))
1083 def releasefn(tr, success):
1080 def releasefn(tr, success):
1084 repo = reporef()
1081 repo = reporef()
1085 if success:
1082 if success:
1086 # this should be explicitly invoked here, because
1083 # this should be explicitly invoked here, because
1087 # in-memory changes aren't written out at closing
1084 # in-memory changes aren't written out at closing
1088 # transaction, if tr.addfilegenerator (via
1085 # transaction, if tr.addfilegenerator (via
1089 # dirstate.write or so) isn't invoked while
1086 # dirstate.write or so) isn't invoked while
1090 # transaction running
1087 # transaction running
1091 repo.dirstate.write(None)
1088 repo.dirstate.write(None)
1092 else:
1089 else:
1093 # discard all changes (including ones already written
1090 # discard all changes (including ones already written
1094 # out) in this transaction
1091 # out) in this transaction
1095 repo.dirstate.restorebackup(None, prefix='journal.')
1092 repo.dirstate.restorebackup(None, prefix='journal.')
1096
1093
1097 repo.invalidate(clearfilecache=True)
1094 repo.invalidate(clearfilecache=True)
1098
1095
1099 tr = transaction.transaction(rp, self.svfs, vfsmap,
1096 tr = transaction.transaction(rp, self.svfs, vfsmap,
1100 "journal",
1097 "journal",
1101 "undo",
1098 "undo",
1102 aftertrans(renames),
1099 aftertrans(renames),
1103 self.store.createmode,
1100 self.store.createmode,
1104 validator=validate,
1101 validator=validate,
1105 releasefn=releasefn)
1102 releasefn=releasefn)
1106
1103
1107 tr.hookargs['txnid'] = txnid
1104 tr.hookargs['txnid'] = txnid
1108 # note: writing the fncache only during finalize mean that the file is
1105 # note: writing the fncache only during finalize mean that the file is
1109 # outdated when running hooks. As fncache is used for streaming clone,
1106 # outdated when running hooks. As fncache is used for streaming clone,
1110 # this is not expected to break anything that happen during the hooks.
1107 # this is not expected to break anything that happen during the hooks.
1111 tr.addfinalize('flush-fncache', self.store.write)
1108 tr.addfinalize('flush-fncache', self.store.write)
1112 def txnclosehook(tr2):
1109 def txnclosehook(tr2):
1113 """To be run if transaction is successful, will schedule a hook run
1110 """To be run if transaction is successful, will schedule a hook run
1114 """
1111 """
1115 # Don't reference tr2 in hook() so we don't hold a reference.
1112 # Don't reference tr2 in hook() so we don't hold a reference.
1116 # This reduces memory consumption when there are multiple
1113 # This reduces memory consumption when there are multiple
1117 # transactions per lock. This can likely go away if issue5045
1114 # transactions per lock. This can likely go away if issue5045
1118 # fixes the function accumulation.
1115 # fixes the function accumulation.
1119 hookargs = tr2.hookargs
1116 hookargs = tr2.hookargs
1120
1117
1121 def hook():
1118 def hook():
1122 reporef().hook('txnclose', throw=False, txnname=desc,
1119 reporef().hook('txnclose', throw=False, txnname=desc,
1123 **pycompat.strkwargs(hookargs))
1120 **pycompat.strkwargs(hookargs))
1124 reporef()._afterlock(hook)
1121 reporef()._afterlock(hook)
1125 tr.addfinalize('txnclose-hook', txnclosehook)
1122 tr.addfinalize('txnclose-hook', txnclosehook)
1126 def txnaborthook(tr2):
1123 def txnaborthook(tr2):
1127 """To be run if transaction is aborted
1124 """To be run if transaction is aborted
1128 """
1125 """
1129 reporef().hook('txnabort', throw=False, txnname=desc,
1126 reporef().hook('txnabort', throw=False, txnname=desc,
1130 **tr2.hookargs)
1127 **tr2.hookargs)
1131 tr.addabort('txnabort-hook', txnaborthook)
1128 tr.addabort('txnabort-hook', txnaborthook)
1132 # avoid eager cache invalidation. in-memory data should be identical
1129 # avoid eager cache invalidation. in-memory data should be identical
1133 # to stored data if transaction has no error.
1130 # to stored data if transaction has no error.
1134 tr.addpostclose('refresh-filecachestats', self._refreshfilecachestats)
1131 tr.addpostclose('refresh-filecachestats', self._refreshfilecachestats)
1135 self._transref = weakref.ref(tr)
1132 self._transref = weakref.ref(tr)
1136 return tr
1133 return tr
1137
1134
1138 def _journalfiles(self):
1135 def _journalfiles(self):
1139 return ((self.svfs, 'journal'),
1136 return ((self.svfs, 'journal'),
1140 (self.vfs, 'journal.dirstate'),
1137 (self.vfs, 'journal.dirstate'),
1141 (self.vfs, 'journal.branch'),
1138 (self.vfs, 'journal.branch'),
1142 (self.vfs, 'journal.desc'),
1139 (self.vfs, 'journal.desc'),
1143 (self.vfs, 'journal.bookmarks'),
1140 (self.vfs, 'journal.bookmarks'),
1144 (self.svfs, 'journal.phaseroots'))
1141 (self.svfs, 'journal.phaseroots'))
1145
1142
1146 def undofiles(self):
1143 def undofiles(self):
1147 return [(vfs, undoname(x)) for vfs, x in self._journalfiles()]
1144 return [(vfs, undoname(x)) for vfs, x in self._journalfiles()]
1148
1145
1149 def _writejournal(self, desc):
1146 def _writejournal(self, desc):
1150 self.dirstate.savebackup(None, prefix='journal.')
1147 self.dirstate.savebackup(None, prefix='journal.')
1151 self.vfs.write("journal.branch",
1148 self.vfs.write("journal.branch",
1152 encoding.fromlocal(self.dirstate.branch()))
1149 encoding.fromlocal(self.dirstate.branch()))
1153 self.vfs.write("journal.desc",
1150 self.vfs.write("journal.desc",
1154 "%d\n%s\n" % (len(self), desc))
1151 "%d\n%s\n" % (len(self), desc))
1155 self.vfs.write("journal.bookmarks",
1152 self.vfs.write("journal.bookmarks",
1156 self.vfs.tryread("bookmarks"))
1153 self.vfs.tryread("bookmarks"))
1157 self.svfs.write("journal.phaseroots",
1154 self.svfs.write("journal.phaseroots",
1158 self.svfs.tryread("phaseroots"))
1155 self.svfs.tryread("phaseroots"))
1159
1156
1160 def recover(self):
1157 def recover(self):
1161 with self.lock():
1158 with self.lock():
1162 if self.svfs.exists("journal"):
1159 if self.svfs.exists("journal"):
1163 self.ui.status(_("rolling back interrupted transaction\n"))
1160 self.ui.status(_("rolling back interrupted transaction\n"))
1164 vfsmap = {'': self.svfs,
1161 vfsmap = {'': self.svfs,
1165 'plain': self.vfs,}
1162 'plain': self.vfs,}
1166 transaction.rollback(self.svfs, vfsmap, "journal",
1163 transaction.rollback(self.svfs, vfsmap, "journal",
1167 self.ui.warn)
1164 self.ui.warn)
1168 self.invalidate()
1165 self.invalidate()
1169 return True
1166 return True
1170 else:
1167 else:
1171 self.ui.warn(_("no interrupted transaction available\n"))
1168 self.ui.warn(_("no interrupted transaction available\n"))
1172 return False
1169 return False
1173
1170
1174 def rollback(self, dryrun=False, force=False):
1171 def rollback(self, dryrun=False, force=False):
1175 wlock = lock = dsguard = None
1172 wlock = lock = dsguard = None
1176 try:
1173 try:
1177 wlock = self.wlock()
1174 wlock = self.wlock()
1178 lock = self.lock()
1175 lock = self.lock()
1179 if self.svfs.exists("undo"):
1176 if self.svfs.exists("undo"):
1180 dsguard = dirstateguard.dirstateguard(self, 'rollback')
1177 dsguard = dirstateguard.dirstateguard(self, 'rollback')
1181
1178
1182 return self._rollback(dryrun, force, dsguard)
1179 return self._rollback(dryrun, force, dsguard)
1183 else:
1180 else:
1184 self.ui.warn(_("no rollback information available\n"))
1181 self.ui.warn(_("no rollback information available\n"))
1185 return 1
1182 return 1
1186 finally:
1183 finally:
1187 release(dsguard, lock, wlock)
1184 release(dsguard, lock, wlock)
1188
1185
1189 @unfilteredmethod # Until we get smarter cache management
1186 @unfilteredmethod # Until we get smarter cache management
1190 def _rollback(self, dryrun, force, dsguard):
1187 def _rollback(self, dryrun, force, dsguard):
1191 ui = self.ui
1188 ui = self.ui
1192 try:
1189 try:
1193 args = self.vfs.read('undo.desc').splitlines()
1190 args = self.vfs.read('undo.desc').splitlines()
1194 (oldlen, desc, detail) = (int(args[0]), args[1], None)
1191 (oldlen, desc, detail) = (int(args[0]), args[1], None)
1195 if len(args) >= 3:
1192 if len(args) >= 3:
1196 detail = args[2]
1193 detail = args[2]
1197 oldtip = oldlen - 1
1194 oldtip = oldlen - 1
1198
1195
1199 if detail and ui.verbose:
1196 if detail and ui.verbose:
1200 msg = (_('repository tip rolled back to revision %s'
1197 msg = (_('repository tip rolled back to revision %s'
1201 ' (undo %s: %s)\n')
1198 ' (undo %s: %s)\n')
1202 % (oldtip, desc, detail))
1199 % (oldtip, desc, detail))
1203 else:
1200 else:
1204 msg = (_('repository tip rolled back to revision %s'
1201 msg = (_('repository tip rolled back to revision %s'
1205 ' (undo %s)\n')
1202 ' (undo %s)\n')
1206 % (oldtip, desc))
1203 % (oldtip, desc))
1207 except IOError:
1204 except IOError:
1208 msg = _('rolling back unknown transaction\n')
1205 msg = _('rolling back unknown transaction\n')
1209 desc = None
1206 desc = None
1210
1207
1211 if not force and self['.'] != self['tip'] and desc == 'commit':
1208 if not force and self['.'] != self['tip'] and desc == 'commit':
1212 raise error.Abort(
1209 raise error.Abort(
1213 _('rollback of last commit while not checked out '
1210 _('rollback of last commit while not checked out '
1214 'may lose data'), hint=_('use -f to force'))
1211 'may lose data'), hint=_('use -f to force'))
1215
1212
1216 ui.status(msg)
1213 ui.status(msg)
1217 if dryrun:
1214 if dryrun:
1218 return 0
1215 return 0
1219
1216
1220 parents = self.dirstate.parents()
1217 parents = self.dirstate.parents()
1221 self.destroying()
1218 self.destroying()
1222 vfsmap = {'plain': self.vfs, '': self.svfs}
1219 vfsmap = {'plain': self.vfs, '': self.svfs}
1223 transaction.rollback(self.svfs, vfsmap, 'undo', ui.warn)
1220 transaction.rollback(self.svfs, vfsmap, 'undo', ui.warn)
1224 if self.vfs.exists('undo.bookmarks'):
1221 if self.vfs.exists('undo.bookmarks'):
1225 self.vfs.rename('undo.bookmarks', 'bookmarks', checkambig=True)
1222 self.vfs.rename('undo.bookmarks', 'bookmarks', checkambig=True)
1226 if self.svfs.exists('undo.phaseroots'):
1223 if self.svfs.exists('undo.phaseroots'):
1227 self.svfs.rename('undo.phaseroots', 'phaseroots', checkambig=True)
1224 self.svfs.rename('undo.phaseroots', 'phaseroots', checkambig=True)
1228 self.invalidate()
1225 self.invalidate()
1229
1226
1230 parentgone = (parents[0] not in self.changelog.nodemap or
1227 parentgone = (parents[0] not in self.changelog.nodemap or
1231 parents[1] not in self.changelog.nodemap)
1228 parents[1] not in self.changelog.nodemap)
1232 if parentgone:
1229 if parentgone:
1233 # prevent dirstateguard from overwriting already restored one
1230 # prevent dirstateguard from overwriting already restored one
1234 dsguard.close()
1231 dsguard.close()
1235
1232
1236 self.dirstate.restorebackup(None, prefix='undo.')
1233 self.dirstate.restorebackup(None, prefix='undo.')
1237 try:
1234 try:
1238 branch = self.vfs.read('undo.branch')
1235 branch = self.vfs.read('undo.branch')
1239 self.dirstate.setbranch(encoding.tolocal(branch))
1236 self.dirstate.setbranch(encoding.tolocal(branch))
1240 except IOError:
1237 except IOError:
1241 ui.warn(_('named branch could not be reset: '
1238 ui.warn(_('named branch could not be reset: '
1242 'current branch is still \'%s\'\n')
1239 'current branch is still \'%s\'\n')
1243 % self.dirstate.branch())
1240 % self.dirstate.branch())
1244
1241
1245 parents = tuple([p.rev() for p in self[None].parents()])
1242 parents = tuple([p.rev() for p in self[None].parents()])
1246 if len(parents) > 1:
1243 if len(parents) > 1:
1247 ui.status(_('working directory now based on '
1244 ui.status(_('working directory now based on '
1248 'revisions %d and %d\n') % parents)
1245 'revisions %d and %d\n') % parents)
1249 else:
1246 else:
1250 ui.status(_('working directory now based on '
1247 ui.status(_('working directory now based on '
1251 'revision %d\n') % parents)
1248 'revision %d\n') % parents)
1252 mergemod.mergestate.clean(self, self['.'].node())
1249 mergemod.mergestate.clean(self, self['.'].node())
1253
1250
1254 # TODO: if we know which new heads may result from this rollback, pass
1251 # TODO: if we know which new heads may result from this rollback, pass
1255 # them to destroy(), which will prevent the branchhead cache from being
1252 # them to destroy(), which will prevent the branchhead cache from being
1256 # invalidated.
1253 # invalidated.
1257 self.destroyed()
1254 self.destroyed()
1258 return 0
1255 return 0
1259
1256
1260 def invalidatecaches(self):
1257 def invalidatecaches(self):
1261
1258
1262 if '_tagscache' in vars(self):
1259 if '_tagscache' in vars(self):
1263 # can't use delattr on proxy
1260 # can't use delattr on proxy
1264 del self.__dict__['_tagscache']
1261 del self.__dict__['_tagscache']
1265
1262
1266 self.unfiltered()._branchcaches.clear()
1263 self.unfiltered()._branchcaches.clear()
1267 self.invalidatevolatilesets()
1264 self.invalidatevolatilesets()
1268
1265
1269 def invalidatevolatilesets(self):
1266 def invalidatevolatilesets(self):
1270 self.filteredrevcache.clear()
1267 self.filteredrevcache.clear()
1271 obsolete.clearobscaches(self)
1268 obsolete.clearobscaches(self)
1272
1269
1273 def invalidatedirstate(self):
1270 def invalidatedirstate(self):
1274 '''Invalidates the dirstate, causing the next call to dirstate
1271 '''Invalidates the dirstate, causing the next call to dirstate
1275 to check if it was modified since the last time it was read,
1272 to check if it was modified since the last time it was read,
1276 rereading it if it has.
1273 rereading it if it has.
1277
1274
1278 This is different to dirstate.invalidate() that it doesn't always
1275 This is different to dirstate.invalidate() that it doesn't always
1279 rereads the dirstate. Use dirstate.invalidate() if you want to
1276 rereads the dirstate. Use dirstate.invalidate() if you want to
1280 explicitly read the dirstate again (i.e. restoring it to a previous
1277 explicitly read the dirstate again (i.e. restoring it to a previous
1281 known good state).'''
1278 known good state).'''
1282 if hasunfilteredcache(self, 'dirstate'):
1279 if hasunfilteredcache(self, 'dirstate'):
1283 for k in self.dirstate._filecache:
1280 for k in self.dirstate._filecache:
1284 try:
1281 try:
1285 delattr(self.dirstate, k)
1282 delattr(self.dirstate, k)
1286 except AttributeError:
1283 except AttributeError:
1287 pass
1284 pass
1288 delattr(self.unfiltered(), 'dirstate')
1285 delattr(self.unfiltered(), 'dirstate')
1289
1286
1290 def invalidate(self, clearfilecache=False):
1287 def invalidate(self, clearfilecache=False):
1291 '''Invalidates both store and non-store parts other than dirstate
1288 '''Invalidates both store and non-store parts other than dirstate
1292
1289
1293 If a transaction is running, invalidation of store is omitted,
1290 If a transaction is running, invalidation of store is omitted,
1294 because discarding in-memory changes might cause inconsistency
1291 because discarding in-memory changes might cause inconsistency
1295 (e.g. incomplete fncache causes unintentional failure, but
1292 (e.g. incomplete fncache causes unintentional failure, but
1296 redundant one doesn't).
1293 redundant one doesn't).
1297 '''
1294 '''
1298 unfiltered = self.unfiltered() # all file caches are stored unfiltered
1295 unfiltered = self.unfiltered() # all file caches are stored unfiltered
1299 for k in list(self._filecache.keys()):
1296 for k in list(self._filecache.keys()):
1300 # dirstate is invalidated separately in invalidatedirstate()
1297 # dirstate is invalidated separately in invalidatedirstate()
1301 if k == 'dirstate':
1298 if k == 'dirstate':
1302 continue
1299 continue
1303
1300
1304 if clearfilecache:
1301 if clearfilecache:
1305 del self._filecache[k]
1302 del self._filecache[k]
1306 try:
1303 try:
1307 delattr(unfiltered, k)
1304 delattr(unfiltered, k)
1308 except AttributeError:
1305 except AttributeError:
1309 pass
1306 pass
1310 self.invalidatecaches()
1307 self.invalidatecaches()
1311 if not self.currenttransaction():
1308 if not self.currenttransaction():
1312 # TODO: Changing contents of store outside transaction
1309 # TODO: Changing contents of store outside transaction
1313 # causes inconsistency. We should make in-memory store
1310 # causes inconsistency. We should make in-memory store
1314 # changes detectable, and abort if changed.
1311 # changes detectable, and abort if changed.
1315 self.store.invalidatecaches()
1312 self.store.invalidatecaches()
1316
1313
1317 def invalidateall(self):
1314 def invalidateall(self):
1318 '''Fully invalidates both store and non-store parts, causing the
1315 '''Fully invalidates both store and non-store parts, causing the
1319 subsequent operation to reread any outside changes.'''
1316 subsequent operation to reread any outside changes.'''
1320 # extension should hook this to invalidate its caches
1317 # extension should hook this to invalidate its caches
1321 self.invalidate()
1318 self.invalidate()
1322 self.invalidatedirstate()
1319 self.invalidatedirstate()
1323
1320
1324 @unfilteredmethod
1321 @unfilteredmethod
1325 def _refreshfilecachestats(self, tr):
1322 def _refreshfilecachestats(self, tr):
1326 """Reload stats of cached files so that they are flagged as valid"""
1323 """Reload stats of cached files so that they are flagged as valid"""
1327 for k, ce in self._filecache.items():
1324 for k, ce in self._filecache.items():
1328 if k == 'dirstate' or k not in self.__dict__:
1325 if k == 'dirstate' or k not in self.__dict__:
1329 continue
1326 continue
1330 ce.refresh()
1327 ce.refresh()
1331
1328
1332 def _lock(self, vfs, lockname, wait, releasefn, acquirefn, desc,
1329 def _lock(self, vfs, lockname, wait, releasefn, acquirefn, desc,
1333 inheritchecker=None, parentenvvar=None):
1330 inheritchecker=None, parentenvvar=None):
1334 parentlock = None
1331 parentlock = None
1335 # the contents of parentenvvar are used by the underlying lock to
1332 # the contents of parentenvvar are used by the underlying lock to
1336 # determine whether it can be inherited
1333 # determine whether it can be inherited
1337 if parentenvvar is not None:
1334 if parentenvvar is not None:
1338 parentlock = encoding.environ.get(parentenvvar)
1335 parentlock = encoding.environ.get(parentenvvar)
1339 try:
1336 try:
1340 l = lockmod.lock(vfs, lockname, 0, releasefn=releasefn,
1337 l = lockmod.lock(vfs, lockname, 0, releasefn=releasefn,
1341 acquirefn=acquirefn, desc=desc,
1338 acquirefn=acquirefn, desc=desc,
1342 inheritchecker=inheritchecker,
1339 inheritchecker=inheritchecker,
1343 parentlock=parentlock)
1340 parentlock=parentlock)
1344 except error.LockHeld as inst:
1341 except error.LockHeld as inst:
1345 if not wait:
1342 if not wait:
1346 raise
1343 raise
1347 # show more details for new-style locks
1344 # show more details for new-style locks
1348 if ':' in inst.locker:
1345 if ':' in inst.locker:
1349 host, pid = inst.locker.split(":", 1)
1346 host, pid = inst.locker.split(":", 1)
1350 self.ui.warn(
1347 self.ui.warn(
1351 _("waiting for lock on %s held by process %r "
1348 _("waiting for lock on %s held by process %r "
1352 "on host %r\n") % (desc, pid, host))
1349 "on host %r\n") % (desc, pid, host))
1353 else:
1350 else:
1354 self.ui.warn(_("waiting for lock on %s held by %r\n") %
1351 self.ui.warn(_("waiting for lock on %s held by %r\n") %
1355 (desc, inst.locker))
1352 (desc, inst.locker))
1356 # default to 600 seconds timeout
1353 # default to 600 seconds timeout
1357 l = lockmod.lock(vfs, lockname,
1354 l = lockmod.lock(vfs, lockname,
1358 int(self.ui.config("ui", "timeout", "600")),
1355 int(self.ui.config("ui", "timeout", "600")),
1359 releasefn=releasefn, acquirefn=acquirefn,
1356 releasefn=releasefn, acquirefn=acquirefn,
1360 desc=desc)
1357 desc=desc)
1361 self.ui.warn(_("got lock after %s seconds\n") % l.delay)
1358 self.ui.warn(_("got lock after %s seconds\n") % l.delay)
1362 return l
1359 return l
1363
1360
1364 def _afterlock(self, callback):
1361 def _afterlock(self, callback):
1365 """add a callback to be run when the repository is fully unlocked
1362 """add a callback to be run when the repository is fully unlocked
1366
1363
1367 The callback will be executed when the outermost lock is released
1364 The callback will be executed when the outermost lock is released
1368 (with wlock being higher level than 'lock')."""
1365 (with wlock being higher level than 'lock')."""
1369 for ref in (self._wlockref, self._lockref):
1366 for ref in (self._wlockref, self._lockref):
1370 l = ref and ref()
1367 l = ref and ref()
1371 if l and l.held:
1368 if l and l.held:
1372 l.postrelease.append(callback)
1369 l.postrelease.append(callback)
1373 break
1370 break
1374 else: # no lock have been found.
1371 else: # no lock have been found.
1375 callback()
1372 callback()
1376
1373
1377 def lock(self, wait=True):
1374 def lock(self, wait=True):
1378 '''Lock the repository store (.hg/store) and return a weak reference
1375 '''Lock the repository store (.hg/store) and return a weak reference
1379 to the lock. Use this before modifying the store (e.g. committing or
1376 to the lock. Use this before modifying the store (e.g. committing or
1380 stripping). If you are opening a transaction, get a lock as well.)
1377 stripping). If you are opening a transaction, get a lock as well.)
1381
1378
1382 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
1379 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
1383 'wlock' first to avoid a dead-lock hazard.'''
1380 'wlock' first to avoid a dead-lock hazard.'''
1384 l = self._currentlock(self._lockref)
1381 l = self._currentlock(self._lockref)
1385 if l is not None:
1382 if l is not None:
1386 l.lock()
1383 l.lock()
1387 return l
1384 return l
1388
1385
1389 l = self._lock(self.svfs, "lock", wait, None,
1386 l = self._lock(self.svfs, "lock", wait, None,
1390 self.invalidate, _('repository %s') % self.origroot)
1387 self.invalidate, _('repository %s') % self.origroot)
1391 self._lockref = weakref.ref(l)
1388 self._lockref = weakref.ref(l)
1392 return l
1389 return l
1393
1390
1394 def _wlockchecktransaction(self):
1391 def _wlockchecktransaction(self):
1395 if self.currenttransaction() is not None:
1392 if self.currenttransaction() is not None:
1396 raise error.LockInheritanceContractViolation(
1393 raise error.LockInheritanceContractViolation(
1397 'wlock cannot be inherited in the middle of a transaction')
1394 'wlock cannot be inherited in the middle of a transaction')
1398
1395
1399 def wlock(self, wait=True):
1396 def wlock(self, wait=True):
1400 '''Lock the non-store parts of the repository (everything under
1397 '''Lock the non-store parts of the repository (everything under
1401 .hg except .hg/store) and return a weak reference to the lock.
1398 .hg except .hg/store) and return a weak reference to the lock.
1402
1399
1403 Use this before modifying files in .hg.
1400 Use this before modifying files in .hg.
1404
1401
1405 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
1402 If both 'lock' and 'wlock' must be acquired, ensure you always acquires
1406 'wlock' first to avoid a dead-lock hazard.'''
1403 'wlock' first to avoid a dead-lock hazard.'''
1407 l = self._wlockref and self._wlockref()
1404 l = self._wlockref and self._wlockref()
1408 if l is not None and l.held:
1405 if l is not None and l.held:
1409 l.lock()
1406 l.lock()
1410 return l
1407 return l
1411
1408
1412 # We do not need to check for non-waiting lock acquisition. Such
1409 # We do not need to check for non-waiting lock acquisition. Such
1413 # acquisition would not cause dead-lock as they would just fail.
1410 # acquisition would not cause dead-lock as they would just fail.
1414 if wait and (self.ui.configbool('devel', 'all-warnings')
1411 if wait and (self.ui.configbool('devel', 'all-warnings')
1415 or self.ui.configbool('devel', 'check-locks')):
1412 or self.ui.configbool('devel', 'check-locks')):
1416 if self._currentlock(self._lockref) is not None:
1413 if self._currentlock(self._lockref) is not None:
1417 self.ui.develwarn('"wlock" acquired after "lock"')
1414 self.ui.develwarn('"wlock" acquired after "lock"')
1418
1415
1419 def unlock():
1416 def unlock():
1420 if self.dirstate.pendingparentchange():
1417 if self.dirstate.pendingparentchange():
1421 self.dirstate.invalidate()
1418 self.dirstate.invalidate()
1422 else:
1419 else:
1423 self.dirstate.write(None)
1420 self.dirstate.write(None)
1424
1421
1425 self._filecache['dirstate'].refresh()
1422 self._filecache['dirstate'].refresh()
1426
1423
1427 l = self._lock(self.vfs, "wlock", wait, unlock,
1424 l = self._lock(self.vfs, "wlock", wait, unlock,
1428 self.invalidatedirstate, _('working directory of %s') %
1425 self.invalidatedirstate, _('working directory of %s') %
1429 self.origroot,
1426 self.origroot,
1430 inheritchecker=self._wlockchecktransaction,
1427 inheritchecker=self._wlockchecktransaction,
1431 parentenvvar='HG_WLOCK_LOCKER')
1428 parentenvvar='HG_WLOCK_LOCKER')
1432 self._wlockref = weakref.ref(l)
1429 self._wlockref = weakref.ref(l)
1433 return l
1430 return l
1434
1431
1435 def _currentlock(self, lockref):
1432 def _currentlock(self, lockref):
1436 """Returns the lock if it's held, or None if it's not."""
1433 """Returns the lock if it's held, or None if it's not."""
1437 if lockref is None:
1434 if lockref is None:
1438 return None
1435 return None
1439 l = lockref()
1436 l = lockref()
1440 if l is None or not l.held:
1437 if l is None or not l.held:
1441 return None
1438 return None
1442 return l
1439 return l
1443
1440
1444 def currentwlock(self):
1441 def currentwlock(self):
1445 """Returns the wlock if it's held, or None if it's not."""
1442 """Returns the wlock if it's held, or None if it's not."""
1446 return self._currentlock(self._wlockref)
1443 return self._currentlock(self._wlockref)
1447
1444
1448 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1445 def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist):
1449 """
1446 """
1450 commit an individual file as part of a larger transaction
1447 commit an individual file as part of a larger transaction
1451 """
1448 """
1452
1449
1453 fname = fctx.path()
1450 fname = fctx.path()
1454 fparent1 = manifest1.get(fname, nullid)
1451 fparent1 = manifest1.get(fname, nullid)
1455 fparent2 = manifest2.get(fname, nullid)
1452 fparent2 = manifest2.get(fname, nullid)
1456 if isinstance(fctx, context.filectx):
1453 if isinstance(fctx, context.filectx):
1457 node = fctx.filenode()
1454 node = fctx.filenode()
1458 if node in [fparent1, fparent2]:
1455 if node in [fparent1, fparent2]:
1459 self.ui.debug('reusing %s filelog entry\n' % fname)
1456 self.ui.debug('reusing %s filelog entry\n' % fname)
1460 if manifest1.flags(fname) != fctx.flags():
1457 if manifest1.flags(fname) != fctx.flags():
1461 changelist.append(fname)
1458 changelist.append(fname)
1462 return node
1459 return node
1463
1460
1464 flog = self.file(fname)
1461 flog = self.file(fname)
1465 meta = {}
1462 meta = {}
1466 copy = fctx.renamed()
1463 copy = fctx.renamed()
1467 if copy and copy[0] != fname:
1464 if copy and copy[0] != fname:
1468 # Mark the new revision of this file as a copy of another
1465 # Mark the new revision of this file as a copy of another
1469 # file. This copy data will effectively act as a parent
1466 # file. This copy data will effectively act as a parent
1470 # of this new revision. If this is a merge, the first
1467 # of this new revision. If this is a merge, the first
1471 # parent will be the nullid (meaning "look up the copy data")
1468 # parent will be the nullid (meaning "look up the copy data")
1472 # and the second one will be the other parent. For example:
1469 # and the second one will be the other parent. For example:
1473 #
1470 #
1474 # 0 --- 1 --- 3 rev1 changes file foo
1471 # 0 --- 1 --- 3 rev1 changes file foo
1475 # \ / rev2 renames foo to bar and changes it
1472 # \ / rev2 renames foo to bar and changes it
1476 # \- 2 -/ rev3 should have bar with all changes and
1473 # \- 2 -/ rev3 should have bar with all changes and
1477 # should record that bar descends from
1474 # should record that bar descends from
1478 # bar in rev2 and foo in rev1
1475 # bar in rev2 and foo in rev1
1479 #
1476 #
1480 # this allows this merge to succeed:
1477 # this allows this merge to succeed:
1481 #
1478 #
1482 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1479 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
1483 # \ / merging rev3 and rev4 should use bar@rev2
1480 # \ / merging rev3 and rev4 should use bar@rev2
1484 # \- 2 --- 4 as the merge base
1481 # \- 2 --- 4 as the merge base
1485 #
1482 #
1486
1483
1487 cfname = copy[0]
1484 cfname = copy[0]
1488 crev = manifest1.get(cfname)
1485 crev = manifest1.get(cfname)
1489 newfparent = fparent2
1486 newfparent = fparent2
1490
1487
1491 if manifest2: # branch merge
1488 if manifest2: # branch merge
1492 if fparent2 == nullid or crev is None: # copied on remote side
1489 if fparent2 == nullid or crev is None: # copied on remote side
1493 if cfname in manifest2:
1490 if cfname in manifest2:
1494 crev = manifest2[cfname]
1491 crev = manifest2[cfname]
1495 newfparent = fparent1
1492 newfparent = fparent1
1496
1493
1497 # Here, we used to search backwards through history to try to find
1494 # Here, we used to search backwards through history to try to find
1498 # where the file copy came from if the source of a copy was not in
1495 # where the file copy came from if the source of a copy was not in
1499 # the parent directory. However, this doesn't actually make sense to
1496 # the parent directory. However, this doesn't actually make sense to
1500 # do (what does a copy from something not in your working copy even
1497 # do (what does a copy from something not in your working copy even
1501 # mean?) and it causes bugs (eg, issue4476). Instead, we will warn
1498 # mean?) and it causes bugs (eg, issue4476). Instead, we will warn
1502 # the user that copy information was dropped, so if they didn't
1499 # the user that copy information was dropped, so if they didn't
1503 # expect this outcome it can be fixed, but this is the correct
1500 # expect this outcome it can be fixed, but this is the correct
1504 # behavior in this circumstance.
1501 # behavior in this circumstance.
1505
1502
1506 if crev:
1503 if crev:
1507 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1504 self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(crev)))
1508 meta["copy"] = cfname
1505 meta["copy"] = cfname
1509 meta["copyrev"] = hex(crev)
1506 meta["copyrev"] = hex(crev)
1510 fparent1, fparent2 = nullid, newfparent
1507 fparent1, fparent2 = nullid, newfparent
1511 else:
1508 else:
1512 self.ui.warn(_("warning: can't find ancestor for '%s' "
1509 self.ui.warn(_("warning: can't find ancestor for '%s' "
1513 "copied from '%s'!\n") % (fname, cfname))
1510 "copied from '%s'!\n") % (fname, cfname))
1514
1511
1515 elif fparent1 == nullid:
1512 elif fparent1 == nullid:
1516 fparent1, fparent2 = fparent2, nullid
1513 fparent1, fparent2 = fparent2, nullid
1517 elif fparent2 != nullid:
1514 elif fparent2 != nullid:
1518 # is one parent an ancestor of the other?
1515 # is one parent an ancestor of the other?
1519 fparentancestors = flog.commonancestorsheads(fparent1, fparent2)
1516 fparentancestors = flog.commonancestorsheads(fparent1, fparent2)
1520 if fparent1 in fparentancestors:
1517 if fparent1 in fparentancestors:
1521 fparent1, fparent2 = fparent2, nullid
1518 fparent1, fparent2 = fparent2, nullid
1522 elif fparent2 in fparentancestors:
1519 elif fparent2 in fparentancestors:
1523 fparent2 = nullid
1520 fparent2 = nullid
1524
1521
1525 # is the file changed?
1522 # is the file changed?
1526 text = fctx.data()
1523 text = fctx.data()
1527 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1524 if fparent2 != nullid or flog.cmp(fparent1, text) or meta:
1528 changelist.append(fname)
1525 changelist.append(fname)
1529 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1526 return flog.add(text, meta, tr, linkrev, fparent1, fparent2)
1530 # are just the flags changed during merge?
1527 # are just the flags changed during merge?
1531 elif fname in manifest1 and manifest1.flags(fname) != fctx.flags():
1528 elif fname in manifest1 and manifest1.flags(fname) != fctx.flags():
1532 changelist.append(fname)
1529 changelist.append(fname)
1533
1530
1534 return fparent1
1531 return fparent1
1535
1532
1536 def checkcommitpatterns(self, wctx, vdirs, match, status, fail):
1533 def checkcommitpatterns(self, wctx, vdirs, match, status, fail):
1537 """check for commit arguments that aren't committable"""
1534 """check for commit arguments that aren't committable"""
1538 if match.isexact() or match.prefix():
1535 if match.isexact() or match.prefix():
1539 matched = set(status.modified + status.added + status.removed)
1536 matched = set(status.modified + status.added + status.removed)
1540
1537
1541 for f in match.files():
1538 for f in match.files():
1542 f = self.dirstate.normalize(f)
1539 f = self.dirstate.normalize(f)
1543 if f == '.' or f in matched or f in wctx.substate:
1540 if f == '.' or f in matched or f in wctx.substate:
1544 continue
1541 continue
1545 if f in status.deleted:
1542 if f in status.deleted:
1546 fail(f, _('file not found!'))
1543 fail(f, _('file not found!'))
1547 if f in vdirs: # visited directory
1544 if f in vdirs: # visited directory
1548 d = f + '/'
1545 d = f + '/'
1549 for mf in matched:
1546 for mf in matched:
1550 if mf.startswith(d):
1547 if mf.startswith(d):
1551 break
1548 break
1552 else:
1549 else:
1553 fail(f, _("no match under directory!"))
1550 fail(f, _("no match under directory!"))
1554 elif f not in self.dirstate:
1551 elif f not in self.dirstate:
1555 fail(f, _("file not tracked!"))
1552 fail(f, _("file not tracked!"))
1556
1553
1557 @unfilteredmethod
1554 @unfilteredmethod
1558 def commit(self, text="", user=None, date=None, match=None, force=False,
1555 def commit(self, text="", user=None, date=None, match=None, force=False,
1559 editor=False, extra=None):
1556 editor=False, extra=None):
1560 """Add a new revision to current repository.
1557 """Add a new revision to current repository.
1561
1558
1562 Revision information is gathered from the working directory,
1559 Revision information is gathered from the working directory,
1563 match can be used to filter the committed files. If editor is
1560 match can be used to filter the committed files. If editor is
1564 supplied, it is called to get a commit message.
1561 supplied, it is called to get a commit message.
1565 """
1562 """
1566 if extra is None:
1563 if extra is None:
1567 extra = {}
1564 extra = {}
1568
1565
1569 def fail(f, msg):
1566 def fail(f, msg):
1570 raise error.Abort('%s: %s' % (f, msg))
1567 raise error.Abort('%s: %s' % (f, msg))
1571
1568
1572 if not match:
1569 if not match:
1573 match = matchmod.always(self.root, '')
1570 match = matchmod.always(self.root, '')
1574
1571
1575 if not force:
1572 if not force:
1576 vdirs = []
1573 vdirs = []
1577 match.explicitdir = vdirs.append
1574 match.explicitdir = vdirs.append
1578 match.bad = fail
1575 match.bad = fail
1579
1576
1580 wlock = lock = tr = None
1577 wlock = lock = tr = None
1581 try:
1578 try:
1582 wlock = self.wlock()
1579 wlock = self.wlock()
1583 lock = self.lock() # for recent changelog (see issue4368)
1580 lock = self.lock() # for recent changelog (see issue4368)
1584
1581
1585 wctx = self[None]
1582 wctx = self[None]
1586 merge = len(wctx.parents()) > 1
1583 merge = len(wctx.parents()) > 1
1587
1584
1588 if not force and merge and match.ispartial():
1585 if not force and merge and match.ispartial():
1589 raise error.Abort(_('cannot partially commit a merge '
1586 raise error.Abort(_('cannot partially commit a merge '
1590 '(do not specify files or patterns)'))
1587 '(do not specify files or patterns)'))
1591
1588
1592 status = self.status(match=match, clean=force)
1589 status = self.status(match=match, clean=force)
1593 if force:
1590 if force:
1594 status.modified.extend(status.clean) # mq may commit clean files
1591 status.modified.extend(status.clean) # mq may commit clean files
1595
1592
1596 # check subrepos
1593 # check subrepos
1597 subs = []
1594 subs = []
1598 commitsubs = set()
1595 commitsubs = set()
1599 newstate = wctx.substate.copy()
1596 newstate = wctx.substate.copy()
1600 # only manage subrepos and .hgsubstate if .hgsub is present
1597 # only manage subrepos and .hgsubstate if .hgsub is present
1601 if '.hgsub' in wctx:
1598 if '.hgsub' in wctx:
1602 # we'll decide whether to track this ourselves, thanks
1599 # we'll decide whether to track this ourselves, thanks
1603 for c in status.modified, status.added, status.removed:
1600 for c in status.modified, status.added, status.removed:
1604 if '.hgsubstate' in c:
1601 if '.hgsubstate' in c:
1605 c.remove('.hgsubstate')
1602 c.remove('.hgsubstate')
1606
1603
1607 # compare current state to last committed state
1604 # compare current state to last committed state
1608 # build new substate based on last committed state
1605 # build new substate based on last committed state
1609 oldstate = wctx.p1().substate
1606 oldstate = wctx.p1().substate
1610 for s in sorted(newstate.keys()):
1607 for s in sorted(newstate.keys()):
1611 if not match(s):
1608 if not match(s):
1612 # ignore working copy, use old state if present
1609 # ignore working copy, use old state if present
1613 if s in oldstate:
1610 if s in oldstate:
1614 newstate[s] = oldstate[s]
1611 newstate[s] = oldstate[s]
1615 continue
1612 continue
1616 if not force:
1613 if not force:
1617 raise error.Abort(
1614 raise error.Abort(
1618 _("commit with new subrepo %s excluded") % s)
1615 _("commit with new subrepo %s excluded") % s)
1619 dirtyreason = wctx.sub(s).dirtyreason(True)
1616 dirtyreason = wctx.sub(s).dirtyreason(True)
1620 if dirtyreason:
1617 if dirtyreason:
1621 if not self.ui.configbool('ui', 'commitsubrepos'):
1618 if not self.ui.configbool('ui', 'commitsubrepos'):
1622 raise error.Abort(dirtyreason,
1619 raise error.Abort(dirtyreason,
1623 hint=_("use --subrepos for recursive commit"))
1620 hint=_("use --subrepos for recursive commit"))
1624 subs.append(s)
1621 subs.append(s)
1625 commitsubs.add(s)
1622 commitsubs.add(s)
1626 else:
1623 else:
1627 bs = wctx.sub(s).basestate()
1624 bs = wctx.sub(s).basestate()
1628 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1625 newstate[s] = (newstate[s][0], bs, newstate[s][2])
1629 if oldstate.get(s, (None, None, None))[1] != bs:
1626 if oldstate.get(s, (None, None, None))[1] != bs:
1630 subs.append(s)
1627 subs.append(s)
1631
1628
1632 # check for removed subrepos
1629 # check for removed subrepos
1633 for p in wctx.parents():
1630 for p in wctx.parents():
1634 r = [s for s in p.substate if s not in newstate]
1631 r = [s for s in p.substate if s not in newstate]
1635 subs += [s for s in r if match(s)]
1632 subs += [s for s in r if match(s)]
1636 if subs:
1633 if subs:
1637 if (not match('.hgsub') and
1634 if (not match('.hgsub') and
1638 '.hgsub' in (wctx.modified() + wctx.added())):
1635 '.hgsub' in (wctx.modified() + wctx.added())):
1639 raise error.Abort(
1636 raise error.Abort(
1640 _("can't commit subrepos without .hgsub"))
1637 _("can't commit subrepos without .hgsub"))
1641 status.modified.insert(0, '.hgsubstate')
1638 status.modified.insert(0, '.hgsubstate')
1642
1639
1643 elif '.hgsub' in status.removed:
1640 elif '.hgsub' in status.removed:
1644 # clean up .hgsubstate when .hgsub is removed
1641 # clean up .hgsubstate when .hgsub is removed
1645 if ('.hgsubstate' in wctx and
1642 if ('.hgsubstate' in wctx and
1646 '.hgsubstate' not in (status.modified + status.added +
1643 '.hgsubstate' not in (status.modified + status.added +
1647 status.removed)):
1644 status.removed)):
1648 status.removed.insert(0, '.hgsubstate')
1645 status.removed.insert(0, '.hgsubstate')
1649
1646
1650 # make sure all explicit patterns are matched
1647 # make sure all explicit patterns are matched
1651 if not force:
1648 if not force:
1652 self.checkcommitpatterns(wctx, vdirs, match, status, fail)
1649 self.checkcommitpatterns(wctx, vdirs, match, status, fail)
1653
1650
1654 cctx = context.workingcommitctx(self, status,
1651 cctx = context.workingcommitctx(self, status,
1655 text, user, date, extra)
1652 text, user, date, extra)
1656
1653
1657 # internal config: ui.allowemptycommit
1654 # internal config: ui.allowemptycommit
1658 allowemptycommit = (wctx.branch() != wctx.p1().branch()
1655 allowemptycommit = (wctx.branch() != wctx.p1().branch()
1659 or extra.get('close') or merge or cctx.files()
1656 or extra.get('close') or merge or cctx.files()
1660 or self.ui.configbool('ui', 'allowemptycommit'))
1657 or self.ui.configbool('ui', 'allowemptycommit'))
1661 if not allowemptycommit:
1658 if not allowemptycommit:
1662 return None
1659 return None
1663
1660
1664 if merge and cctx.deleted():
1661 if merge and cctx.deleted():
1665 raise error.Abort(_("cannot commit merge with missing files"))
1662 raise error.Abort(_("cannot commit merge with missing files"))
1666
1663
1667 ms = mergemod.mergestate.read(self)
1664 ms = mergemod.mergestate.read(self)
1668 mergeutil.checkunresolved(ms)
1665 mergeutil.checkunresolved(ms)
1669
1666
1670 if editor:
1667 if editor:
1671 cctx._text = editor(self, cctx, subs)
1668 cctx._text = editor(self, cctx, subs)
1672 edited = (text != cctx._text)
1669 edited = (text != cctx._text)
1673
1670
1674 # Save commit message in case this transaction gets rolled back
1671 # Save commit message in case this transaction gets rolled back
1675 # (e.g. by a pretxncommit hook). Leave the content alone on
1672 # (e.g. by a pretxncommit hook). Leave the content alone on
1676 # the assumption that the user will use the same editor again.
1673 # the assumption that the user will use the same editor again.
1677 msgfn = self.savecommitmessage(cctx._text)
1674 msgfn = self.savecommitmessage(cctx._text)
1678
1675
1679 # commit subs and write new state
1676 # commit subs and write new state
1680 if subs:
1677 if subs:
1681 for s in sorted(commitsubs):
1678 for s in sorted(commitsubs):
1682 sub = wctx.sub(s)
1679 sub = wctx.sub(s)
1683 self.ui.status(_('committing subrepository %s\n') %
1680 self.ui.status(_('committing subrepository %s\n') %
1684 subrepo.subrelpath(sub))
1681 subrepo.subrelpath(sub))
1685 sr = sub.commit(cctx._text, user, date)
1682 sr = sub.commit(cctx._text, user, date)
1686 newstate[s] = (newstate[s][0], sr)
1683 newstate[s] = (newstate[s][0], sr)
1687 subrepo.writestate(self, newstate)
1684 subrepo.writestate(self, newstate)
1688
1685
1689 p1, p2 = self.dirstate.parents()
1686 p1, p2 = self.dirstate.parents()
1690 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1687 hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '')
1691 try:
1688 try:
1692 self.hook("precommit", throw=True, parent1=hookp1,
1689 self.hook("precommit", throw=True, parent1=hookp1,
1693 parent2=hookp2)
1690 parent2=hookp2)
1694 tr = self.transaction('commit')
1691 tr = self.transaction('commit')
1695 ret = self.commitctx(cctx, True)
1692 ret = self.commitctx(cctx, True)
1696 except: # re-raises
1693 except: # re-raises
1697 if edited:
1694 if edited:
1698 self.ui.write(
1695 self.ui.write(
1699 _('note: commit message saved in %s\n') % msgfn)
1696 _('note: commit message saved in %s\n') % msgfn)
1700 raise
1697 raise
1701 # update bookmarks, dirstate and mergestate
1698 # update bookmarks, dirstate and mergestate
1702 bookmarks.update(self, [p1, p2], ret)
1699 bookmarks.update(self, [p1, p2], ret)
1703 cctx.markcommitted(ret)
1700 cctx.markcommitted(ret)
1704 ms.reset()
1701 ms.reset()
1705 tr.close()
1702 tr.close()
1706
1703
1707 finally:
1704 finally:
1708 lockmod.release(tr, lock, wlock)
1705 lockmod.release(tr, lock, wlock)
1709
1706
1710 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1707 def commithook(node=hex(ret), parent1=hookp1, parent2=hookp2):
1711 # hack for command that use a temporary commit (eg: histedit)
1708 # hack for command that use a temporary commit (eg: histedit)
1712 # temporary commit got stripped before hook release
1709 # temporary commit got stripped before hook release
1713 if self.changelog.hasnode(ret):
1710 if self.changelog.hasnode(ret):
1714 self.hook("commit", node=node, parent1=parent1,
1711 self.hook("commit", node=node, parent1=parent1,
1715 parent2=parent2)
1712 parent2=parent2)
1716 self._afterlock(commithook)
1713 self._afterlock(commithook)
1717 return ret
1714 return ret
1718
1715
1719 @unfilteredmethod
1716 @unfilteredmethod
1720 def commitctx(self, ctx, error=False):
1717 def commitctx(self, ctx, error=False):
1721 """Add a new revision to current repository.
1718 """Add a new revision to current repository.
1722 Revision information is passed via the context argument.
1719 Revision information is passed via the context argument.
1723 """
1720 """
1724
1721
1725 tr = None
1722 tr = None
1726 p1, p2 = ctx.p1(), ctx.p2()
1723 p1, p2 = ctx.p1(), ctx.p2()
1727 user = ctx.user()
1724 user = ctx.user()
1728
1725
1729 lock = self.lock()
1726 lock = self.lock()
1730 try:
1727 try:
1731 tr = self.transaction("commit")
1728 tr = self.transaction("commit")
1732 trp = weakref.proxy(tr)
1729 trp = weakref.proxy(tr)
1733
1730
1734 if ctx.manifestnode():
1731 if ctx.manifestnode():
1735 # reuse an existing manifest revision
1732 # reuse an existing manifest revision
1736 mn = ctx.manifestnode()
1733 mn = ctx.manifestnode()
1737 files = ctx.files()
1734 files = ctx.files()
1738 elif ctx.files():
1735 elif ctx.files():
1739 m1ctx = p1.manifestctx()
1736 m1ctx = p1.manifestctx()
1740 m2ctx = p2.manifestctx()
1737 m2ctx = p2.manifestctx()
1741 mctx = m1ctx.copy()
1738 mctx = m1ctx.copy()
1742
1739
1743 m = mctx.read()
1740 m = mctx.read()
1744 m1 = m1ctx.read()
1741 m1 = m1ctx.read()
1745 m2 = m2ctx.read()
1742 m2 = m2ctx.read()
1746
1743
1747 # check in files
1744 # check in files
1748 added = []
1745 added = []
1749 changed = []
1746 changed = []
1750 removed = list(ctx.removed())
1747 removed = list(ctx.removed())
1751 linkrev = len(self)
1748 linkrev = len(self)
1752 self.ui.note(_("committing files:\n"))
1749 self.ui.note(_("committing files:\n"))
1753 for f in sorted(ctx.modified() + ctx.added()):
1750 for f in sorted(ctx.modified() + ctx.added()):
1754 self.ui.note(f + "\n")
1751 self.ui.note(f + "\n")
1755 try:
1752 try:
1756 fctx = ctx[f]
1753 fctx = ctx[f]
1757 if fctx is None:
1754 if fctx is None:
1758 removed.append(f)
1755 removed.append(f)
1759 else:
1756 else:
1760 added.append(f)
1757 added.append(f)
1761 m[f] = self._filecommit(fctx, m1, m2, linkrev,
1758 m[f] = self._filecommit(fctx, m1, m2, linkrev,
1762 trp, changed)
1759 trp, changed)
1763 m.setflag(f, fctx.flags())
1760 m.setflag(f, fctx.flags())
1764 except OSError as inst:
1761 except OSError as inst:
1765 self.ui.warn(_("trouble committing %s!\n") % f)
1762 self.ui.warn(_("trouble committing %s!\n") % f)
1766 raise
1763 raise
1767 except IOError as inst:
1764 except IOError as inst:
1768 errcode = getattr(inst, 'errno', errno.ENOENT)
1765 errcode = getattr(inst, 'errno', errno.ENOENT)
1769 if error or errcode and errcode != errno.ENOENT:
1766 if error or errcode and errcode != errno.ENOENT:
1770 self.ui.warn(_("trouble committing %s!\n") % f)
1767 self.ui.warn(_("trouble committing %s!\n") % f)
1771 raise
1768 raise
1772
1769
1773 # update manifest
1770 # update manifest
1774 self.ui.note(_("committing manifest\n"))
1771 self.ui.note(_("committing manifest\n"))
1775 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1772 removed = [f for f in sorted(removed) if f in m1 or f in m2]
1776 drop = [f for f in removed if f in m]
1773 drop = [f for f in removed if f in m]
1777 for f in drop:
1774 for f in drop:
1778 del m[f]
1775 del m[f]
1779 mn = mctx.write(trp, linkrev,
1776 mn = mctx.write(trp, linkrev,
1780 p1.manifestnode(), p2.manifestnode(),
1777 p1.manifestnode(), p2.manifestnode(),
1781 added, drop)
1778 added, drop)
1782 files = changed + removed
1779 files = changed + removed
1783 else:
1780 else:
1784 mn = p1.manifestnode()
1781 mn = p1.manifestnode()
1785 files = []
1782 files = []
1786
1783
1787 # update changelog
1784 # update changelog
1788 self.ui.note(_("committing changelog\n"))
1785 self.ui.note(_("committing changelog\n"))
1789 self.changelog.delayupdate(tr)
1786 self.changelog.delayupdate(tr)
1790 n = self.changelog.add(mn, files, ctx.description(),
1787 n = self.changelog.add(mn, files, ctx.description(),
1791 trp, p1.node(), p2.node(),
1788 trp, p1.node(), p2.node(),
1792 user, ctx.date(), ctx.extra().copy())
1789 user, ctx.date(), ctx.extra().copy())
1793 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1790 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
1794 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1791 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
1795 parent2=xp2)
1792 parent2=xp2)
1796 # set the new commit is proper phase
1793 # set the new commit is proper phase
1797 targetphase = subrepo.newcommitphase(self.ui, ctx)
1794 targetphase = subrepo.newcommitphase(self.ui, ctx)
1798 if targetphase:
1795 if targetphase:
1799 # retract boundary do not alter parent changeset.
1796 # retract boundary do not alter parent changeset.
1800 # if a parent have higher the resulting phase will
1797 # if a parent have higher the resulting phase will
1801 # be compliant anyway
1798 # be compliant anyway
1802 #
1799 #
1803 # if minimal phase was 0 we don't need to retract anything
1800 # if minimal phase was 0 we don't need to retract anything
1804 phases.retractboundary(self, tr, targetphase, [n])
1801 phases.retractboundary(self, tr, targetphase, [n])
1805 tr.close()
1802 tr.close()
1806 branchmap.updatecache(self.filtered('served'))
1803 branchmap.updatecache(self.filtered('served'))
1807 return n
1804 return n
1808 finally:
1805 finally:
1809 if tr:
1806 if tr:
1810 tr.release()
1807 tr.release()
1811 lock.release()
1808 lock.release()
1812
1809
1813 @unfilteredmethod
1810 @unfilteredmethod
1814 def destroying(self):
1811 def destroying(self):
1815 '''Inform the repository that nodes are about to be destroyed.
1812 '''Inform the repository that nodes are about to be destroyed.
1816 Intended for use by strip and rollback, so there's a common
1813 Intended for use by strip and rollback, so there's a common
1817 place for anything that has to be done before destroying history.
1814 place for anything that has to be done before destroying history.
1818
1815
1819 This is mostly useful for saving state that is in memory and waiting
1816 This is mostly useful for saving state that is in memory and waiting
1820 to be flushed when the current lock is released. Because a call to
1817 to be flushed when the current lock is released. Because a call to
1821 destroyed is imminent, the repo will be invalidated causing those
1818 destroyed is imminent, the repo will be invalidated causing those
1822 changes to stay in memory (waiting for the next unlock), or vanish
1819 changes to stay in memory (waiting for the next unlock), or vanish
1823 completely.
1820 completely.
1824 '''
1821 '''
1825 # When using the same lock to commit and strip, the phasecache is left
1822 # When using the same lock to commit and strip, the phasecache is left
1826 # dirty after committing. Then when we strip, the repo is invalidated,
1823 # dirty after committing. Then when we strip, the repo is invalidated,
1827 # causing those changes to disappear.
1824 # causing those changes to disappear.
1828 if '_phasecache' in vars(self):
1825 if '_phasecache' in vars(self):
1829 self._phasecache.write()
1826 self._phasecache.write()
1830
1827
1831 @unfilteredmethod
1828 @unfilteredmethod
1832 def destroyed(self):
1829 def destroyed(self):
1833 '''Inform the repository that nodes have been destroyed.
1830 '''Inform the repository that nodes have been destroyed.
1834 Intended for use by strip and rollback, so there's a common
1831 Intended for use by strip and rollback, so there's a common
1835 place for anything that has to be done after destroying history.
1832 place for anything that has to be done after destroying history.
1836 '''
1833 '''
1837 # When one tries to:
1834 # When one tries to:
1838 # 1) destroy nodes thus calling this method (e.g. strip)
1835 # 1) destroy nodes thus calling this method (e.g. strip)
1839 # 2) use phasecache somewhere (e.g. commit)
1836 # 2) use phasecache somewhere (e.g. commit)
1840 #
1837 #
1841 # then 2) will fail because the phasecache contains nodes that were
1838 # then 2) will fail because the phasecache contains nodes that were
1842 # removed. We can either remove phasecache from the filecache,
1839 # removed. We can either remove phasecache from the filecache,
1843 # causing it to reload next time it is accessed, or simply filter
1840 # causing it to reload next time it is accessed, or simply filter
1844 # the removed nodes now and write the updated cache.
1841 # the removed nodes now and write the updated cache.
1845 self._phasecache.filterunknown(self)
1842 self._phasecache.filterunknown(self)
1846 self._phasecache.write()
1843 self._phasecache.write()
1847
1844
1848 # update the 'served' branch cache to help read only server process
1845 # update the 'served' branch cache to help read only server process
1849 # Thanks to branchcache collaboration this is done from the nearest
1846 # Thanks to branchcache collaboration this is done from the nearest
1850 # filtered subset and it is expected to be fast.
1847 # filtered subset and it is expected to be fast.
1851 branchmap.updatecache(self.filtered('served'))
1848 branchmap.updatecache(self.filtered('served'))
1852
1849
1853 # Ensure the persistent tag cache is updated. Doing it now
1850 # Ensure the persistent tag cache is updated. Doing it now
1854 # means that the tag cache only has to worry about destroyed
1851 # means that the tag cache only has to worry about destroyed
1855 # heads immediately after a strip/rollback. That in turn
1852 # heads immediately after a strip/rollback. That in turn
1856 # guarantees that "cachetip == currenttip" (comparing both rev
1853 # guarantees that "cachetip == currenttip" (comparing both rev
1857 # and node) always means no nodes have been added or destroyed.
1854 # and node) always means no nodes have been added or destroyed.
1858
1855
1859 # XXX this is suboptimal when qrefresh'ing: we strip the current
1856 # XXX this is suboptimal when qrefresh'ing: we strip the current
1860 # head, refresh the tag cache, then immediately add a new head.
1857 # head, refresh the tag cache, then immediately add a new head.
1861 # But I think doing it this way is necessary for the "instant
1858 # But I think doing it this way is necessary for the "instant
1862 # tag cache retrieval" case to work.
1859 # tag cache retrieval" case to work.
1863 self.invalidate()
1860 self.invalidate()
1864
1861
1865 def walk(self, match, node=None):
1862 def walk(self, match, node=None):
1866 '''
1863 '''
1867 walk recursively through the directory tree or a given
1864 walk recursively through the directory tree or a given
1868 changeset, finding all files matched by the match
1865 changeset, finding all files matched by the match
1869 function
1866 function
1870 '''
1867 '''
1871 return self[node].walk(match)
1868 return self[node].walk(match)
1872
1869
1873 def status(self, node1='.', node2=None, match=None,
1870 def status(self, node1='.', node2=None, match=None,
1874 ignored=False, clean=False, unknown=False,
1871 ignored=False, clean=False, unknown=False,
1875 listsubrepos=False):
1872 listsubrepos=False):
1876 '''a convenience method that calls node1.status(node2)'''
1873 '''a convenience method that calls node1.status(node2)'''
1877 return self[node1].status(node2, match, ignored, clean, unknown,
1874 return self[node1].status(node2, match, ignored, clean, unknown,
1878 listsubrepos)
1875 listsubrepos)
1879
1876
1880 def heads(self, start=None):
1877 def heads(self, start=None):
1881 if start is None:
1878 if start is None:
1882 cl = self.changelog
1879 cl = self.changelog
1883 headrevs = reversed(cl.headrevs())
1880 headrevs = reversed(cl.headrevs())
1884 return [cl.node(rev) for rev in headrevs]
1881 return [cl.node(rev) for rev in headrevs]
1885
1882
1886 heads = self.changelog.heads(start)
1883 heads = self.changelog.heads(start)
1887 # sort the output in rev descending order
1884 # sort the output in rev descending order
1888 return sorted(heads, key=self.changelog.rev, reverse=True)
1885 return sorted(heads, key=self.changelog.rev, reverse=True)
1889
1886
1890 def branchheads(self, branch=None, start=None, closed=False):
1887 def branchheads(self, branch=None, start=None, closed=False):
1891 '''return a (possibly filtered) list of heads for the given branch
1888 '''return a (possibly filtered) list of heads for the given branch
1892
1889
1893 Heads are returned in topological order, from newest to oldest.
1890 Heads are returned in topological order, from newest to oldest.
1894 If branch is None, use the dirstate branch.
1891 If branch is None, use the dirstate branch.
1895 If start is not None, return only heads reachable from start.
1892 If start is not None, return only heads reachable from start.
1896 If closed is True, return heads that are marked as closed as well.
1893 If closed is True, return heads that are marked as closed as well.
1897 '''
1894 '''
1898 if branch is None:
1895 if branch is None:
1899 branch = self[None].branch()
1896 branch = self[None].branch()
1900 branches = self.branchmap()
1897 branches = self.branchmap()
1901 if branch not in branches:
1898 if branch not in branches:
1902 return []
1899 return []
1903 # the cache returns heads ordered lowest to highest
1900 # the cache returns heads ordered lowest to highest
1904 bheads = list(reversed(branches.branchheads(branch, closed=closed)))
1901 bheads = list(reversed(branches.branchheads(branch, closed=closed)))
1905 if start is not None:
1902 if start is not None:
1906 # filter out the heads that cannot be reached from startrev
1903 # filter out the heads that cannot be reached from startrev
1907 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1904 fbheads = set(self.changelog.nodesbetween([start], bheads)[2])
1908 bheads = [h for h in bheads if h in fbheads]
1905 bheads = [h for h in bheads if h in fbheads]
1909 return bheads
1906 return bheads
1910
1907
1911 def branches(self, nodes):
1908 def branches(self, nodes):
1912 if not nodes:
1909 if not nodes:
1913 nodes = [self.changelog.tip()]
1910 nodes = [self.changelog.tip()]
1914 b = []
1911 b = []
1915 for n in nodes:
1912 for n in nodes:
1916 t = n
1913 t = n
1917 while True:
1914 while True:
1918 p = self.changelog.parents(n)
1915 p = self.changelog.parents(n)
1919 if p[1] != nullid or p[0] == nullid:
1916 if p[1] != nullid or p[0] == nullid:
1920 b.append((t, n, p[0], p[1]))
1917 b.append((t, n, p[0], p[1]))
1921 break
1918 break
1922 n = p[0]
1919 n = p[0]
1923 return b
1920 return b
1924
1921
1925 def between(self, pairs):
1922 def between(self, pairs):
1926 r = []
1923 r = []
1927
1924
1928 for top, bottom in pairs:
1925 for top, bottom in pairs:
1929 n, l, i = top, [], 0
1926 n, l, i = top, [], 0
1930 f = 1
1927 f = 1
1931
1928
1932 while n != bottom and n != nullid:
1929 while n != bottom and n != nullid:
1933 p = self.changelog.parents(n)[0]
1930 p = self.changelog.parents(n)[0]
1934 if i == f:
1931 if i == f:
1935 l.append(n)
1932 l.append(n)
1936 f = f * 2
1933 f = f * 2
1937 n = p
1934 n = p
1938 i += 1
1935 i += 1
1939
1936
1940 r.append(l)
1937 r.append(l)
1941
1938
1942 return r
1939 return r
1943
1940
1944 def checkpush(self, pushop):
1941 def checkpush(self, pushop):
1945 """Extensions can override this function if additional checks have
1942 """Extensions can override this function if additional checks have
1946 to be performed before pushing, or call it if they override push
1943 to be performed before pushing, or call it if they override push
1947 command.
1944 command.
1948 """
1945 """
1949 pass
1946 pass
1950
1947
1951 @unfilteredpropertycache
1948 @unfilteredpropertycache
1952 def prepushoutgoinghooks(self):
1949 def prepushoutgoinghooks(self):
1953 """Return util.hooks consists of a pushop with repo, remote, outgoing
1950 """Return util.hooks consists of a pushop with repo, remote, outgoing
1954 methods, which are called before pushing changesets.
1951 methods, which are called before pushing changesets.
1955 """
1952 """
1956 return util.hooks()
1953 return util.hooks()
1957
1954
1958 def pushkey(self, namespace, key, old, new):
1955 def pushkey(self, namespace, key, old, new):
1959 try:
1956 try:
1960 tr = self.currenttransaction()
1957 tr = self.currenttransaction()
1961 hookargs = {}
1958 hookargs = {}
1962 if tr is not None:
1959 if tr is not None:
1963 hookargs.update(tr.hookargs)
1960 hookargs.update(tr.hookargs)
1964 hookargs['namespace'] = namespace
1961 hookargs['namespace'] = namespace
1965 hookargs['key'] = key
1962 hookargs['key'] = key
1966 hookargs['old'] = old
1963 hookargs['old'] = old
1967 hookargs['new'] = new
1964 hookargs['new'] = new
1968 self.hook('prepushkey', throw=True, **hookargs)
1965 self.hook('prepushkey', throw=True, **hookargs)
1969 except error.HookAbort as exc:
1966 except error.HookAbort as exc:
1970 self.ui.write_err(_("pushkey-abort: %s\n") % exc)
1967 self.ui.write_err(_("pushkey-abort: %s\n") % exc)
1971 if exc.hint:
1968 if exc.hint:
1972 self.ui.write_err(_("(%s)\n") % exc.hint)
1969 self.ui.write_err(_("(%s)\n") % exc.hint)
1973 return False
1970 return False
1974 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
1971 self.ui.debug('pushing key for "%s:%s"\n' % (namespace, key))
1975 ret = pushkey.push(self, namespace, key, old, new)
1972 ret = pushkey.push(self, namespace, key, old, new)
1976 def runhook():
1973 def runhook():
1977 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
1974 self.hook('pushkey', namespace=namespace, key=key, old=old, new=new,
1978 ret=ret)
1975 ret=ret)
1979 self._afterlock(runhook)
1976 self._afterlock(runhook)
1980 return ret
1977 return ret
1981
1978
1982 def listkeys(self, namespace):
1979 def listkeys(self, namespace):
1983 self.hook('prelistkeys', throw=True, namespace=namespace)
1980 self.hook('prelistkeys', throw=True, namespace=namespace)
1984 self.ui.debug('listing keys for "%s"\n' % namespace)
1981 self.ui.debug('listing keys for "%s"\n' % namespace)
1985 values = pushkey.list(self, namespace)
1982 values = pushkey.list(self, namespace)
1986 self.hook('listkeys', namespace=namespace, values=values)
1983 self.hook('listkeys', namespace=namespace, values=values)
1987 return values
1984 return values
1988
1985
1989 def debugwireargs(self, one, two, three=None, four=None, five=None):
1986 def debugwireargs(self, one, two, three=None, four=None, five=None):
1990 '''used to test argument passing over the wire'''
1987 '''used to test argument passing over the wire'''
1991 return "%s %s %s %s %s" % (one, two, three, four, five)
1988 return "%s %s %s %s %s" % (one, two, three, four, five)
1992
1989
1993 def savecommitmessage(self, text):
1990 def savecommitmessage(self, text):
1994 fp = self.vfs('last-message.txt', 'wb')
1991 fp = self.vfs('last-message.txt', 'wb')
1995 try:
1992 try:
1996 fp.write(text)
1993 fp.write(text)
1997 finally:
1994 finally:
1998 fp.close()
1995 fp.close()
1999 return self.pathto(fp.name[len(self.root) + 1:])
1996 return self.pathto(fp.name[len(self.root) + 1:])
2000
1997
2001 # used to avoid circular references so destructors work
1998 # used to avoid circular references so destructors work
2002 def aftertrans(files):
1999 def aftertrans(files):
2003 renamefiles = [tuple(t) for t in files]
2000 renamefiles = [tuple(t) for t in files]
2004 def a():
2001 def a():
2005 for vfs, src, dest in renamefiles:
2002 for vfs, src, dest in renamefiles:
2006 # if src and dest refer to a same file, vfs.rename is a no-op,
2003 # if src and dest refer to a same file, vfs.rename is a no-op,
2007 # leaving both src and dest on disk. delete dest to make sure
2004 # leaving both src and dest on disk. delete dest to make sure
2008 # the rename couldn't be such a no-op.
2005 # the rename couldn't be such a no-op.
2009 vfs.tryunlink(dest)
2006 vfs.tryunlink(dest)
2010 try:
2007 try:
2011 vfs.rename(src, dest)
2008 vfs.rename(src, dest)
2012 except OSError: # journal file does not yet exist
2009 except OSError: # journal file does not yet exist
2013 pass
2010 pass
2014 return a
2011 return a
2015
2012
2016 def undoname(fn):
2013 def undoname(fn):
2017 base, name = os.path.split(fn)
2014 base, name = os.path.split(fn)
2018 assert name.startswith('journal')
2015 assert name.startswith('journal')
2019 return os.path.join(base, name.replace('journal', 'undo', 1))
2016 return os.path.join(base, name.replace('journal', 'undo', 1))
2020
2017
2021 def instance(ui, path, create):
2018 def instance(ui, path, create):
2022 return localrepository(ui, util.urllocalpath(path), create)
2019 return localrepository(ui, util.urllocalpath(path), create)
2023
2020
2024 def islocal(path):
2021 def islocal(path):
2025 return True
2022 return True
2026
2023
2027 def newreporequirements(repo):
2024 def newreporequirements(repo):
2028 """Determine the set of requirements for a new local repository.
2025 """Determine the set of requirements for a new local repository.
2029
2026
2030 Extensions can wrap this function to specify custom requirements for
2027 Extensions can wrap this function to specify custom requirements for
2031 new repositories.
2028 new repositories.
2032 """
2029 """
2033 ui = repo.ui
2030 ui = repo.ui
2034 requirements = set(['revlogv1'])
2031 requirements = set(['revlogv1'])
2035 if ui.configbool('format', 'usestore', True):
2032 if ui.configbool('format', 'usestore', True):
2036 requirements.add('store')
2033 requirements.add('store')
2037 if ui.configbool('format', 'usefncache', True):
2034 if ui.configbool('format', 'usefncache', True):
2038 requirements.add('fncache')
2035 requirements.add('fncache')
2039 if ui.configbool('format', 'dotencode', True):
2036 if ui.configbool('format', 'dotencode', True):
2040 requirements.add('dotencode')
2037 requirements.add('dotencode')
2041
2038
2042 compengine = ui.config('experimental', 'format.compression', 'zlib')
2039 compengine = ui.config('experimental', 'format.compression', 'zlib')
2043 if compengine not in util.compengines:
2040 if compengine not in util.compengines:
2044 raise error.Abort(_('compression engine %s defined by '
2041 raise error.Abort(_('compression engine %s defined by '
2045 'experimental.format.compression not available') %
2042 'experimental.format.compression not available') %
2046 compengine,
2043 compengine,
2047 hint=_('run "hg debuginstall" to list available '
2044 hint=_('run "hg debuginstall" to list available '
2048 'compression engines'))
2045 'compression engines'))
2049
2046
2050 # zlib is the historical default and doesn't need an explicit requirement.
2047 # zlib is the historical default and doesn't need an explicit requirement.
2051 if compengine != 'zlib':
2048 if compengine != 'zlib':
2052 requirements.add('exp-compression-%s' % compengine)
2049 requirements.add('exp-compression-%s' % compengine)
2053
2050
2054 if scmutil.gdinitconfig(ui):
2051 if scmutil.gdinitconfig(ui):
2055 requirements.add('generaldelta')
2052 requirements.add('generaldelta')
2056 if ui.configbool('experimental', 'treemanifest', False):
2053 if ui.configbool('experimental', 'treemanifest', False):
2057 requirements.add('treemanifest')
2054 requirements.add('treemanifest')
2058 if ui.configbool('experimental', 'manifestv2', False):
2055 if ui.configbool('experimental', 'manifestv2', False):
2059 requirements.add('manifestv2')
2056 requirements.add('manifestv2')
2060
2057
2061 return requirements
2058 return requirements
@@ -1,2147 +1,2138 b''
1 # revlog.py - storage back-end for mercurial
1 # revlog.py - storage back-end for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@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 """Storage back-end for Mercurial.
8 """Storage back-end for Mercurial.
9
9
10 This provides efficient delta storage with O(1) retrieve and append
10 This provides efficient delta storage with O(1) retrieve and append
11 and O(changes) merge between branches.
11 and O(changes) merge between branches.
12 """
12 """
13
13
14 from __future__ import absolute_import
14 from __future__ import absolute_import
15
15
16 import collections
16 import collections
17 import errno
17 import errno
18 import hashlib
18 import hashlib
19 import os
19 import os
20 import struct
20 import struct
21 import zlib
21 import zlib
22
22
23 # import stuff from node for others to import from revlog
23 # import stuff from node for others to import from revlog
24 from .node import (
24 from .node import (
25 bin,
25 bin,
26 hex,
26 hex,
27 nullid,
27 nullid,
28 nullrev,
28 nullrev,
29 )
29 )
30 from .i18n import _
30 from .i18n import _
31 from . import (
31 from . import (
32 ancestor,
32 ancestor,
33 error,
33 error,
34 mdiff,
34 mdiff,
35 parsers,
35 parsers,
36 pycompat,
36 pycompat,
37 templatefilters,
37 templatefilters,
38 util,
38 util,
39 )
39 )
40
40
41 _pack = struct.pack
41 _pack = struct.pack
42 _unpack = struct.unpack
42 _unpack = struct.unpack
43 # Aliased for performance.
43 # Aliased for performance.
44 _zlibdecompress = zlib.decompress
44 _zlibdecompress = zlib.decompress
45
45
46 # revlog header flags
46 # revlog header flags
47 REVLOGV0 = 0
47 REVLOGV0 = 0
48 REVLOGNG = 1
48 REVLOGNG = 1
49 REVLOGNGINLINEDATA = (1 << 16)
49 REVLOGNGINLINEDATA = (1 << 16)
50 REVLOGGENERALDELTA = (1 << 17)
50 REVLOGGENERALDELTA = (1 << 17)
51 REVLOG_DEFAULT_FLAGS = REVLOGNGINLINEDATA
51 REVLOG_DEFAULT_FLAGS = REVLOGNGINLINEDATA
52 REVLOG_DEFAULT_FORMAT = REVLOGNG
52 REVLOG_DEFAULT_FORMAT = REVLOGNG
53 REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
53 REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
54 REVLOGNG_FLAGS = REVLOGNGINLINEDATA | REVLOGGENERALDELTA
54 REVLOGNG_FLAGS = REVLOGNGINLINEDATA | REVLOGGENERALDELTA
55
55
56 # revlog index flags
56 # revlog index flags
57 REVIDX_ISCENSORED = (1 << 15) # revision has censor metadata, must be verified
57 REVIDX_ISCENSORED = (1 << 15) # revision has censor metadata, must be verified
58 REVIDX_ELLIPSIS = (1 << 14) # revision hash does not match data (narrowhg)
58 REVIDX_ELLIPSIS = (1 << 14) # revision hash does not match data (narrowhg)
59 REVIDX_EXTSTORED = (1 << 13) # revision data is stored externally
59 REVIDX_EXTSTORED = (1 << 13) # revision data is stored externally
60 REVIDX_DEFAULT_FLAGS = 0
60 REVIDX_DEFAULT_FLAGS = 0
61 # stable order in which flags need to be processed and their processors applied
61 # stable order in which flags need to be processed and their processors applied
62 REVIDX_FLAGS_ORDER = [
62 REVIDX_FLAGS_ORDER = [
63 REVIDX_ISCENSORED,
63 REVIDX_ISCENSORED,
64 REVIDX_ELLIPSIS,
64 REVIDX_ELLIPSIS,
65 REVIDX_EXTSTORED,
65 REVIDX_EXTSTORED,
66 ]
66 ]
67 REVIDX_KNOWN_FLAGS = util.bitsfrom(REVIDX_FLAGS_ORDER)
67 REVIDX_KNOWN_FLAGS = util.bitsfrom(REVIDX_FLAGS_ORDER)
68
68
69 # max size of revlog with inline data
69 # max size of revlog with inline data
70 _maxinline = 131072
70 _maxinline = 131072
71 _chunksize = 1048576
71 _chunksize = 1048576
72
72
73 RevlogError = error.RevlogError
73 RevlogError = error.RevlogError
74 LookupError = error.LookupError
74 LookupError = error.LookupError
75 CensoredNodeError = error.CensoredNodeError
75 CensoredNodeError = error.CensoredNodeError
76 ProgrammingError = error.ProgrammingError
76 ProgrammingError = error.ProgrammingError
77
77
78 # Store flag processors (cf. 'addflagprocessor()' to register)
78 # Store flag processors (cf. 'addflagprocessor()' to register)
79 _flagprocessors = {
79 _flagprocessors = {
80 REVIDX_ISCENSORED: None,
80 REVIDX_ISCENSORED: None,
81 }
81 }
82
82
83 def addflagprocessor(flag, processor):
83 def addflagprocessor(flag, processor):
84 """Register a flag processor on a revision data flag.
84 """Register a flag processor on a revision data flag.
85
85
86 Invariant:
86 Invariant:
87 - Flags need to be defined in REVIDX_KNOWN_FLAGS and REVIDX_FLAGS_ORDER.
87 - Flags need to be defined in REVIDX_KNOWN_FLAGS and REVIDX_FLAGS_ORDER.
88 - Only one flag processor can be registered on a specific flag.
88 - Only one flag processor can be registered on a specific flag.
89 - flagprocessors must be 3-tuples of functions (read, write, raw) with the
89 - flagprocessors must be 3-tuples of functions (read, write, raw) with the
90 following signatures:
90 following signatures:
91 - (read) f(self, rawtext) -> text, bool
91 - (read) f(self, rawtext) -> text, bool
92 - (write) f(self, text) -> rawtext, bool
92 - (write) f(self, text) -> rawtext, bool
93 - (raw) f(self, rawtext) -> bool
93 - (raw) f(self, rawtext) -> bool
94 "text" is presented to the user. "rawtext" is stored in revlog data, not
94 "text" is presented to the user. "rawtext" is stored in revlog data, not
95 directly visible to the user.
95 directly visible to the user.
96 The boolean returned by these transforms is used to determine whether
96 The boolean returned by these transforms is used to determine whether
97 the returned text can be used for hash integrity checking. For example,
97 the returned text can be used for hash integrity checking. For example,
98 if "write" returns False, then "text" is used to generate hash. If
98 if "write" returns False, then "text" is used to generate hash. If
99 "write" returns True, that basically means "rawtext" returned by "write"
99 "write" returns True, that basically means "rawtext" returned by "write"
100 should be used to generate hash. Usually, "write" and "read" return
100 should be used to generate hash. Usually, "write" and "read" return
101 different booleans. And "raw" returns a same boolean as "write".
101 different booleans. And "raw" returns a same boolean as "write".
102
102
103 Note: The 'raw' transform is used for changegroup generation and in some
103 Note: The 'raw' transform is used for changegroup generation and in some
104 debug commands. In this case the transform only indicates whether the
104 debug commands. In this case the transform only indicates whether the
105 contents can be used for hash integrity checks.
105 contents can be used for hash integrity checks.
106 """
106 """
107 if not flag & REVIDX_KNOWN_FLAGS:
107 if not flag & REVIDX_KNOWN_FLAGS:
108 msg = _("cannot register processor on unknown flag '%#x'.") % (flag)
108 msg = _("cannot register processor on unknown flag '%#x'.") % (flag)
109 raise ProgrammingError(msg)
109 raise ProgrammingError(msg)
110 if flag not in REVIDX_FLAGS_ORDER:
110 if flag not in REVIDX_FLAGS_ORDER:
111 msg = _("flag '%#x' undefined in REVIDX_FLAGS_ORDER.") % (flag)
111 msg = _("flag '%#x' undefined in REVIDX_FLAGS_ORDER.") % (flag)
112 raise ProgrammingError(msg)
112 raise ProgrammingError(msg)
113 if flag in _flagprocessors:
113 if flag in _flagprocessors:
114 msg = _("cannot register multiple processors on flag '%#x'.") % (flag)
114 msg = _("cannot register multiple processors on flag '%#x'.") % (flag)
115 raise error.Abort(msg)
115 raise error.Abort(msg)
116 _flagprocessors[flag] = processor
116 _flagprocessors[flag] = processor
117
117
118 def getoffset(q):
118 def getoffset(q):
119 return int(q >> 16)
119 return int(q >> 16)
120
120
121 def gettype(q):
121 def gettype(q):
122 return int(q & 0xFFFF)
122 return int(q & 0xFFFF)
123
123
124 def offset_type(offset, type):
124 def offset_type(offset, type):
125 if (type & ~REVIDX_KNOWN_FLAGS) != 0:
125 if (type & ~REVIDX_KNOWN_FLAGS) != 0:
126 raise ValueError('unknown revlog index flags')
126 raise ValueError('unknown revlog index flags')
127 return int(int(offset) << 16 | type)
127 return int(int(offset) << 16 | type)
128
128
129 _nullhash = hashlib.sha1(nullid)
129 _nullhash = hashlib.sha1(nullid)
130
130
131 def hash(text, p1, p2):
131 def hash(text, p1, p2):
132 """generate a hash from the given text and its parent hashes
132 """generate a hash from the given text and its parent hashes
133
133
134 This hash combines both the current file contents and its history
134 This hash combines both the current file contents and its history
135 in a manner that makes it easy to distinguish nodes with the same
135 in a manner that makes it easy to distinguish nodes with the same
136 content in the revision graph.
136 content in the revision graph.
137 """
137 """
138 # As of now, if one of the parent node is null, p2 is null
138 # As of now, if one of the parent node is null, p2 is null
139 if p2 == nullid:
139 if p2 == nullid:
140 # deep copy of a hash is faster than creating one
140 # deep copy of a hash is faster than creating one
141 s = _nullhash.copy()
141 s = _nullhash.copy()
142 s.update(p1)
142 s.update(p1)
143 else:
143 else:
144 # none of the parent nodes are nullid
144 # none of the parent nodes are nullid
145 l = [p1, p2]
145 l = [p1, p2]
146 l.sort()
146 l.sort()
147 s = hashlib.sha1(l[0])
147 s = hashlib.sha1(l[0])
148 s.update(l[1])
148 s.update(l[1])
149 s.update(text)
149 s.update(text)
150 return s.digest()
150 return s.digest()
151
151
152 # index v0:
152 # index v0:
153 # 4 bytes: offset
153 # 4 bytes: offset
154 # 4 bytes: compressed length
154 # 4 bytes: compressed length
155 # 4 bytes: base rev
155 # 4 bytes: base rev
156 # 4 bytes: link rev
156 # 4 bytes: link rev
157 # 20 bytes: parent 1 nodeid
157 # 20 bytes: parent 1 nodeid
158 # 20 bytes: parent 2 nodeid
158 # 20 bytes: parent 2 nodeid
159 # 20 bytes: nodeid
159 # 20 bytes: nodeid
160 indexformatv0 = ">4l20s20s20s"
160 indexformatv0 = ">4l20s20s20s"
161
161
162 class revlogoldio(object):
162 class revlogoldio(object):
163 def __init__(self):
163 def __init__(self):
164 self.size = struct.calcsize(indexformatv0)
164 self.size = struct.calcsize(indexformatv0)
165
165
166 def parseindex(self, data, inline):
166 def parseindex(self, data, inline):
167 s = self.size
167 s = self.size
168 index = []
168 index = []
169 nodemap = {nullid: nullrev}
169 nodemap = {nullid: nullrev}
170 n = off = 0
170 n = off = 0
171 l = len(data)
171 l = len(data)
172 while off + s <= l:
172 while off + s <= l:
173 cur = data[off:off + s]
173 cur = data[off:off + s]
174 off += s
174 off += s
175 e = _unpack(indexformatv0, cur)
175 e = _unpack(indexformatv0, cur)
176 # transform to revlogv1 format
176 # transform to revlogv1 format
177 e2 = (offset_type(e[0], 0), e[1], -1, e[2], e[3],
177 e2 = (offset_type(e[0], 0), e[1], -1, e[2], e[3],
178 nodemap.get(e[4], nullrev), nodemap.get(e[5], nullrev), e[6])
178 nodemap.get(e[4], nullrev), nodemap.get(e[5], nullrev), e[6])
179 index.append(e2)
179 index.append(e2)
180 nodemap[e[6]] = n
180 nodemap[e[6]] = n
181 n += 1
181 n += 1
182
182
183 # add the magic null revision at -1
183 # add the magic null revision at -1
184 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
184 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
185
185
186 return index, nodemap, None
186 return index, nodemap, None
187
187
188 def packentry(self, entry, node, version, rev):
188 def packentry(self, entry, node, version, rev):
189 if gettype(entry[0]):
189 if gettype(entry[0]):
190 raise RevlogError(_("index entry flags need RevlogNG"))
190 raise RevlogError(_("index entry flags need RevlogNG"))
191 e2 = (getoffset(entry[0]), entry[1], entry[3], entry[4],
191 e2 = (getoffset(entry[0]), entry[1], entry[3], entry[4],
192 node(entry[5]), node(entry[6]), entry[7])
192 node(entry[5]), node(entry[6]), entry[7])
193 return _pack(indexformatv0, *e2)
193 return _pack(indexformatv0, *e2)
194
194
195 # index ng:
195 # index ng:
196 # 6 bytes: offset
196 # 6 bytes: offset
197 # 2 bytes: flags
197 # 2 bytes: flags
198 # 4 bytes: compressed length
198 # 4 bytes: compressed length
199 # 4 bytes: uncompressed length
199 # 4 bytes: uncompressed length
200 # 4 bytes: base rev
200 # 4 bytes: base rev
201 # 4 bytes: link rev
201 # 4 bytes: link rev
202 # 4 bytes: parent 1 rev
202 # 4 bytes: parent 1 rev
203 # 4 bytes: parent 2 rev
203 # 4 bytes: parent 2 rev
204 # 32 bytes: nodeid
204 # 32 bytes: nodeid
205 indexformatng = ">Qiiiiii20s12x"
205 indexformatng = ">Qiiiiii20s12x"
206 versionformat = ">I"
206 versionformat = ">I"
207
207
208 # corresponds to uncompressed length of indexformatng (2 gigs, 4-byte
208 # corresponds to uncompressed length of indexformatng (2 gigs, 4-byte
209 # signed integer)
209 # signed integer)
210 _maxentrysize = 0x7fffffff
210 _maxentrysize = 0x7fffffff
211
211
212 class revlogio(object):
212 class revlogio(object):
213 def __init__(self):
213 def __init__(self):
214 self.size = struct.calcsize(indexformatng)
214 self.size = struct.calcsize(indexformatng)
215
215
216 def parseindex(self, data, inline):
216 def parseindex(self, data, inline):
217 # call the C implementation to parse the index data
217 # call the C implementation to parse the index data
218 index, cache = parsers.parse_index2(data, inline)
218 index, cache = parsers.parse_index2(data, inline)
219 return index, getattr(index, 'nodemap', None), cache
219 return index, getattr(index, 'nodemap', None), cache
220
220
221 def packentry(self, entry, node, version, rev):
221 def packentry(self, entry, node, version, rev):
222 p = _pack(indexformatng, *entry)
222 p = _pack(indexformatng, *entry)
223 if rev == 0:
223 if rev == 0:
224 p = _pack(versionformat, version) + p[4:]
224 p = _pack(versionformat, version) + p[4:]
225 return p
225 return p
226
226
227 class revlog(object):
227 class revlog(object):
228 """
228 """
229 the underlying revision storage object
229 the underlying revision storage object
230
230
231 A revlog consists of two parts, an index and the revision data.
231 A revlog consists of two parts, an index and the revision data.
232
232
233 The index is a file with a fixed record size containing
233 The index is a file with a fixed record size containing
234 information on each revision, including its nodeid (hash), the
234 information on each revision, including its nodeid (hash), the
235 nodeids of its parents, the position and offset of its data within
235 nodeids of its parents, the position and offset of its data within
236 the data file, and the revision it's based on. Finally, each entry
236 the data file, and the revision it's based on. Finally, each entry
237 contains a linkrev entry that can serve as a pointer to external
237 contains a linkrev entry that can serve as a pointer to external
238 data.
238 data.
239
239
240 The revision data itself is a linear collection of data chunks.
240 The revision data itself is a linear collection of data chunks.
241 Each chunk represents a revision and is usually represented as a
241 Each chunk represents a revision and is usually represented as a
242 delta against the previous chunk. To bound lookup time, runs of
242 delta against the previous chunk. To bound lookup time, runs of
243 deltas are limited to about 2 times the length of the original
243 deltas are limited to about 2 times the length of the original
244 version data. This makes retrieval of a version proportional to
244 version data. This makes retrieval of a version proportional to
245 its size, or O(1) relative to the number of revisions.
245 its size, or O(1) relative to the number of revisions.
246
246
247 Both pieces of the revlog are written to in an append-only
247 Both pieces of the revlog are written to in an append-only
248 fashion, which means we never need to rewrite a file to insert or
248 fashion, which means we never need to rewrite a file to insert or
249 remove data, and can use some simple techniques to avoid the need
249 remove data, and can use some simple techniques to avoid the need
250 for locking while reading.
250 for locking while reading.
251
251
252 If checkambig, indexfile is opened with checkambig=True at
252 If checkambig, indexfile is opened with checkambig=True at
253 writing, to avoid file stat ambiguity.
253 writing, to avoid file stat ambiguity.
254 """
254 """
255 def __init__(self, opener, indexfile, checkambig=False):
255 def __init__(self, opener, indexfile, checkambig=False):
256 """
256 """
257 create a revlog object
257 create a revlog object
258
258
259 opener is a function that abstracts the file opening operation
259 opener is a function that abstracts the file opening operation
260 and can be used to implement COW semantics or the like.
260 and can be used to implement COW semantics or the like.
261 """
261 """
262 self.indexfile = indexfile
262 self.indexfile = indexfile
263 self.datafile = indexfile[:-2] + ".d"
263 self.datafile = indexfile[:-2] + ".d"
264 self.opener = opener
264 self.opener = opener
265 # When True, indexfile is opened with checkambig=True at writing, to
265 # When True, indexfile is opened with checkambig=True at writing, to
266 # avoid file stat ambiguity.
266 # avoid file stat ambiguity.
267 self._checkambig = checkambig
267 self._checkambig = checkambig
268 # 3-tuple of (node, rev, text) for a raw revision.
268 # 3-tuple of (node, rev, text) for a raw revision.
269 self._cache = None
269 self._cache = None
270 # Maps rev to chain base rev.
270 # Maps rev to chain base rev.
271 self._chainbasecache = util.lrucachedict(100)
271 self._chainbasecache = util.lrucachedict(100)
272 # 2-tuple of (offset, data) of raw data from the revlog at an offset.
272 # 2-tuple of (offset, data) of raw data from the revlog at an offset.
273 self._chunkcache = (0, '')
273 self._chunkcache = (0, '')
274 # How much data to read and cache into the raw revlog data cache.
274 # How much data to read and cache into the raw revlog data cache.
275 self._chunkcachesize = 65536
275 self._chunkcachesize = 65536
276 self._maxchainlen = None
276 self._maxchainlen = None
277 self._aggressivemergedeltas = False
277 self._aggressivemergedeltas = False
278 self.index = []
278 self.index = []
279 # Mapping of partial identifiers to full nodes.
279 # Mapping of partial identifiers to full nodes.
280 self._pcache = {}
280 self._pcache = {}
281 # Mapping of revision integer to full node.
281 # Mapping of revision integer to full node.
282 self._nodecache = {nullid: nullrev}
282 self._nodecache = {nullid: nullrev}
283 self._nodepos = None
283 self._nodepos = None
284 self._compengine = 'zlib'
284 self._compengine = 'zlib'
285 self._maxdeltachainspan = -1
286
285
287 v = REVLOG_DEFAULT_VERSION
286 v = REVLOG_DEFAULT_VERSION
288 opts = getattr(opener, 'options', None)
287 opts = getattr(opener, 'options', None)
289 if opts is not None:
288 if opts is not None:
290 if 'revlogv1' in opts:
289 if 'revlogv1' in opts:
291 if 'generaldelta' in opts:
290 if 'generaldelta' in opts:
292 v |= REVLOGGENERALDELTA
291 v |= REVLOGGENERALDELTA
293 else:
292 else:
294 v = 0
293 v = 0
295 if 'chunkcachesize' in opts:
294 if 'chunkcachesize' in opts:
296 self._chunkcachesize = opts['chunkcachesize']
295 self._chunkcachesize = opts['chunkcachesize']
297 if 'maxchainlen' in opts:
296 if 'maxchainlen' in opts:
298 self._maxchainlen = opts['maxchainlen']
297 self._maxchainlen = opts['maxchainlen']
299 if 'aggressivemergedeltas' in opts:
298 if 'aggressivemergedeltas' in opts:
300 self._aggressivemergedeltas = opts['aggressivemergedeltas']
299 self._aggressivemergedeltas = opts['aggressivemergedeltas']
301 self._lazydeltabase = bool(opts.get('lazydeltabase', False))
300 self._lazydeltabase = bool(opts.get('lazydeltabase', False))
302 if 'compengine' in opts:
301 if 'compengine' in opts:
303 self._compengine = opts['compengine']
302 self._compengine = opts['compengine']
304 if 'maxdeltachainspan' in opts:
305 self._maxdeltachainspan = opts['maxdeltachainspan']
306
303
307 if self._chunkcachesize <= 0:
304 if self._chunkcachesize <= 0:
308 raise RevlogError(_('revlog chunk cache size %r is not greater '
305 raise RevlogError(_('revlog chunk cache size %r is not greater '
309 'than 0') % self._chunkcachesize)
306 'than 0') % self._chunkcachesize)
310 elif self._chunkcachesize & (self._chunkcachesize - 1):
307 elif self._chunkcachesize & (self._chunkcachesize - 1):
311 raise RevlogError(_('revlog chunk cache size %r is not a power '
308 raise RevlogError(_('revlog chunk cache size %r is not a power '
312 'of 2') % self._chunkcachesize)
309 'of 2') % self._chunkcachesize)
313
310
314 indexdata = ''
311 indexdata = ''
315 self._initempty = True
312 self._initempty = True
316 try:
313 try:
317 f = self.opener(self.indexfile)
314 f = self.opener(self.indexfile)
318 indexdata = f.read()
315 indexdata = f.read()
319 f.close()
316 f.close()
320 if len(indexdata) > 0:
317 if len(indexdata) > 0:
321 v = struct.unpack(versionformat, indexdata[:4])[0]
318 v = struct.unpack(versionformat, indexdata[:4])[0]
322 self._initempty = False
319 self._initempty = False
323 except IOError as inst:
320 except IOError as inst:
324 if inst.errno != errno.ENOENT:
321 if inst.errno != errno.ENOENT:
325 raise
322 raise
326
323
327 self.version = v
324 self.version = v
328 self._inline = v & REVLOGNGINLINEDATA
325 self._inline = v & REVLOGNGINLINEDATA
329 self._generaldelta = v & REVLOGGENERALDELTA
326 self._generaldelta = v & REVLOGGENERALDELTA
330 flags = v & ~0xFFFF
327 flags = v & ~0xFFFF
331 fmt = v & 0xFFFF
328 fmt = v & 0xFFFF
332 if fmt == REVLOGV0 and flags:
329 if fmt == REVLOGV0 and flags:
333 raise RevlogError(_("index %s unknown flags %#04x for format v0")
330 raise RevlogError(_("index %s unknown flags %#04x for format v0")
334 % (self.indexfile, flags >> 16))
331 % (self.indexfile, flags >> 16))
335 elif fmt == REVLOGNG and flags & ~REVLOGNG_FLAGS:
332 elif fmt == REVLOGNG and flags & ~REVLOGNG_FLAGS:
336 raise RevlogError(_("index %s unknown flags %#04x for revlogng")
333 raise RevlogError(_("index %s unknown flags %#04x for revlogng")
337 % (self.indexfile, flags >> 16))
334 % (self.indexfile, flags >> 16))
338 elif fmt > REVLOGNG:
335 elif fmt > REVLOGNG:
339 raise RevlogError(_("index %s unknown format %d")
336 raise RevlogError(_("index %s unknown format %d")
340 % (self.indexfile, fmt))
337 % (self.indexfile, fmt))
341
338
342 self.storedeltachains = True
339 self.storedeltachains = True
343
340
344 self._io = revlogio()
341 self._io = revlogio()
345 if self.version == REVLOGV0:
342 if self.version == REVLOGV0:
346 self._io = revlogoldio()
343 self._io = revlogoldio()
347 try:
344 try:
348 d = self._io.parseindex(indexdata, self._inline)
345 d = self._io.parseindex(indexdata, self._inline)
349 except (ValueError, IndexError):
346 except (ValueError, IndexError):
350 raise RevlogError(_("index %s is corrupted") % (self.indexfile))
347 raise RevlogError(_("index %s is corrupted") % (self.indexfile))
351 self.index, nodemap, self._chunkcache = d
348 self.index, nodemap, self._chunkcache = d
352 if nodemap is not None:
349 if nodemap is not None:
353 self.nodemap = self._nodecache = nodemap
350 self.nodemap = self._nodecache = nodemap
354 if not self._chunkcache:
351 if not self._chunkcache:
355 self._chunkclear()
352 self._chunkclear()
356 # revnum -> (chain-length, sum-delta-length)
353 # revnum -> (chain-length, sum-delta-length)
357 self._chaininfocache = {}
354 self._chaininfocache = {}
358 # revlog header -> revlog compressor
355 # revlog header -> revlog compressor
359 self._decompressors = {}
356 self._decompressors = {}
360
357
361 @util.propertycache
358 @util.propertycache
362 def _compressor(self):
359 def _compressor(self):
363 return util.compengines[self._compengine].revlogcompressor()
360 return util.compengines[self._compengine].revlogcompressor()
364
361
365 def tip(self):
362 def tip(self):
366 return self.node(len(self.index) - 2)
363 return self.node(len(self.index) - 2)
367 def __contains__(self, rev):
364 def __contains__(self, rev):
368 return 0 <= rev < len(self)
365 return 0 <= rev < len(self)
369 def __len__(self):
366 def __len__(self):
370 return len(self.index) - 1
367 return len(self.index) - 1
371 def __iter__(self):
368 def __iter__(self):
372 return iter(xrange(len(self)))
369 return iter(xrange(len(self)))
373 def revs(self, start=0, stop=None):
370 def revs(self, start=0, stop=None):
374 """iterate over all rev in this revlog (from start to stop)"""
371 """iterate over all rev in this revlog (from start to stop)"""
375 step = 1
372 step = 1
376 if stop is not None:
373 if stop is not None:
377 if start > stop:
374 if start > stop:
378 step = -1
375 step = -1
379 stop += step
376 stop += step
380 else:
377 else:
381 stop = len(self)
378 stop = len(self)
382 return xrange(start, stop, step)
379 return xrange(start, stop, step)
383
380
384 @util.propertycache
381 @util.propertycache
385 def nodemap(self):
382 def nodemap(self):
386 self.rev(self.node(0))
383 self.rev(self.node(0))
387 return self._nodecache
384 return self._nodecache
388
385
389 def hasnode(self, node):
386 def hasnode(self, node):
390 try:
387 try:
391 self.rev(node)
388 self.rev(node)
392 return True
389 return True
393 except KeyError:
390 except KeyError:
394 return False
391 return False
395
392
396 def clearcaches(self):
393 def clearcaches(self):
397 self._cache = None
394 self._cache = None
398 self._chainbasecache.clear()
395 self._chainbasecache.clear()
399 self._chunkcache = (0, '')
396 self._chunkcache = (0, '')
400 self._pcache = {}
397 self._pcache = {}
401
398
402 try:
399 try:
403 self._nodecache.clearcaches()
400 self._nodecache.clearcaches()
404 except AttributeError:
401 except AttributeError:
405 self._nodecache = {nullid: nullrev}
402 self._nodecache = {nullid: nullrev}
406 self._nodepos = None
403 self._nodepos = None
407
404
408 def rev(self, node):
405 def rev(self, node):
409 try:
406 try:
410 return self._nodecache[node]
407 return self._nodecache[node]
411 except TypeError:
408 except TypeError:
412 raise
409 raise
413 except RevlogError:
410 except RevlogError:
414 # parsers.c radix tree lookup failed
411 # parsers.c radix tree lookup failed
415 raise LookupError(node, self.indexfile, _('no node'))
412 raise LookupError(node, self.indexfile, _('no node'))
416 except KeyError:
413 except KeyError:
417 # pure python cache lookup failed
414 # pure python cache lookup failed
418 n = self._nodecache
415 n = self._nodecache
419 i = self.index
416 i = self.index
420 p = self._nodepos
417 p = self._nodepos
421 if p is None:
418 if p is None:
422 p = len(i) - 2
419 p = len(i) - 2
423 for r in xrange(p, -1, -1):
420 for r in xrange(p, -1, -1):
424 v = i[r][7]
421 v = i[r][7]
425 n[v] = r
422 n[v] = r
426 if v == node:
423 if v == node:
427 self._nodepos = r - 1
424 self._nodepos = r - 1
428 return r
425 return r
429 raise LookupError(node, self.indexfile, _('no node'))
426 raise LookupError(node, self.indexfile, _('no node'))
430
427
431 # Accessors for index entries.
428 # Accessors for index entries.
432
429
433 # First tuple entry is 8 bytes. First 6 bytes are offset. Last 2 bytes
430 # First tuple entry is 8 bytes. First 6 bytes are offset. Last 2 bytes
434 # are flags.
431 # are flags.
435 def start(self, rev):
432 def start(self, rev):
436 return int(self.index[rev][0] >> 16)
433 return int(self.index[rev][0] >> 16)
437
434
438 def flags(self, rev):
435 def flags(self, rev):
439 return self.index[rev][0] & 0xFFFF
436 return self.index[rev][0] & 0xFFFF
440
437
441 def length(self, rev):
438 def length(self, rev):
442 return self.index[rev][1]
439 return self.index[rev][1]
443
440
444 def rawsize(self, rev):
441 def rawsize(self, rev):
445 """return the length of the uncompressed text for a given revision"""
442 """return the length of the uncompressed text for a given revision"""
446 l = self.index[rev][2]
443 l = self.index[rev][2]
447 if l >= 0:
444 if l >= 0:
448 return l
445 return l
449
446
450 t = self.revision(rev, raw=True)
447 t = self.revision(rev, raw=True)
451 return len(t)
448 return len(t)
452
449
453 def size(self, rev):
450 def size(self, rev):
454 """length of non-raw text (processed by a "read" flag processor)"""
451 """length of non-raw text (processed by a "read" flag processor)"""
455 # fast path: if no "read" flag processor could change the content,
452 # fast path: if no "read" flag processor could change the content,
456 # size is rawsize. note: ELLIPSIS is known to not change the content.
453 # size is rawsize. note: ELLIPSIS is known to not change the content.
457 flags = self.flags(rev)
454 flags = self.flags(rev)
458 if flags & (REVIDX_KNOWN_FLAGS ^ REVIDX_ELLIPSIS) == 0:
455 if flags & (REVIDX_KNOWN_FLAGS ^ REVIDX_ELLIPSIS) == 0:
459 return self.rawsize(rev)
456 return self.rawsize(rev)
460
457
461 return len(self.revision(rev, raw=False))
458 return len(self.revision(rev, raw=False))
462
459
463 def chainbase(self, rev):
460 def chainbase(self, rev):
464 base = self._chainbasecache.get(rev)
461 base = self._chainbasecache.get(rev)
465 if base is not None:
462 if base is not None:
466 return base
463 return base
467
464
468 index = self.index
465 index = self.index
469 base = index[rev][3]
466 base = index[rev][3]
470 while base != rev:
467 while base != rev:
471 rev = base
468 rev = base
472 base = index[rev][3]
469 base = index[rev][3]
473
470
474 self._chainbasecache[rev] = base
471 self._chainbasecache[rev] = base
475 return base
472 return base
476
473
477 def linkrev(self, rev):
474 def linkrev(self, rev):
478 return self.index[rev][4]
475 return self.index[rev][4]
479
476
480 def parentrevs(self, rev):
477 def parentrevs(self, rev):
481 return self.index[rev][5:7]
478 return self.index[rev][5:7]
482
479
483 def node(self, rev):
480 def node(self, rev):
484 return self.index[rev][7]
481 return self.index[rev][7]
485
482
486 # Derived from index values.
483 # Derived from index values.
487
484
488 def end(self, rev):
485 def end(self, rev):
489 return self.start(rev) + self.length(rev)
486 return self.start(rev) + self.length(rev)
490
487
491 def parents(self, node):
488 def parents(self, node):
492 i = self.index
489 i = self.index
493 d = i[self.rev(node)]
490 d = i[self.rev(node)]
494 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
491 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
495
492
496 def chainlen(self, rev):
493 def chainlen(self, rev):
497 return self._chaininfo(rev)[0]
494 return self._chaininfo(rev)[0]
498
495
499 def _chaininfo(self, rev):
496 def _chaininfo(self, rev):
500 chaininfocache = self._chaininfocache
497 chaininfocache = self._chaininfocache
501 if rev in chaininfocache:
498 if rev in chaininfocache:
502 return chaininfocache[rev]
499 return chaininfocache[rev]
503 index = self.index
500 index = self.index
504 generaldelta = self._generaldelta
501 generaldelta = self._generaldelta
505 iterrev = rev
502 iterrev = rev
506 e = index[iterrev]
503 e = index[iterrev]
507 clen = 0
504 clen = 0
508 compresseddeltalen = 0
505 compresseddeltalen = 0
509 while iterrev != e[3]:
506 while iterrev != e[3]:
510 clen += 1
507 clen += 1
511 compresseddeltalen += e[1]
508 compresseddeltalen += e[1]
512 if generaldelta:
509 if generaldelta:
513 iterrev = e[3]
510 iterrev = e[3]
514 else:
511 else:
515 iterrev -= 1
512 iterrev -= 1
516 if iterrev in chaininfocache:
513 if iterrev in chaininfocache:
517 t = chaininfocache[iterrev]
514 t = chaininfocache[iterrev]
518 clen += t[0]
515 clen += t[0]
519 compresseddeltalen += t[1]
516 compresseddeltalen += t[1]
520 break
517 break
521 e = index[iterrev]
518 e = index[iterrev]
522 else:
519 else:
523 # Add text length of base since decompressing that also takes
520 # Add text length of base since decompressing that also takes
524 # work. For cache hits the length is already included.
521 # work. For cache hits the length is already included.
525 compresseddeltalen += e[1]
522 compresseddeltalen += e[1]
526 r = (clen, compresseddeltalen)
523 r = (clen, compresseddeltalen)
527 chaininfocache[rev] = r
524 chaininfocache[rev] = r
528 return r
525 return r
529
526
530 def _deltachain(self, rev, stoprev=None):
527 def _deltachain(self, rev, stoprev=None):
531 """Obtain the delta chain for a revision.
528 """Obtain the delta chain for a revision.
532
529
533 ``stoprev`` specifies a revision to stop at. If not specified, we
530 ``stoprev`` specifies a revision to stop at. If not specified, we
534 stop at the base of the chain.
531 stop at the base of the chain.
535
532
536 Returns a 2-tuple of (chain, stopped) where ``chain`` is a list of
533 Returns a 2-tuple of (chain, stopped) where ``chain`` is a list of
537 revs in ascending order and ``stopped`` is a bool indicating whether
534 revs in ascending order and ``stopped`` is a bool indicating whether
538 ``stoprev`` was hit.
535 ``stoprev`` was hit.
539 """
536 """
540 chain = []
537 chain = []
541
538
542 # Alias to prevent attribute lookup in tight loop.
539 # Alias to prevent attribute lookup in tight loop.
543 index = self.index
540 index = self.index
544 generaldelta = self._generaldelta
541 generaldelta = self._generaldelta
545
542
546 iterrev = rev
543 iterrev = rev
547 e = index[iterrev]
544 e = index[iterrev]
548 while iterrev != e[3] and iterrev != stoprev:
545 while iterrev != e[3] and iterrev != stoprev:
549 chain.append(iterrev)
546 chain.append(iterrev)
550 if generaldelta:
547 if generaldelta:
551 iterrev = e[3]
548 iterrev = e[3]
552 else:
549 else:
553 iterrev -= 1
550 iterrev -= 1
554 e = index[iterrev]
551 e = index[iterrev]
555
552
556 if iterrev == stoprev:
553 if iterrev == stoprev:
557 stopped = True
554 stopped = True
558 else:
555 else:
559 chain.append(iterrev)
556 chain.append(iterrev)
560 stopped = False
557 stopped = False
561
558
562 chain.reverse()
559 chain.reverse()
563 return chain, stopped
560 return chain, stopped
564
561
565 def ancestors(self, revs, stoprev=0, inclusive=False):
562 def ancestors(self, revs, stoprev=0, inclusive=False):
566 """Generate the ancestors of 'revs' in reverse topological order.
563 """Generate the ancestors of 'revs' in reverse topological order.
567 Does not generate revs lower than stoprev.
564 Does not generate revs lower than stoprev.
568
565
569 See the documentation for ancestor.lazyancestors for more details."""
566 See the documentation for ancestor.lazyancestors for more details."""
570
567
571 return ancestor.lazyancestors(self.parentrevs, revs, stoprev=stoprev,
568 return ancestor.lazyancestors(self.parentrevs, revs, stoprev=stoprev,
572 inclusive=inclusive)
569 inclusive=inclusive)
573
570
574 def descendants(self, revs):
571 def descendants(self, revs):
575 """Generate the descendants of 'revs' in revision order.
572 """Generate the descendants of 'revs' in revision order.
576
573
577 Yield a sequence of revision numbers starting with a child of
574 Yield a sequence of revision numbers starting with a child of
578 some rev in revs, i.e., each revision is *not* considered a
575 some rev in revs, i.e., each revision is *not* considered a
579 descendant of itself. Results are ordered by revision number (a
576 descendant of itself. Results are ordered by revision number (a
580 topological sort)."""
577 topological sort)."""
581 first = min(revs)
578 first = min(revs)
582 if first == nullrev:
579 if first == nullrev:
583 for i in self:
580 for i in self:
584 yield i
581 yield i
585 return
582 return
586
583
587 seen = set(revs)
584 seen = set(revs)
588 for i in self.revs(start=first + 1):
585 for i in self.revs(start=first + 1):
589 for x in self.parentrevs(i):
586 for x in self.parentrevs(i):
590 if x != nullrev and x in seen:
587 if x != nullrev and x in seen:
591 seen.add(i)
588 seen.add(i)
592 yield i
589 yield i
593 break
590 break
594
591
595 def findcommonmissing(self, common=None, heads=None):
592 def findcommonmissing(self, common=None, heads=None):
596 """Return a tuple of the ancestors of common and the ancestors of heads
593 """Return a tuple of the ancestors of common and the ancestors of heads
597 that are not ancestors of common. In revset terminology, we return the
594 that are not ancestors of common. In revset terminology, we return the
598 tuple:
595 tuple:
599
596
600 ::common, (::heads) - (::common)
597 ::common, (::heads) - (::common)
601
598
602 The list is sorted by revision number, meaning it is
599 The list is sorted by revision number, meaning it is
603 topologically sorted.
600 topologically sorted.
604
601
605 'heads' and 'common' are both lists of node IDs. If heads is
602 'heads' and 'common' are both lists of node IDs. If heads is
606 not supplied, uses all of the revlog's heads. If common is not
603 not supplied, uses all of the revlog's heads. If common is not
607 supplied, uses nullid."""
604 supplied, uses nullid."""
608 if common is None:
605 if common is None:
609 common = [nullid]
606 common = [nullid]
610 if heads is None:
607 if heads is None:
611 heads = self.heads()
608 heads = self.heads()
612
609
613 common = [self.rev(n) for n in common]
610 common = [self.rev(n) for n in common]
614 heads = [self.rev(n) for n in heads]
611 heads = [self.rev(n) for n in heads]
615
612
616 # we want the ancestors, but inclusive
613 # we want the ancestors, but inclusive
617 class lazyset(object):
614 class lazyset(object):
618 def __init__(self, lazyvalues):
615 def __init__(self, lazyvalues):
619 self.addedvalues = set()
616 self.addedvalues = set()
620 self.lazyvalues = lazyvalues
617 self.lazyvalues = lazyvalues
621
618
622 def __contains__(self, value):
619 def __contains__(self, value):
623 return value in self.addedvalues or value in self.lazyvalues
620 return value in self.addedvalues or value in self.lazyvalues
624
621
625 def __iter__(self):
622 def __iter__(self):
626 added = self.addedvalues
623 added = self.addedvalues
627 for r in added:
624 for r in added:
628 yield r
625 yield r
629 for r in self.lazyvalues:
626 for r in self.lazyvalues:
630 if not r in added:
627 if not r in added:
631 yield r
628 yield r
632
629
633 def add(self, value):
630 def add(self, value):
634 self.addedvalues.add(value)
631 self.addedvalues.add(value)
635
632
636 def update(self, values):
633 def update(self, values):
637 self.addedvalues.update(values)
634 self.addedvalues.update(values)
638
635
639 has = lazyset(self.ancestors(common))
636 has = lazyset(self.ancestors(common))
640 has.add(nullrev)
637 has.add(nullrev)
641 has.update(common)
638 has.update(common)
642
639
643 # take all ancestors from heads that aren't in has
640 # take all ancestors from heads that aren't in has
644 missing = set()
641 missing = set()
645 visit = collections.deque(r for r in heads if r not in has)
642 visit = collections.deque(r for r in heads if r not in has)
646 while visit:
643 while visit:
647 r = visit.popleft()
644 r = visit.popleft()
648 if r in missing:
645 if r in missing:
649 continue
646 continue
650 else:
647 else:
651 missing.add(r)
648 missing.add(r)
652 for p in self.parentrevs(r):
649 for p in self.parentrevs(r):
653 if p not in has:
650 if p not in has:
654 visit.append(p)
651 visit.append(p)
655 missing = list(missing)
652 missing = list(missing)
656 missing.sort()
653 missing.sort()
657 return has, [self.node(miss) for miss in missing]
654 return has, [self.node(miss) for miss in missing]
658
655
659 def incrementalmissingrevs(self, common=None):
656 def incrementalmissingrevs(self, common=None):
660 """Return an object that can be used to incrementally compute the
657 """Return an object that can be used to incrementally compute the
661 revision numbers of the ancestors of arbitrary sets that are not
658 revision numbers of the ancestors of arbitrary sets that are not
662 ancestors of common. This is an ancestor.incrementalmissingancestors
659 ancestors of common. This is an ancestor.incrementalmissingancestors
663 object.
660 object.
664
661
665 'common' is a list of revision numbers. If common is not supplied, uses
662 'common' is a list of revision numbers. If common is not supplied, uses
666 nullrev.
663 nullrev.
667 """
664 """
668 if common is None:
665 if common is None:
669 common = [nullrev]
666 common = [nullrev]
670
667
671 return ancestor.incrementalmissingancestors(self.parentrevs, common)
668 return ancestor.incrementalmissingancestors(self.parentrevs, common)
672
669
673 def findmissingrevs(self, common=None, heads=None):
670 def findmissingrevs(self, common=None, heads=None):
674 """Return the revision numbers of the ancestors of heads that
671 """Return the revision numbers of the ancestors of heads that
675 are not ancestors of common.
672 are not ancestors of common.
676
673
677 More specifically, return a list of revision numbers corresponding to
674 More specifically, return a list of revision numbers corresponding to
678 nodes N such that every N satisfies the following constraints:
675 nodes N such that every N satisfies the following constraints:
679
676
680 1. N is an ancestor of some node in 'heads'
677 1. N is an ancestor of some node in 'heads'
681 2. N is not an ancestor of any node in 'common'
678 2. N is not an ancestor of any node in 'common'
682
679
683 The list is sorted by revision number, meaning it is
680 The list is sorted by revision number, meaning it is
684 topologically sorted.
681 topologically sorted.
685
682
686 'heads' and 'common' are both lists of revision numbers. If heads is
683 'heads' and 'common' are both lists of revision numbers. If heads is
687 not supplied, uses all of the revlog's heads. If common is not
684 not supplied, uses all of the revlog's heads. If common is not
688 supplied, uses nullid."""
685 supplied, uses nullid."""
689 if common is None:
686 if common is None:
690 common = [nullrev]
687 common = [nullrev]
691 if heads is None:
688 if heads is None:
692 heads = self.headrevs()
689 heads = self.headrevs()
693
690
694 inc = self.incrementalmissingrevs(common=common)
691 inc = self.incrementalmissingrevs(common=common)
695 return inc.missingancestors(heads)
692 return inc.missingancestors(heads)
696
693
697 def findmissing(self, common=None, heads=None):
694 def findmissing(self, common=None, heads=None):
698 """Return the ancestors of heads that are not ancestors of common.
695 """Return the ancestors of heads that are not ancestors of common.
699
696
700 More specifically, return a list of nodes N such that every N
697 More specifically, return a list of nodes N such that every N
701 satisfies the following constraints:
698 satisfies the following constraints:
702
699
703 1. N is an ancestor of some node in 'heads'
700 1. N is an ancestor of some node in 'heads'
704 2. N is not an ancestor of any node in 'common'
701 2. N is not an ancestor of any node in 'common'
705
702
706 The list is sorted by revision number, meaning it is
703 The list is sorted by revision number, meaning it is
707 topologically sorted.
704 topologically sorted.
708
705
709 'heads' and 'common' are both lists of node IDs. If heads is
706 'heads' and 'common' are both lists of node IDs. If heads is
710 not supplied, uses all of the revlog's heads. If common is not
707 not supplied, uses all of the revlog's heads. If common is not
711 supplied, uses nullid."""
708 supplied, uses nullid."""
712 if common is None:
709 if common is None:
713 common = [nullid]
710 common = [nullid]
714 if heads is None:
711 if heads is None:
715 heads = self.heads()
712 heads = self.heads()
716
713
717 common = [self.rev(n) for n in common]
714 common = [self.rev(n) for n in common]
718 heads = [self.rev(n) for n in heads]
715 heads = [self.rev(n) for n in heads]
719
716
720 inc = self.incrementalmissingrevs(common=common)
717 inc = self.incrementalmissingrevs(common=common)
721 return [self.node(r) for r in inc.missingancestors(heads)]
718 return [self.node(r) for r in inc.missingancestors(heads)]
722
719
723 def nodesbetween(self, roots=None, heads=None):
720 def nodesbetween(self, roots=None, heads=None):
724 """Return a topological path from 'roots' to 'heads'.
721 """Return a topological path from 'roots' to 'heads'.
725
722
726 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
723 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
727 topologically sorted list of all nodes N that satisfy both of
724 topologically sorted list of all nodes N that satisfy both of
728 these constraints:
725 these constraints:
729
726
730 1. N is a descendant of some node in 'roots'
727 1. N is a descendant of some node in 'roots'
731 2. N is an ancestor of some node in 'heads'
728 2. N is an ancestor of some node in 'heads'
732
729
733 Every node is considered to be both a descendant and an ancestor
730 Every node is considered to be both a descendant and an ancestor
734 of itself, so every reachable node in 'roots' and 'heads' will be
731 of itself, so every reachable node in 'roots' and 'heads' will be
735 included in 'nodes'.
732 included in 'nodes'.
736
733
737 'outroots' is the list of reachable nodes in 'roots', i.e., the
734 'outroots' is the list of reachable nodes in 'roots', i.e., the
738 subset of 'roots' that is returned in 'nodes'. Likewise,
735 subset of 'roots' that is returned in 'nodes'. Likewise,
739 'outheads' is the subset of 'heads' that is also in 'nodes'.
736 'outheads' is the subset of 'heads' that is also in 'nodes'.
740
737
741 'roots' and 'heads' are both lists of node IDs. If 'roots' is
738 'roots' and 'heads' are both lists of node IDs. If 'roots' is
742 unspecified, uses nullid as the only root. If 'heads' is
739 unspecified, uses nullid as the only root. If 'heads' is
743 unspecified, uses list of all of the revlog's heads."""
740 unspecified, uses list of all of the revlog's heads."""
744 nonodes = ([], [], [])
741 nonodes = ([], [], [])
745 if roots is not None:
742 if roots is not None:
746 roots = list(roots)
743 roots = list(roots)
747 if not roots:
744 if not roots:
748 return nonodes
745 return nonodes
749 lowestrev = min([self.rev(n) for n in roots])
746 lowestrev = min([self.rev(n) for n in roots])
750 else:
747 else:
751 roots = [nullid] # Everybody's a descendant of nullid
748 roots = [nullid] # Everybody's a descendant of nullid
752 lowestrev = nullrev
749 lowestrev = nullrev
753 if (lowestrev == nullrev) and (heads is None):
750 if (lowestrev == nullrev) and (heads is None):
754 # We want _all_ the nodes!
751 # We want _all_ the nodes!
755 return ([self.node(r) for r in self], [nullid], list(self.heads()))
752 return ([self.node(r) for r in self], [nullid], list(self.heads()))
756 if heads is None:
753 if heads is None:
757 # All nodes are ancestors, so the latest ancestor is the last
754 # All nodes are ancestors, so the latest ancestor is the last
758 # node.
755 # node.
759 highestrev = len(self) - 1
756 highestrev = len(self) - 1
760 # Set ancestors to None to signal that every node is an ancestor.
757 # Set ancestors to None to signal that every node is an ancestor.
761 ancestors = None
758 ancestors = None
762 # Set heads to an empty dictionary for later discovery of heads
759 # Set heads to an empty dictionary for later discovery of heads
763 heads = {}
760 heads = {}
764 else:
761 else:
765 heads = list(heads)
762 heads = list(heads)
766 if not heads:
763 if not heads:
767 return nonodes
764 return nonodes
768 ancestors = set()
765 ancestors = set()
769 # Turn heads into a dictionary so we can remove 'fake' heads.
766 # Turn heads into a dictionary so we can remove 'fake' heads.
770 # Also, later we will be using it to filter out the heads we can't
767 # Also, later we will be using it to filter out the heads we can't
771 # find from roots.
768 # find from roots.
772 heads = dict.fromkeys(heads, False)
769 heads = dict.fromkeys(heads, False)
773 # Start at the top and keep marking parents until we're done.
770 # Start at the top and keep marking parents until we're done.
774 nodestotag = set(heads)
771 nodestotag = set(heads)
775 # Remember where the top was so we can use it as a limit later.
772 # Remember where the top was so we can use it as a limit later.
776 highestrev = max([self.rev(n) for n in nodestotag])
773 highestrev = max([self.rev(n) for n in nodestotag])
777 while nodestotag:
774 while nodestotag:
778 # grab a node to tag
775 # grab a node to tag
779 n = nodestotag.pop()
776 n = nodestotag.pop()
780 # Never tag nullid
777 # Never tag nullid
781 if n == nullid:
778 if n == nullid:
782 continue
779 continue
783 # A node's revision number represents its place in a
780 # A node's revision number represents its place in a
784 # topologically sorted list of nodes.
781 # topologically sorted list of nodes.
785 r = self.rev(n)
782 r = self.rev(n)
786 if r >= lowestrev:
783 if r >= lowestrev:
787 if n not in ancestors:
784 if n not in ancestors:
788 # If we are possibly a descendant of one of the roots
785 # If we are possibly a descendant of one of the roots
789 # and we haven't already been marked as an ancestor
786 # and we haven't already been marked as an ancestor
790 ancestors.add(n) # Mark as ancestor
787 ancestors.add(n) # Mark as ancestor
791 # Add non-nullid parents to list of nodes to tag.
788 # Add non-nullid parents to list of nodes to tag.
792 nodestotag.update([p for p in self.parents(n) if
789 nodestotag.update([p for p in self.parents(n) if
793 p != nullid])
790 p != nullid])
794 elif n in heads: # We've seen it before, is it a fake head?
791 elif n in heads: # We've seen it before, is it a fake head?
795 # So it is, real heads should not be the ancestors of
792 # So it is, real heads should not be the ancestors of
796 # any other heads.
793 # any other heads.
797 heads.pop(n)
794 heads.pop(n)
798 if not ancestors:
795 if not ancestors:
799 return nonodes
796 return nonodes
800 # Now that we have our set of ancestors, we want to remove any
797 # Now that we have our set of ancestors, we want to remove any
801 # roots that are not ancestors.
798 # roots that are not ancestors.
802
799
803 # If one of the roots was nullid, everything is included anyway.
800 # If one of the roots was nullid, everything is included anyway.
804 if lowestrev > nullrev:
801 if lowestrev > nullrev:
805 # But, since we weren't, let's recompute the lowest rev to not
802 # But, since we weren't, let's recompute the lowest rev to not
806 # include roots that aren't ancestors.
803 # include roots that aren't ancestors.
807
804
808 # Filter out roots that aren't ancestors of heads
805 # Filter out roots that aren't ancestors of heads
809 roots = [root for root in roots if root in ancestors]
806 roots = [root for root in roots if root in ancestors]
810 # Recompute the lowest revision
807 # Recompute the lowest revision
811 if roots:
808 if roots:
812 lowestrev = min([self.rev(root) for root in roots])
809 lowestrev = min([self.rev(root) for root in roots])
813 else:
810 else:
814 # No more roots? Return empty list
811 # No more roots? Return empty list
815 return nonodes
812 return nonodes
816 else:
813 else:
817 # We are descending from nullid, and don't need to care about
814 # We are descending from nullid, and don't need to care about
818 # any other roots.
815 # any other roots.
819 lowestrev = nullrev
816 lowestrev = nullrev
820 roots = [nullid]
817 roots = [nullid]
821 # Transform our roots list into a set.
818 # Transform our roots list into a set.
822 descendants = set(roots)
819 descendants = set(roots)
823 # Also, keep the original roots so we can filter out roots that aren't
820 # Also, keep the original roots so we can filter out roots that aren't
824 # 'real' roots (i.e. are descended from other roots).
821 # 'real' roots (i.e. are descended from other roots).
825 roots = descendants.copy()
822 roots = descendants.copy()
826 # Our topologically sorted list of output nodes.
823 # Our topologically sorted list of output nodes.
827 orderedout = []
824 orderedout = []
828 # Don't start at nullid since we don't want nullid in our output list,
825 # Don't start at nullid since we don't want nullid in our output list,
829 # and if nullid shows up in descendants, empty parents will look like
826 # and if nullid shows up in descendants, empty parents will look like
830 # they're descendants.
827 # they're descendants.
831 for r in self.revs(start=max(lowestrev, 0), stop=highestrev + 1):
828 for r in self.revs(start=max(lowestrev, 0), stop=highestrev + 1):
832 n = self.node(r)
829 n = self.node(r)
833 isdescendant = False
830 isdescendant = False
834 if lowestrev == nullrev: # Everybody is a descendant of nullid
831 if lowestrev == nullrev: # Everybody is a descendant of nullid
835 isdescendant = True
832 isdescendant = True
836 elif n in descendants:
833 elif n in descendants:
837 # n is already a descendant
834 # n is already a descendant
838 isdescendant = True
835 isdescendant = True
839 # This check only needs to be done here because all the roots
836 # This check only needs to be done here because all the roots
840 # will start being marked is descendants before the loop.
837 # will start being marked is descendants before the loop.
841 if n in roots:
838 if n in roots:
842 # If n was a root, check if it's a 'real' root.
839 # If n was a root, check if it's a 'real' root.
843 p = tuple(self.parents(n))
840 p = tuple(self.parents(n))
844 # If any of its parents are descendants, it's not a root.
841 # If any of its parents are descendants, it's not a root.
845 if (p[0] in descendants) or (p[1] in descendants):
842 if (p[0] in descendants) or (p[1] in descendants):
846 roots.remove(n)
843 roots.remove(n)
847 else:
844 else:
848 p = tuple(self.parents(n))
845 p = tuple(self.parents(n))
849 # A node is a descendant if either of its parents are
846 # A node is a descendant if either of its parents are
850 # descendants. (We seeded the dependents list with the roots
847 # descendants. (We seeded the dependents list with the roots
851 # up there, remember?)
848 # up there, remember?)
852 if (p[0] in descendants) or (p[1] in descendants):
849 if (p[0] in descendants) or (p[1] in descendants):
853 descendants.add(n)
850 descendants.add(n)
854 isdescendant = True
851 isdescendant = True
855 if isdescendant and ((ancestors is None) or (n in ancestors)):
852 if isdescendant and ((ancestors is None) or (n in ancestors)):
856 # Only include nodes that are both descendants and ancestors.
853 # Only include nodes that are both descendants and ancestors.
857 orderedout.append(n)
854 orderedout.append(n)
858 if (ancestors is not None) and (n in heads):
855 if (ancestors is not None) and (n in heads):
859 # We're trying to figure out which heads are reachable
856 # We're trying to figure out which heads are reachable
860 # from roots.
857 # from roots.
861 # Mark this head as having been reached
858 # Mark this head as having been reached
862 heads[n] = True
859 heads[n] = True
863 elif ancestors is None:
860 elif ancestors is None:
864 # Otherwise, we're trying to discover the heads.
861 # Otherwise, we're trying to discover the heads.
865 # Assume this is a head because if it isn't, the next step
862 # Assume this is a head because if it isn't, the next step
866 # will eventually remove it.
863 # will eventually remove it.
867 heads[n] = True
864 heads[n] = True
868 # But, obviously its parents aren't.
865 # But, obviously its parents aren't.
869 for p in self.parents(n):
866 for p in self.parents(n):
870 heads.pop(p, None)
867 heads.pop(p, None)
871 heads = [head for head, flag in heads.iteritems() if flag]
868 heads = [head for head, flag in heads.iteritems() if flag]
872 roots = list(roots)
869 roots = list(roots)
873 assert orderedout
870 assert orderedout
874 assert roots
871 assert roots
875 assert heads
872 assert heads
876 return (orderedout, roots, heads)
873 return (orderedout, roots, heads)
877
874
878 def headrevs(self):
875 def headrevs(self):
879 try:
876 try:
880 return self.index.headrevs()
877 return self.index.headrevs()
881 except AttributeError:
878 except AttributeError:
882 return self._headrevs()
879 return self._headrevs()
883
880
884 def computephases(self, roots):
881 def computephases(self, roots):
885 return self.index.computephasesmapsets(roots)
882 return self.index.computephasesmapsets(roots)
886
883
887 def _headrevs(self):
884 def _headrevs(self):
888 count = len(self)
885 count = len(self)
889 if not count:
886 if not count:
890 return [nullrev]
887 return [nullrev]
891 # we won't iter over filtered rev so nobody is a head at start
888 # we won't iter over filtered rev so nobody is a head at start
892 ishead = [0] * (count + 1)
889 ishead = [0] * (count + 1)
893 index = self.index
890 index = self.index
894 for r in self:
891 for r in self:
895 ishead[r] = 1 # I may be an head
892 ishead[r] = 1 # I may be an head
896 e = index[r]
893 e = index[r]
897 ishead[e[5]] = ishead[e[6]] = 0 # my parent are not
894 ishead[e[5]] = ishead[e[6]] = 0 # my parent are not
898 return [r for r, val in enumerate(ishead) if val]
895 return [r for r, val in enumerate(ishead) if val]
899
896
900 def heads(self, start=None, stop=None):
897 def heads(self, start=None, stop=None):
901 """return the list of all nodes that have no children
898 """return the list of all nodes that have no children
902
899
903 if start is specified, only heads that are descendants of
900 if start is specified, only heads that are descendants of
904 start will be returned
901 start will be returned
905 if stop is specified, it will consider all the revs from stop
902 if stop is specified, it will consider all the revs from stop
906 as if they had no children
903 as if they had no children
907 """
904 """
908 if start is None and stop is None:
905 if start is None and stop is None:
909 if not len(self):
906 if not len(self):
910 return [nullid]
907 return [nullid]
911 return [self.node(r) for r in self.headrevs()]
908 return [self.node(r) for r in self.headrevs()]
912
909
913 if start is None:
910 if start is None:
914 start = nullid
911 start = nullid
915 if stop is None:
912 if stop is None:
916 stop = []
913 stop = []
917 stoprevs = set([self.rev(n) for n in stop])
914 stoprevs = set([self.rev(n) for n in stop])
918 startrev = self.rev(start)
915 startrev = self.rev(start)
919 reachable = set((startrev,))
916 reachable = set((startrev,))
920 heads = set((startrev,))
917 heads = set((startrev,))
921
918
922 parentrevs = self.parentrevs
919 parentrevs = self.parentrevs
923 for r in self.revs(start=startrev + 1):
920 for r in self.revs(start=startrev + 1):
924 for p in parentrevs(r):
921 for p in parentrevs(r):
925 if p in reachable:
922 if p in reachable:
926 if r not in stoprevs:
923 if r not in stoprevs:
927 reachable.add(r)
924 reachable.add(r)
928 heads.add(r)
925 heads.add(r)
929 if p in heads and p not in stoprevs:
926 if p in heads and p not in stoprevs:
930 heads.remove(p)
927 heads.remove(p)
931
928
932 return [self.node(r) for r in heads]
929 return [self.node(r) for r in heads]
933
930
934 def children(self, node):
931 def children(self, node):
935 """find the children of a given node"""
932 """find the children of a given node"""
936 c = []
933 c = []
937 p = self.rev(node)
934 p = self.rev(node)
938 for r in self.revs(start=p + 1):
935 for r in self.revs(start=p + 1):
939 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
936 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
940 if prevs:
937 if prevs:
941 for pr in prevs:
938 for pr in prevs:
942 if pr == p:
939 if pr == p:
943 c.append(self.node(r))
940 c.append(self.node(r))
944 elif p == nullrev:
941 elif p == nullrev:
945 c.append(self.node(r))
942 c.append(self.node(r))
946 return c
943 return c
947
944
948 def descendant(self, start, end):
945 def descendant(self, start, end):
949 if start == nullrev:
946 if start == nullrev:
950 return True
947 return True
951 for i in self.descendants([start]):
948 for i in self.descendants([start]):
952 if i == end:
949 if i == end:
953 return True
950 return True
954 elif i > end:
951 elif i > end:
955 break
952 break
956 return False
953 return False
957
954
958 def commonancestorsheads(self, a, b):
955 def commonancestorsheads(self, a, b):
959 """calculate all the heads of the common ancestors of nodes a and b"""
956 """calculate all the heads of the common ancestors of nodes a and b"""
960 a, b = self.rev(a), self.rev(b)
957 a, b = self.rev(a), self.rev(b)
961 try:
958 try:
962 ancs = self.index.commonancestorsheads(a, b)
959 ancs = self.index.commonancestorsheads(a, b)
963 except (AttributeError, OverflowError): # C implementation failed
960 except (AttributeError, OverflowError): # C implementation failed
964 ancs = ancestor.commonancestorsheads(self.parentrevs, a, b)
961 ancs = ancestor.commonancestorsheads(self.parentrevs, a, b)
965 return pycompat.maplist(self.node, ancs)
962 return pycompat.maplist(self.node, ancs)
966
963
967 def isancestor(self, a, b):
964 def isancestor(self, a, b):
968 """return True if node a is an ancestor of node b
965 """return True if node a is an ancestor of node b
969
966
970 The implementation of this is trivial but the use of
967 The implementation of this is trivial but the use of
971 commonancestorsheads is not."""
968 commonancestorsheads is not."""
972 return a in self.commonancestorsheads(a, b)
969 return a in self.commonancestorsheads(a, b)
973
970
974 def ancestor(self, a, b):
971 def ancestor(self, a, b):
975 """calculate the "best" common ancestor of nodes a and b"""
972 """calculate the "best" common ancestor of nodes a and b"""
976
973
977 a, b = self.rev(a), self.rev(b)
974 a, b = self.rev(a), self.rev(b)
978 try:
975 try:
979 ancs = self.index.ancestors(a, b)
976 ancs = self.index.ancestors(a, b)
980 except (AttributeError, OverflowError):
977 except (AttributeError, OverflowError):
981 ancs = ancestor.ancestors(self.parentrevs, a, b)
978 ancs = ancestor.ancestors(self.parentrevs, a, b)
982 if ancs:
979 if ancs:
983 # choose a consistent winner when there's a tie
980 # choose a consistent winner when there's a tie
984 return min(map(self.node, ancs))
981 return min(map(self.node, ancs))
985 return nullid
982 return nullid
986
983
987 def _match(self, id):
984 def _match(self, id):
988 if isinstance(id, int):
985 if isinstance(id, int):
989 # rev
986 # rev
990 return self.node(id)
987 return self.node(id)
991 if len(id) == 20:
988 if len(id) == 20:
992 # possibly a binary node
989 # possibly a binary node
993 # odds of a binary node being all hex in ASCII are 1 in 10**25
990 # odds of a binary node being all hex in ASCII are 1 in 10**25
994 try:
991 try:
995 node = id
992 node = id
996 self.rev(node) # quick search the index
993 self.rev(node) # quick search the index
997 return node
994 return node
998 except LookupError:
995 except LookupError:
999 pass # may be partial hex id
996 pass # may be partial hex id
1000 try:
997 try:
1001 # str(rev)
998 # str(rev)
1002 rev = int(id)
999 rev = int(id)
1003 if str(rev) != id:
1000 if str(rev) != id:
1004 raise ValueError
1001 raise ValueError
1005 if rev < 0:
1002 if rev < 0:
1006 rev = len(self) + rev
1003 rev = len(self) + rev
1007 if rev < 0 or rev >= len(self):
1004 if rev < 0 or rev >= len(self):
1008 raise ValueError
1005 raise ValueError
1009 return self.node(rev)
1006 return self.node(rev)
1010 except (ValueError, OverflowError):
1007 except (ValueError, OverflowError):
1011 pass
1008 pass
1012 if len(id) == 40:
1009 if len(id) == 40:
1013 try:
1010 try:
1014 # a full hex nodeid?
1011 # a full hex nodeid?
1015 node = bin(id)
1012 node = bin(id)
1016 self.rev(node)
1013 self.rev(node)
1017 return node
1014 return node
1018 except (TypeError, LookupError):
1015 except (TypeError, LookupError):
1019 pass
1016 pass
1020
1017
1021 def _partialmatch(self, id):
1018 def _partialmatch(self, id):
1022 try:
1019 try:
1023 partial = self.index.partialmatch(id)
1020 partial = self.index.partialmatch(id)
1024 if partial and self.hasnode(partial):
1021 if partial and self.hasnode(partial):
1025 return partial
1022 return partial
1026 return None
1023 return None
1027 except RevlogError:
1024 except RevlogError:
1028 # parsers.c radix tree lookup gave multiple matches
1025 # parsers.c radix tree lookup gave multiple matches
1029 # fast path: for unfiltered changelog, radix tree is accurate
1026 # fast path: for unfiltered changelog, radix tree is accurate
1030 if not getattr(self, 'filteredrevs', None):
1027 if not getattr(self, 'filteredrevs', None):
1031 raise LookupError(id, self.indexfile,
1028 raise LookupError(id, self.indexfile,
1032 _('ambiguous identifier'))
1029 _('ambiguous identifier'))
1033 # fall through to slow path that filters hidden revisions
1030 # fall through to slow path that filters hidden revisions
1034 except (AttributeError, ValueError):
1031 except (AttributeError, ValueError):
1035 # we are pure python, or key was too short to search radix tree
1032 # we are pure python, or key was too short to search radix tree
1036 pass
1033 pass
1037
1034
1038 if id in self._pcache:
1035 if id in self._pcache:
1039 return self._pcache[id]
1036 return self._pcache[id]
1040
1037
1041 if len(id) < 40:
1038 if len(id) < 40:
1042 try:
1039 try:
1043 # hex(node)[:...]
1040 # hex(node)[:...]
1044 l = len(id) // 2 # grab an even number of digits
1041 l = len(id) // 2 # grab an even number of digits
1045 prefix = bin(id[:l * 2])
1042 prefix = bin(id[:l * 2])
1046 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
1043 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
1047 nl = [n for n in nl if hex(n).startswith(id) and
1044 nl = [n for n in nl if hex(n).startswith(id) and
1048 self.hasnode(n)]
1045 self.hasnode(n)]
1049 if len(nl) > 0:
1046 if len(nl) > 0:
1050 if len(nl) == 1:
1047 if len(nl) == 1:
1051 self._pcache[id] = nl[0]
1048 self._pcache[id] = nl[0]
1052 return nl[0]
1049 return nl[0]
1053 raise LookupError(id, self.indexfile,
1050 raise LookupError(id, self.indexfile,
1054 _('ambiguous identifier'))
1051 _('ambiguous identifier'))
1055 return None
1052 return None
1056 except TypeError:
1053 except TypeError:
1057 pass
1054 pass
1058
1055
1059 def lookup(self, id):
1056 def lookup(self, id):
1060 """locate a node based on:
1057 """locate a node based on:
1061 - revision number or str(revision number)
1058 - revision number or str(revision number)
1062 - nodeid or subset of hex nodeid
1059 - nodeid or subset of hex nodeid
1063 """
1060 """
1064 n = self._match(id)
1061 n = self._match(id)
1065 if n is not None:
1062 if n is not None:
1066 return n
1063 return n
1067 n = self._partialmatch(id)
1064 n = self._partialmatch(id)
1068 if n:
1065 if n:
1069 return n
1066 return n
1070
1067
1071 raise LookupError(id, self.indexfile, _('no match found'))
1068 raise LookupError(id, self.indexfile, _('no match found'))
1072
1069
1073 def cmp(self, node, text):
1070 def cmp(self, node, text):
1074 """compare text with a given file revision
1071 """compare text with a given file revision
1075
1072
1076 returns True if text is different than what is stored.
1073 returns True if text is different than what is stored.
1077 """
1074 """
1078 p1, p2 = self.parents(node)
1075 p1, p2 = self.parents(node)
1079 return hash(text, p1, p2) != node
1076 return hash(text, p1, p2) != node
1080
1077
1081 def _addchunk(self, offset, data):
1078 def _addchunk(self, offset, data):
1082 """Add a segment to the revlog cache.
1079 """Add a segment to the revlog cache.
1083
1080
1084 Accepts an absolute offset and the data that is at that location.
1081 Accepts an absolute offset and the data that is at that location.
1085 """
1082 """
1086 o, d = self._chunkcache
1083 o, d = self._chunkcache
1087 # try to add to existing cache
1084 # try to add to existing cache
1088 if o + len(d) == offset and len(d) + len(data) < _chunksize:
1085 if o + len(d) == offset and len(d) + len(data) < _chunksize:
1089 self._chunkcache = o, d + data
1086 self._chunkcache = o, d + data
1090 else:
1087 else:
1091 self._chunkcache = offset, data
1088 self._chunkcache = offset, data
1092
1089
1093 def _loadchunk(self, offset, length, df=None):
1090 def _loadchunk(self, offset, length, df=None):
1094 """Load a segment of raw data from the revlog.
1091 """Load a segment of raw data from the revlog.
1095
1092
1096 Accepts an absolute offset, length to read, and an optional existing
1093 Accepts an absolute offset, length to read, and an optional existing
1097 file handle to read from.
1094 file handle to read from.
1098
1095
1099 If an existing file handle is passed, it will be seeked and the
1096 If an existing file handle is passed, it will be seeked and the
1100 original seek position will NOT be restored.
1097 original seek position will NOT be restored.
1101
1098
1102 Returns a str or buffer of raw byte data.
1099 Returns a str or buffer of raw byte data.
1103 """
1100 """
1104 if df is not None:
1101 if df is not None:
1105 closehandle = False
1102 closehandle = False
1106 else:
1103 else:
1107 if self._inline:
1104 if self._inline:
1108 df = self.opener(self.indexfile)
1105 df = self.opener(self.indexfile)
1109 else:
1106 else:
1110 df = self.opener(self.datafile)
1107 df = self.opener(self.datafile)
1111 closehandle = True
1108 closehandle = True
1112
1109
1113 # Cache data both forward and backward around the requested
1110 # Cache data both forward and backward around the requested
1114 # data, in a fixed size window. This helps speed up operations
1111 # data, in a fixed size window. This helps speed up operations
1115 # involving reading the revlog backwards.
1112 # involving reading the revlog backwards.
1116 cachesize = self._chunkcachesize
1113 cachesize = self._chunkcachesize
1117 realoffset = offset & ~(cachesize - 1)
1114 realoffset = offset & ~(cachesize - 1)
1118 reallength = (((offset + length + cachesize) & ~(cachesize - 1))
1115 reallength = (((offset + length + cachesize) & ~(cachesize - 1))
1119 - realoffset)
1116 - realoffset)
1120 df.seek(realoffset)
1117 df.seek(realoffset)
1121 d = df.read(reallength)
1118 d = df.read(reallength)
1122 if closehandle:
1119 if closehandle:
1123 df.close()
1120 df.close()
1124 self._addchunk(realoffset, d)
1121 self._addchunk(realoffset, d)
1125 if offset != realoffset or reallength != length:
1122 if offset != realoffset or reallength != length:
1126 return util.buffer(d, offset - realoffset, length)
1123 return util.buffer(d, offset - realoffset, length)
1127 return d
1124 return d
1128
1125
1129 def _getchunk(self, offset, length, df=None):
1126 def _getchunk(self, offset, length, df=None):
1130 """Obtain a segment of raw data from the revlog.
1127 """Obtain a segment of raw data from the revlog.
1131
1128
1132 Accepts an absolute offset, length of bytes to obtain, and an
1129 Accepts an absolute offset, length of bytes to obtain, and an
1133 optional file handle to the already-opened revlog. If the file
1130 optional file handle to the already-opened revlog. If the file
1134 handle is used, it's original seek position will not be preserved.
1131 handle is used, it's original seek position will not be preserved.
1135
1132
1136 Requests for data may be returned from a cache.
1133 Requests for data may be returned from a cache.
1137
1134
1138 Returns a str or a buffer instance of raw byte data.
1135 Returns a str or a buffer instance of raw byte data.
1139 """
1136 """
1140 o, d = self._chunkcache
1137 o, d = self._chunkcache
1141 l = len(d)
1138 l = len(d)
1142
1139
1143 # is it in the cache?
1140 # is it in the cache?
1144 cachestart = offset - o
1141 cachestart = offset - o
1145 cacheend = cachestart + length
1142 cacheend = cachestart + length
1146 if cachestart >= 0 and cacheend <= l:
1143 if cachestart >= 0 and cacheend <= l:
1147 if cachestart == 0 and cacheend == l:
1144 if cachestart == 0 and cacheend == l:
1148 return d # avoid a copy
1145 return d # avoid a copy
1149 return util.buffer(d, cachestart, cacheend - cachestart)
1146 return util.buffer(d, cachestart, cacheend - cachestart)
1150
1147
1151 return self._loadchunk(offset, length, df=df)
1148 return self._loadchunk(offset, length, df=df)
1152
1149
1153 def _chunkraw(self, startrev, endrev, df=None):
1150 def _chunkraw(self, startrev, endrev, df=None):
1154 """Obtain a segment of raw data corresponding to a range of revisions.
1151 """Obtain a segment of raw data corresponding to a range of revisions.
1155
1152
1156 Accepts the start and end revisions and an optional already-open
1153 Accepts the start and end revisions and an optional already-open
1157 file handle to be used for reading. If the file handle is read, its
1154 file handle to be used for reading. If the file handle is read, its
1158 seek position will not be preserved.
1155 seek position will not be preserved.
1159
1156
1160 Requests for data may be satisfied by a cache.
1157 Requests for data may be satisfied by a cache.
1161
1158
1162 Returns a 2-tuple of (offset, data) for the requested range of
1159 Returns a 2-tuple of (offset, data) for the requested range of
1163 revisions. Offset is the integer offset from the beginning of the
1160 revisions. Offset is the integer offset from the beginning of the
1164 revlog and data is a str or buffer of the raw byte data.
1161 revlog and data is a str or buffer of the raw byte data.
1165
1162
1166 Callers will need to call ``self.start(rev)`` and ``self.length(rev)``
1163 Callers will need to call ``self.start(rev)`` and ``self.length(rev)``
1167 to determine where each revision's data begins and ends.
1164 to determine where each revision's data begins and ends.
1168 """
1165 """
1169 # Inlined self.start(startrev) & self.end(endrev) for perf reasons
1166 # Inlined self.start(startrev) & self.end(endrev) for perf reasons
1170 # (functions are expensive).
1167 # (functions are expensive).
1171 index = self.index
1168 index = self.index
1172 istart = index[startrev]
1169 istart = index[startrev]
1173 start = int(istart[0] >> 16)
1170 start = int(istart[0] >> 16)
1174 if startrev == endrev:
1171 if startrev == endrev:
1175 end = start + istart[1]
1172 end = start + istart[1]
1176 else:
1173 else:
1177 iend = index[endrev]
1174 iend = index[endrev]
1178 end = int(iend[0] >> 16) + iend[1]
1175 end = int(iend[0] >> 16) + iend[1]
1179
1176
1180 if self._inline:
1177 if self._inline:
1181 start += (startrev + 1) * self._io.size
1178 start += (startrev + 1) * self._io.size
1182 end += (endrev + 1) * self._io.size
1179 end += (endrev + 1) * self._io.size
1183 length = end - start
1180 length = end - start
1184
1181
1185 return start, self._getchunk(start, length, df=df)
1182 return start, self._getchunk(start, length, df=df)
1186
1183
1187 def _chunk(self, rev, df=None):
1184 def _chunk(self, rev, df=None):
1188 """Obtain a single decompressed chunk for a revision.
1185 """Obtain a single decompressed chunk for a revision.
1189
1186
1190 Accepts an integer revision and an optional already-open file handle
1187 Accepts an integer revision and an optional already-open file handle
1191 to be used for reading. If used, the seek position of the file will not
1188 to be used for reading. If used, the seek position of the file will not
1192 be preserved.
1189 be preserved.
1193
1190
1194 Returns a str holding uncompressed data for the requested revision.
1191 Returns a str holding uncompressed data for the requested revision.
1195 """
1192 """
1196 return self.decompress(self._chunkraw(rev, rev, df=df)[1])
1193 return self.decompress(self._chunkraw(rev, rev, df=df)[1])
1197
1194
1198 def _chunks(self, revs, df=None):
1195 def _chunks(self, revs, df=None):
1199 """Obtain decompressed chunks for the specified revisions.
1196 """Obtain decompressed chunks for the specified revisions.
1200
1197
1201 Accepts an iterable of numeric revisions that are assumed to be in
1198 Accepts an iterable of numeric revisions that are assumed to be in
1202 ascending order. Also accepts an optional already-open file handle
1199 ascending order. Also accepts an optional already-open file handle
1203 to be used for reading. If used, the seek position of the file will
1200 to be used for reading. If used, the seek position of the file will
1204 not be preserved.
1201 not be preserved.
1205
1202
1206 This function is similar to calling ``self._chunk()`` multiple times,
1203 This function is similar to calling ``self._chunk()`` multiple times,
1207 but is faster.
1204 but is faster.
1208
1205
1209 Returns a list with decompressed data for each requested revision.
1206 Returns a list with decompressed data for each requested revision.
1210 """
1207 """
1211 if not revs:
1208 if not revs:
1212 return []
1209 return []
1213 start = self.start
1210 start = self.start
1214 length = self.length
1211 length = self.length
1215 inline = self._inline
1212 inline = self._inline
1216 iosize = self._io.size
1213 iosize = self._io.size
1217 buffer = util.buffer
1214 buffer = util.buffer
1218
1215
1219 l = []
1216 l = []
1220 ladd = l.append
1217 ladd = l.append
1221
1218
1222 try:
1219 try:
1223 offset, data = self._chunkraw(revs[0], revs[-1], df=df)
1220 offset, data = self._chunkraw(revs[0], revs[-1], df=df)
1224 except OverflowError:
1221 except OverflowError:
1225 # issue4215 - we can't cache a run of chunks greater than
1222 # issue4215 - we can't cache a run of chunks greater than
1226 # 2G on Windows
1223 # 2G on Windows
1227 return [self._chunk(rev, df=df) for rev in revs]
1224 return [self._chunk(rev, df=df) for rev in revs]
1228
1225
1229 decomp = self.decompress
1226 decomp = self.decompress
1230 for rev in revs:
1227 for rev in revs:
1231 chunkstart = start(rev)
1228 chunkstart = start(rev)
1232 if inline:
1229 if inline:
1233 chunkstart += (rev + 1) * iosize
1230 chunkstart += (rev + 1) * iosize
1234 chunklength = length(rev)
1231 chunklength = length(rev)
1235 ladd(decomp(buffer(data, chunkstart - offset, chunklength)))
1232 ladd(decomp(buffer(data, chunkstart - offset, chunklength)))
1236
1233
1237 return l
1234 return l
1238
1235
1239 def _chunkclear(self):
1236 def _chunkclear(self):
1240 """Clear the raw chunk cache."""
1237 """Clear the raw chunk cache."""
1241 self._chunkcache = (0, '')
1238 self._chunkcache = (0, '')
1242
1239
1243 def deltaparent(self, rev):
1240 def deltaparent(self, rev):
1244 """return deltaparent of the given revision"""
1241 """return deltaparent of the given revision"""
1245 base = self.index[rev][3]
1242 base = self.index[rev][3]
1246 if base == rev:
1243 if base == rev:
1247 return nullrev
1244 return nullrev
1248 elif self._generaldelta:
1245 elif self._generaldelta:
1249 return base
1246 return base
1250 else:
1247 else:
1251 return rev - 1
1248 return rev - 1
1252
1249
1253 def revdiff(self, rev1, rev2):
1250 def revdiff(self, rev1, rev2):
1254 """return or calculate a delta between two revisions
1251 """return or calculate a delta between two revisions
1255
1252
1256 The delta calculated is in binary form and is intended to be written to
1253 The delta calculated is in binary form and is intended to be written to
1257 revlog data directly. So this function needs raw revision data.
1254 revlog data directly. So this function needs raw revision data.
1258 """
1255 """
1259 if rev1 != nullrev and self.deltaparent(rev2) == rev1:
1256 if rev1 != nullrev and self.deltaparent(rev2) == rev1:
1260 return bytes(self._chunk(rev2))
1257 return bytes(self._chunk(rev2))
1261
1258
1262 return mdiff.textdiff(self.revision(rev1, raw=True),
1259 return mdiff.textdiff(self.revision(rev1, raw=True),
1263 self.revision(rev2, raw=True))
1260 self.revision(rev2, raw=True))
1264
1261
1265 def revision(self, nodeorrev, _df=None, raw=False):
1262 def revision(self, nodeorrev, _df=None, raw=False):
1266 """return an uncompressed revision of a given node or revision
1263 """return an uncompressed revision of a given node or revision
1267 number.
1264 number.
1268
1265
1269 _df - an existing file handle to read from. (internal-only)
1266 _df - an existing file handle to read from. (internal-only)
1270 raw - an optional argument specifying if the revision data is to be
1267 raw - an optional argument specifying if the revision data is to be
1271 treated as raw data when applying flag transforms. 'raw' should be set
1268 treated as raw data when applying flag transforms. 'raw' should be set
1272 to True when generating changegroups or in debug commands.
1269 to True when generating changegroups or in debug commands.
1273 """
1270 """
1274 if isinstance(nodeorrev, int):
1271 if isinstance(nodeorrev, int):
1275 rev = nodeorrev
1272 rev = nodeorrev
1276 node = self.node(rev)
1273 node = self.node(rev)
1277 else:
1274 else:
1278 node = nodeorrev
1275 node = nodeorrev
1279 rev = None
1276 rev = None
1280
1277
1281 cachedrev = None
1278 cachedrev = None
1282 flags = None
1279 flags = None
1283 rawtext = None
1280 rawtext = None
1284 if node == nullid:
1281 if node == nullid:
1285 return ""
1282 return ""
1286 if self._cache:
1283 if self._cache:
1287 if self._cache[0] == node:
1284 if self._cache[0] == node:
1288 # _cache only stores rawtext
1285 # _cache only stores rawtext
1289 if raw:
1286 if raw:
1290 return self._cache[2]
1287 return self._cache[2]
1291 # duplicated, but good for perf
1288 # duplicated, but good for perf
1292 if rev is None:
1289 if rev is None:
1293 rev = self.rev(node)
1290 rev = self.rev(node)
1294 if flags is None:
1291 if flags is None:
1295 flags = self.flags(rev)
1292 flags = self.flags(rev)
1296 # no extra flags set, no flag processor runs, text = rawtext
1293 # no extra flags set, no flag processor runs, text = rawtext
1297 if flags == REVIDX_DEFAULT_FLAGS:
1294 if flags == REVIDX_DEFAULT_FLAGS:
1298 return self._cache[2]
1295 return self._cache[2]
1299 # rawtext is reusable. need to run flag processor
1296 # rawtext is reusable. need to run flag processor
1300 rawtext = self._cache[2]
1297 rawtext = self._cache[2]
1301
1298
1302 cachedrev = self._cache[1]
1299 cachedrev = self._cache[1]
1303
1300
1304 # look up what we need to read
1301 # look up what we need to read
1305 if rawtext is None:
1302 if rawtext is None:
1306 if rev is None:
1303 if rev is None:
1307 rev = self.rev(node)
1304 rev = self.rev(node)
1308
1305
1309 chain, stopped = self._deltachain(rev, stoprev=cachedrev)
1306 chain, stopped = self._deltachain(rev, stoprev=cachedrev)
1310 if stopped:
1307 if stopped:
1311 rawtext = self._cache[2]
1308 rawtext = self._cache[2]
1312
1309
1313 # drop cache to save memory
1310 # drop cache to save memory
1314 self._cache = None
1311 self._cache = None
1315
1312
1316 bins = self._chunks(chain, df=_df)
1313 bins = self._chunks(chain, df=_df)
1317 if rawtext is None:
1314 if rawtext is None:
1318 rawtext = bytes(bins[0])
1315 rawtext = bytes(bins[0])
1319 bins = bins[1:]
1316 bins = bins[1:]
1320
1317
1321 rawtext = mdiff.patches(rawtext, bins)
1318 rawtext = mdiff.patches(rawtext, bins)
1322 self._cache = (node, rev, rawtext)
1319 self._cache = (node, rev, rawtext)
1323
1320
1324 if flags is None:
1321 if flags is None:
1325 if rev is None:
1322 if rev is None:
1326 rev = self.rev(node)
1323 rev = self.rev(node)
1327 flags = self.flags(rev)
1324 flags = self.flags(rev)
1328
1325
1329 text, validatehash = self._processflags(rawtext, flags, 'read', raw=raw)
1326 text, validatehash = self._processflags(rawtext, flags, 'read', raw=raw)
1330 if validatehash:
1327 if validatehash:
1331 self.checkhash(text, node, rev=rev)
1328 self.checkhash(text, node, rev=rev)
1332
1329
1333 return text
1330 return text
1334
1331
1335 def hash(self, text, p1, p2):
1332 def hash(self, text, p1, p2):
1336 """Compute a node hash.
1333 """Compute a node hash.
1337
1334
1338 Available as a function so that subclasses can replace the hash
1335 Available as a function so that subclasses can replace the hash
1339 as needed.
1336 as needed.
1340 """
1337 """
1341 return hash(text, p1, p2)
1338 return hash(text, p1, p2)
1342
1339
1343 def _processflags(self, text, flags, operation, raw=False):
1340 def _processflags(self, text, flags, operation, raw=False):
1344 """Inspect revision data flags and applies transforms defined by
1341 """Inspect revision data flags and applies transforms defined by
1345 registered flag processors.
1342 registered flag processors.
1346
1343
1347 ``text`` - the revision data to process
1344 ``text`` - the revision data to process
1348 ``flags`` - the revision flags
1345 ``flags`` - the revision flags
1349 ``operation`` - the operation being performed (read or write)
1346 ``operation`` - the operation being performed (read or write)
1350 ``raw`` - an optional argument describing if the raw transform should be
1347 ``raw`` - an optional argument describing if the raw transform should be
1351 applied.
1348 applied.
1352
1349
1353 This method processes the flags in the order (or reverse order if
1350 This method processes the flags in the order (or reverse order if
1354 ``operation`` is 'write') defined by REVIDX_FLAGS_ORDER, applying the
1351 ``operation`` is 'write') defined by REVIDX_FLAGS_ORDER, applying the
1355 flag processors registered for present flags. The order of flags defined
1352 flag processors registered for present flags. The order of flags defined
1356 in REVIDX_FLAGS_ORDER needs to be stable to allow non-commutativity.
1353 in REVIDX_FLAGS_ORDER needs to be stable to allow non-commutativity.
1357
1354
1358 Returns a 2-tuple of ``(text, validatehash)`` where ``text`` is the
1355 Returns a 2-tuple of ``(text, validatehash)`` where ``text`` is the
1359 processed text and ``validatehash`` is a bool indicating whether the
1356 processed text and ``validatehash`` is a bool indicating whether the
1360 returned text should be checked for hash integrity.
1357 returned text should be checked for hash integrity.
1361
1358
1362 Note: If the ``raw`` argument is set, it has precedence over the
1359 Note: If the ``raw`` argument is set, it has precedence over the
1363 operation and will only update the value of ``validatehash``.
1360 operation and will only update the value of ``validatehash``.
1364 """
1361 """
1365 if not operation in ('read', 'write'):
1362 if not operation in ('read', 'write'):
1366 raise ProgrammingError(_("invalid '%s' operation ") % (operation))
1363 raise ProgrammingError(_("invalid '%s' operation ") % (operation))
1367 # Check all flags are known.
1364 # Check all flags are known.
1368 if flags & ~REVIDX_KNOWN_FLAGS:
1365 if flags & ~REVIDX_KNOWN_FLAGS:
1369 raise RevlogError(_("incompatible revision flag '%#x'") %
1366 raise RevlogError(_("incompatible revision flag '%#x'") %
1370 (flags & ~REVIDX_KNOWN_FLAGS))
1367 (flags & ~REVIDX_KNOWN_FLAGS))
1371 validatehash = True
1368 validatehash = True
1372 # Depending on the operation (read or write), the order might be
1369 # Depending on the operation (read or write), the order might be
1373 # reversed due to non-commutative transforms.
1370 # reversed due to non-commutative transforms.
1374 orderedflags = REVIDX_FLAGS_ORDER
1371 orderedflags = REVIDX_FLAGS_ORDER
1375 if operation == 'write':
1372 if operation == 'write':
1376 orderedflags = reversed(orderedflags)
1373 orderedflags = reversed(orderedflags)
1377
1374
1378 for flag in orderedflags:
1375 for flag in orderedflags:
1379 # If a flagprocessor has been registered for a known flag, apply the
1376 # If a flagprocessor has been registered for a known flag, apply the
1380 # related operation transform and update result tuple.
1377 # related operation transform and update result tuple.
1381 if flag & flags:
1378 if flag & flags:
1382 vhash = True
1379 vhash = True
1383
1380
1384 if flag not in _flagprocessors:
1381 if flag not in _flagprocessors:
1385 message = _("missing processor for flag '%#x'") % (flag)
1382 message = _("missing processor for flag '%#x'") % (flag)
1386 raise RevlogError(message)
1383 raise RevlogError(message)
1387
1384
1388 processor = _flagprocessors[flag]
1385 processor = _flagprocessors[flag]
1389 if processor is not None:
1386 if processor is not None:
1390 readtransform, writetransform, rawtransform = processor
1387 readtransform, writetransform, rawtransform = processor
1391
1388
1392 if raw:
1389 if raw:
1393 vhash = rawtransform(self, text)
1390 vhash = rawtransform(self, text)
1394 elif operation == 'read':
1391 elif operation == 'read':
1395 text, vhash = readtransform(self, text)
1392 text, vhash = readtransform(self, text)
1396 else: # write operation
1393 else: # write operation
1397 text, vhash = writetransform(self, text)
1394 text, vhash = writetransform(self, text)
1398 validatehash = validatehash and vhash
1395 validatehash = validatehash and vhash
1399
1396
1400 return text, validatehash
1397 return text, validatehash
1401
1398
1402 def checkhash(self, text, node, p1=None, p2=None, rev=None):
1399 def checkhash(self, text, node, p1=None, p2=None, rev=None):
1403 """Check node hash integrity.
1400 """Check node hash integrity.
1404
1401
1405 Available as a function so that subclasses can extend hash mismatch
1402 Available as a function so that subclasses can extend hash mismatch
1406 behaviors as needed.
1403 behaviors as needed.
1407 """
1404 """
1408 if p1 is None and p2 is None:
1405 if p1 is None and p2 is None:
1409 p1, p2 = self.parents(node)
1406 p1, p2 = self.parents(node)
1410 if node != self.hash(text, p1, p2):
1407 if node != self.hash(text, p1, p2):
1411 revornode = rev
1408 revornode = rev
1412 if revornode is None:
1409 if revornode is None:
1413 revornode = templatefilters.short(hex(node))
1410 revornode = templatefilters.short(hex(node))
1414 raise RevlogError(_("integrity check failed on %s:%s")
1411 raise RevlogError(_("integrity check failed on %s:%s")
1415 % (self.indexfile, revornode))
1412 % (self.indexfile, revornode))
1416
1413
1417 def checkinlinesize(self, tr, fp=None):
1414 def checkinlinesize(self, tr, fp=None):
1418 """Check if the revlog is too big for inline and convert if so.
1415 """Check if the revlog is too big for inline and convert if so.
1419
1416
1420 This should be called after revisions are added to the revlog. If the
1417 This should be called after revisions are added to the revlog. If the
1421 revlog has grown too large to be an inline revlog, it will convert it
1418 revlog has grown too large to be an inline revlog, it will convert it
1422 to use multiple index and data files.
1419 to use multiple index and data files.
1423 """
1420 """
1424 if not self._inline or (self.start(-2) + self.length(-2)) < _maxinline:
1421 if not self._inline or (self.start(-2) + self.length(-2)) < _maxinline:
1425 return
1422 return
1426
1423
1427 trinfo = tr.find(self.indexfile)
1424 trinfo = tr.find(self.indexfile)
1428 if trinfo is None:
1425 if trinfo is None:
1429 raise RevlogError(_("%s not found in the transaction")
1426 raise RevlogError(_("%s not found in the transaction")
1430 % self.indexfile)
1427 % self.indexfile)
1431
1428
1432 trindex = trinfo[2]
1429 trindex = trinfo[2]
1433 if trindex is not None:
1430 if trindex is not None:
1434 dataoff = self.start(trindex)
1431 dataoff = self.start(trindex)
1435 else:
1432 else:
1436 # revlog was stripped at start of transaction, use all leftover data
1433 # revlog was stripped at start of transaction, use all leftover data
1437 trindex = len(self) - 1
1434 trindex = len(self) - 1
1438 dataoff = self.end(-2)
1435 dataoff = self.end(-2)
1439
1436
1440 tr.add(self.datafile, dataoff)
1437 tr.add(self.datafile, dataoff)
1441
1438
1442 if fp:
1439 if fp:
1443 fp.flush()
1440 fp.flush()
1444 fp.close()
1441 fp.close()
1445
1442
1446 df = self.opener(self.datafile, 'w')
1443 df = self.opener(self.datafile, 'w')
1447 try:
1444 try:
1448 for r in self:
1445 for r in self:
1449 df.write(self._chunkraw(r, r)[1])
1446 df.write(self._chunkraw(r, r)[1])
1450 finally:
1447 finally:
1451 df.close()
1448 df.close()
1452
1449
1453 fp = self.opener(self.indexfile, 'w', atomictemp=True,
1450 fp = self.opener(self.indexfile, 'w', atomictemp=True,
1454 checkambig=self._checkambig)
1451 checkambig=self._checkambig)
1455 self.version &= ~(REVLOGNGINLINEDATA)
1452 self.version &= ~(REVLOGNGINLINEDATA)
1456 self._inline = False
1453 self._inline = False
1457 for i in self:
1454 for i in self:
1458 e = self._io.packentry(self.index[i], self.node, self.version, i)
1455 e = self._io.packentry(self.index[i], self.node, self.version, i)
1459 fp.write(e)
1456 fp.write(e)
1460
1457
1461 # if we don't call close, the temp file will never replace the
1458 # if we don't call close, the temp file will never replace the
1462 # real index
1459 # real index
1463 fp.close()
1460 fp.close()
1464
1461
1465 tr.replace(self.indexfile, trindex * self._io.size)
1462 tr.replace(self.indexfile, trindex * self._io.size)
1466 self._chunkclear()
1463 self._chunkclear()
1467
1464
1468 def addrevision(self, text, transaction, link, p1, p2, cachedelta=None,
1465 def addrevision(self, text, transaction, link, p1, p2, cachedelta=None,
1469 node=None, flags=REVIDX_DEFAULT_FLAGS):
1466 node=None, flags=REVIDX_DEFAULT_FLAGS):
1470 """add a revision to the log
1467 """add a revision to the log
1471
1468
1472 text - the revision data to add
1469 text - the revision data to add
1473 transaction - the transaction object used for rollback
1470 transaction - the transaction object used for rollback
1474 link - the linkrev data to add
1471 link - the linkrev data to add
1475 p1, p2 - the parent nodeids of the revision
1472 p1, p2 - the parent nodeids of the revision
1476 cachedelta - an optional precomputed delta
1473 cachedelta - an optional precomputed delta
1477 node - nodeid of revision; typically node is not specified, and it is
1474 node - nodeid of revision; typically node is not specified, and it is
1478 computed by default as hash(text, p1, p2), however subclasses might
1475 computed by default as hash(text, p1, p2), however subclasses might
1479 use different hashing method (and override checkhash() in such case)
1476 use different hashing method (and override checkhash() in such case)
1480 flags - the known flags to set on the revision
1477 flags - the known flags to set on the revision
1481 """
1478 """
1482 if link == nullrev:
1479 if link == nullrev:
1483 raise RevlogError(_("attempted to add linkrev -1 to %s")
1480 raise RevlogError(_("attempted to add linkrev -1 to %s")
1484 % self.indexfile)
1481 % self.indexfile)
1485
1482
1486 if flags:
1483 if flags:
1487 node = node or self.hash(text, p1, p2)
1484 node = node or self.hash(text, p1, p2)
1488
1485
1489 rawtext, validatehash = self._processflags(text, flags, 'write')
1486 rawtext, validatehash = self._processflags(text, flags, 'write')
1490
1487
1491 # If the flag processor modifies the revision data, ignore any provided
1488 # If the flag processor modifies the revision data, ignore any provided
1492 # cachedelta.
1489 # cachedelta.
1493 if rawtext != text:
1490 if rawtext != text:
1494 cachedelta = None
1491 cachedelta = None
1495
1492
1496 if len(rawtext) > _maxentrysize:
1493 if len(rawtext) > _maxentrysize:
1497 raise RevlogError(
1494 raise RevlogError(
1498 _("%s: size of %d bytes exceeds maximum revlog storage of 2GiB")
1495 _("%s: size of %d bytes exceeds maximum revlog storage of 2GiB")
1499 % (self.indexfile, len(rawtext)))
1496 % (self.indexfile, len(rawtext)))
1500
1497
1501 node = node or self.hash(rawtext, p1, p2)
1498 node = node or self.hash(rawtext, p1, p2)
1502 if node in self.nodemap:
1499 if node in self.nodemap:
1503 return node
1500 return node
1504
1501
1505 if validatehash:
1502 if validatehash:
1506 self.checkhash(rawtext, node, p1=p1, p2=p2)
1503 self.checkhash(rawtext, node, p1=p1, p2=p2)
1507
1504
1508 dfh = None
1505 dfh = None
1509 if not self._inline:
1506 if not self._inline:
1510 dfh = self.opener(self.datafile, "a+")
1507 dfh = self.opener(self.datafile, "a+")
1511 ifh = self.opener(self.indexfile, "a+", checkambig=self._checkambig)
1508 ifh = self.opener(self.indexfile, "a+", checkambig=self._checkambig)
1512 try:
1509 try:
1513 return self._addrevision(node, rawtext, transaction, link, p1, p2,
1510 return self._addrevision(node, rawtext, transaction, link, p1, p2,
1514 flags, cachedelta, ifh, dfh)
1511 flags, cachedelta, ifh, dfh)
1515 finally:
1512 finally:
1516 if dfh:
1513 if dfh:
1517 dfh.close()
1514 dfh.close()
1518 ifh.close()
1515 ifh.close()
1519
1516
1520 def compress(self, data):
1517 def compress(self, data):
1521 """Generate a possibly-compressed representation of data."""
1518 """Generate a possibly-compressed representation of data."""
1522 if not data:
1519 if not data:
1523 return '', data
1520 return '', data
1524
1521
1525 compressed = self._compressor.compress(data)
1522 compressed = self._compressor.compress(data)
1526
1523
1527 if compressed:
1524 if compressed:
1528 # The revlog compressor added the header in the returned data.
1525 # The revlog compressor added the header in the returned data.
1529 return '', compressed
1526 return '', compressed
1530
1527
1531 if data[0:1] == '\0':
1528 if data[0:1] == '\0':
1532 return '', data
1529 return '', data
1533 return 'u', data
1530 return 'u', data
1534
1531
1535 def decompress(self, data):
1532 def decompress(self, data):
1536 """Decompress a revlog chunk.
1533 """Decompress a revlog chunk.
1537
1534
1538 The chunk is expected to begin with a header identifying the
1535 The chunk is expected to begin with a header identifying the
1539 format type so it can be routed to an appropriate decompressor.
1536 format type so it can be routed to an appropriate decompressor.
1540 """
1537 """
1541 if not data:
1538 if not data:
1542 return data
1539 return data
1543
1540
1544 # Revlogs are read much more frequently than they are written and many
1541 # Revlogs are read much more frequently than they are written and many
1545 # chunks only take microseconds to decompress, so performance is
1542 # chunks only take microseconds to decompress, so performance is
1546 # important here.
1543 # important here.
1547 #
1544 #
1548 # We can make a few assumptions about revlogs:
1545 # We can make a few assumptions about revlogs:
1549 #
1546 #
1550 # 1) the majority of chunks will be compressed (as opposed to inline
1547 # 1) the majority of chunks will be compressed (as opposed to inline
1551 # raw data).
1548 # raw data).
1552 # 2) decompressing *any* data will likely by at least 10x slower than
1549 # 2) decompressing *any* data will likely by at least 10x slower than
1553 # returning raw inline data.
1550 # returning raw inline data.
1554 # 3) we want to prioritize common and officially supported compression
1551 # 3) we want to prioritize common and officially supported compression
1555 # engines
1552 # engines
1556 #
1553 #
1557 # It follows that we want to optimize for "decompress compressed data
1554 # It follows that we want to optimize for "decompress compressed data
1558 # when encoded with common and officially supported compression engines"
1555 # when encoded with common and officially supported compression engines"
1559 # case over "raw data" and "data encoded by less common or non-official
1556 # case over "raw data" and "data encoded by less common or non-official
1560 # compression engines." That is why we have the inline lookup first
1557 # compression engines." That is why we have the inline lookup first
1561 # followed by the compengines lookup.
1558 # followed by the compengines lookup.
1562 #
1559 #
1563 # According to `hg perfrevlogchunks`, this is ~0.5% faster for zlib
1560 # According to `hg perfrevlogchunks`, this is ~0.5% faster for zlib
1564 # compressed chunks. And this matters for changelog and manifest reads.
1561 # compressed chunks. And this matters for changelog and manifest reads.
1565 t = data[0:1]
1562 t = data[0:1]
1566
1563
1567 if t == 'x':
1564 if t == 'x':
1568 try:
1565 try:
1569 return _zlibdecompress(data)
1566 return _zlibdecompress(data)
1570 except zlib.error as e:
1567 except zlib.error as e:
1571 raise RevlogError(_('revlog decompress error: %s') % str(e))
1568 raise RevlogError(_('revlog decompress error: %s') % str(e))
1572 # '\0' is more common than 'u' so it goes first.
1569 # '\0' is more common than 'u' so it goes first.
1573 elif t == '\0':
1570 elif t == '\0':
1574 return data
1571 return data
1575 elif t == 'u':
1572 elif t == 'u':
1576 return util.buffer(data, 1)
1573 return util.buffer(data, 1)
1577
1574
1578 try:
1575 try:
1579 compressor = self._decompressors[t]
1576 compressor = self._decompressors[t]
1580 except KeyError:
1577 except KeyError:
1581 try:
1578 try:
1582 engine = util.compengines.forrevlogheader(t)
1579 engine = util.compengines.forrevlogheader(t)
1583 compressor = engine.revlogcompressor()
1580 compressor = engine.revlogcompressor()
1584 self._decompressors[t] = compressor
1581 self._decompressors[t] = compressor
1585 except KeyError:
1582 except KeyError:
1586 raise RevlogError(_('unknown compression type %r') % t)
1583 raise RevlogError(_('unknown compression type %r') % t)
1587
1584
1588 return compressor.decompress(data)
1585 return compressor.decompress(data)
1589
1586
1590 def _isgooddelta(self, d, textlen):
1587 def _isgooddelta(self, d, textlen):
1591 """Returns True if the given delta is good. Good means that it is within
1588 """Returns True if the given delta is good. Good means that it is within
1592 the disk span, disk size, and chain length bounds that we know to be
1589 the disk span, disk size, and chain length bounds that we know to be
1593 performant."""
1590 performant."""
1594 if d is None:
1591 if d is None:
1595 return False
1592 return False
1596
1593
1597 # - 'dist' is the distance from the base revision -- bounding it limits
1594 # - 'dist' is the distance from the base revision -- bounding it limits
1598 # the amount of I/O we need to do.
1595 # the amount of I/O we need to do.
1599 # - 'compresseddeltalen' is the sum of the total size of deltas we need
1596 # - 'compresseddeltalen' is the sum of the total size of deltas we need
1600 # to apply -- bounding it limits the amount of CPU we consume.
1597 # to apply -- bounding it limits the amount of CPU we consume.
1601 dist, l, data, base, chainbase, chainlen, compresseddeltalen = d
1598 dist, l, data, base, chainbase, chainlen, compresseddeltalen = d
1602
1599 if (dist > textlen * 4 or l > textlen or
1603 defaultmax = textlen * 4
1604 maxdist = self._maxdeltachainspan
1605 if not maxdist:
1606 maxdist = dist # ensure the conditional pass
1607 maxdist = max(maxdist, defaultmax)
1608 if (dist > maxdist or l > textlen or
1609 compresseddeltalen > textlen * 2 or
1600 compresseddeltalen > textlen * 2 or
1610 (self._maxchainlen and chainlen > self._maxchainlen)):
1601 (self._maxchainlen and chainlen > self._maxchainlen)):
1611 return False
1602 return False
1612
1603
1613 return True
1604 return True
1614
1605
1615 def _addrevision(self, node, rawtext, transaction, link, p1, p2, flags,
1606 def _addrevision(self, node, rawtext, transaction, link, p1, p2, flags,
1616 cachedelta, ifh, dfh, alwayscache=False):
1607 cachedelta, ifh, dfh, alwayscache=False):
1617 """internal function to add revisions to the log
1608 """internal function to add revisions to the log
1618
1609
1619 see addrevision for argument descriptions.
1610 see addrevision for argument descriptions.
1620
1611
1621 note: "addrevision" takes non-raw text, "_addrevision" takes raw text.
1612 note: "addrevision" takes non-raw text, "_addrevision" takes raw text.
1622
1613
1623 invariants:
1614 invariants:
1624 - rawtext is optional (can be None); if not set, cachedelta must be set.
1615 - rawtext is optional (can be None); if not set, cachedelta must be set.
1625 if both are set, they must correspond to each other.
1616 if both are set, they must correspond to each other.
1626 """
1617 """
1627 btext = [rawtext]
1618 btext = [rawtext]
1628 def buildtext():
1619 def buildtext():
1629 if btext[0] is not None:
1620 if btext[0] is not None:
1630 return btext[0]
1621 return btext[0]
1631 baserev = cachedelta[0]
1622 baserev = cachedelta[0]
1632 delta = cachedelta[1]
1623 delta = cachedelta[1]
1633 # special case deltas which replace entire base; no need to decode
1624 # special case deltas which replace entire base; no need to decode
1634 # base revision. this neatly avoids censored bases, which throw when
1625 # base revision. this neatly avoids censored bases, which throw when
1635 # they're decoded.
1626 # they're decoded.
1636 hlen = struct.calcsize(">lll")
1627 hlen = struct.calcsize(">lll")
1637 if delta[:hlen] == mdiff.replacediffheader(self.rawsize(baserev),
1628 if delta[:hlen] == mdiff.replacediffheader(self.rawsize(baserev),
1638 len(delta) - hlen):
1629 len(delta) - hlen):
1639 btext[0] = delta[hlen:]
1630 btext[0] = delta[hlen:]
1640 else:
1631 else:
1641 if self._inline:
1632 if self._inline:
1642 fh = ifh
1633 fh = ifh
1643 else:
1634 else:
1644 fh = dfh
1635 fh = dfh
1645 basetext = self.revision(baserev, _df=fh, raw=True)
1636 basetext = self.revision(baserev, _df=fh, raw=True)
1646 btext[0] = mdiff.patch(basetext, delta)
1637 btext[0] = mdiff.patch(basetext, delta)
1647
1638
1648 try:
1639 try:
1649 res = self._processflags(btext[0], flags, 'read', raw=True)
1640 res = self._processflags(btext[0], flags, 'read', raw=True)
1650 btext[0], validatehash = res
1641 btext[0], validatehash = res
1651 if validatehash:
1642 if validatehash:
1652 self.checkhash(btext[0], node, p1=p1, p2=p2)
1643 self.checkhash(btext[0], node, p1=p1, p2=p2)
1653 if flags & REVIDX_ISCENSORED:
1644 if flags & REVIDX_ISCENSORED:
1654 raise RevlogError(_('node %s is not censored') % node)
1645 raise RevlogError(_('node %s is not censored') % node)
1655 except CensoredNodeError:
1646 except CensoredNodeError:
1656 # must pass the censored index flag to add censored revisions
1647 # must pass the censored index flag to add censored revisions
1657 if not flags & REVIDX_ISCENSORED:
1648 if not flags & REVIDX_ISCENSORED:
1658 raise
1649 raise
1659 return btext[0]
1650 return btext[0]
1660
1651
1661 def builddelta(rev):
1652 def builddelta(rev):
1662 # can we use the cached delta?
1653 # can we use the cached delta?
1663 if cachedelta and cachedelta[0] == rev:
1654 if cachedelta and cachedelta[0] == rev:
1664 delta = cachedelta[1]
1655 delta = cachedelta[1]
1665 else:
1656 else:
1666 t = buildtext()
1657 t = buildtext()
1667 if self.iscensored(rev):
1658 if self.iscensored(rev):
1668 # deltas based on a censored revision must replace the
1659 # deltas based on a censored revision must replace the
1669 # full content in one patch, so delta works everywhere
1660 # full content in one patch, so delta works everywhere
1670 header = mdiff.replacediffheader(self.rawsize(rev), len(t))
1661 header = mdiff.replacediffheader(self.rawsize(rev), len(t))
1671 delta = header + t
1662 delta = header + t
1672 else:
1663 else:
1673 if self._inline:
1664 if self._inline:
1674 fh = ifh
1665 fh = ifh
1675 else:
1666 else:
1676 fh = dfh
1667 fh = dfh
1677 ptext = self.revision(rev, _df=fh, raw=True)
1668 ptext = self.revision(rev, _df=fh, raw=True)
1678 delta = mdiff.textdiff(ptext, t)
1669 delta = mdiff.textdiff(ptext, t)
1679 header, data = self.compress(delta)
1670 header, data = self.compress(delta)
1680 deltalen = len(header) + len(data)
1671 deltalen = len(header) + len(data)
1681 chainbase = self.chainbase(rev)
1672 chainbase = self.chainbase(rev)
1682 dist = deltalen + offset - self.start(chainbase)
1673 dist = deltalen + offset - self.start(chainbase)
1683 if self._generaldelta:
1674 if self._generaldelta:
1684 base = rev
1675 base = rev
1685 else:
1676 else:
1686 base = chainbase
1677 base = chainbase
1687 chainlen, compresseddeltalen = self._chaininfo(rev)
1678 chainlen, compresseddeltalen = self._chaininfo(rev)
1688 chainlen += 1
1679 chainlen += 1
1689 compresseddeltalen += deltalen
1680 compresseddeltalen += deltalen
1690 return (dist, deltalen, (header, data), base,
1681 return (dist, deltalen, (header, data), base,
1691 chainbase, chainlen, compresseddeltalen)
1682 chainbase, chainlen, compresseddeltalen)
1692
1683
1693 curr = len(self)
1684 curr = len(self)
1694 prev = curr - 1
1685 prev = curr - 1
1695 offset = self.end(prev)
1686 offset = self.end(prev)
1696 delta = None
1687 delta = None
1697 p1r, p2r = self.rev(p1), self.rev(p2)
1688 p1r, p2r = self.rev(p1), self.rev(p2)
1698
1689
1699 # full versions are inserted when the needed deltas
1690 # full versions are inserted when the needed deltas
1700 # become comparable to the uncompressed text
1691 # become comparable to the uncompressed text
1701 if rawtext is None:
1692 if rawtext is None:
1702 textlen = mdiff.patchedsize(self.rawsize(cachedelta[0]),
1693 textlen = mdiff.patchedsize(self.rawsize(cachedelta[0]),
1703 cachedelta[1])
1694 cachedelta[1])
1704 else:
1695 else:
1705 textlen = len(rawtext)
1696 textlen = len(rawtext)
1706
1697
1707 # should we try to build a delta?
1698 # should we try to build a delta?
1708 if prev != nullrev and self.storedeltachains:
1699 if prev != nullrev and self.storedeltachains:
1709 tested = set()
1700 tested = set()
1710 # This condition is true most of the time when processing
1701 # This condition is true most of the time when processing
1711 # changegroup data into a generaldelta repo. The only time it
1702 # changegroup data into a generaldelta repo. The only time it
1712 # isn't true is if this is the first revision in a delta chain
1703 # isn't true is if this is the first revision in a delta chain
1713 # or if ``format.generaldelta=true`` disabled ``lazydeltabase``.
1704 # or if ``format.generaldelta=true`` disabled ``lazydeltabase``.
1714 if cachedelta and self._generaldelta and self._lazydeltabase:
1705 if cachedelta and self._generaldelta and self._lazydeltabase:
1715 # Assume what we received from the server is a good choice
1706 # Assume what we received from the server is a good choice
1716 # build delta will reuse the cache
1707 # build delta will reuse the cache
1717 candidatedelta = builddelta(cachedelta[0])
1708 candidatedelta = builddelta(cachedelta[0])
1718 tested.add(cachedelta[0])
1709 tested.add(cachedelta[0])
1719 if self._isgooddelta(candidatedelta, textlen):
1710 if self._isgooddelta(candidatedelta, textlen):
1720 delta = candidatedelta
1711 delta = candidatedelta
1721 if delta is None and self._generaldelta:
1712 if delta is None and self._generaldelta:
1722 # exclude already lazy tested base if any
1713 # exclude already lazy tested base if any
1723 parents = [p for p in (p1r, p2r)
1714 parents = [p for p in (p1r, p2r)
1724 if p != nullrev and p not in tested]
1715 if p != nullrev and p not in tested]
1725 if parents and not self._aggressivemergedeltas:
1716 if parents and not self._aggressivemergedeltas:
1726 # Pick whichever parent is closer to us (to minimize the
1717 # Pick whichever parent is closer to us (to minimize the
1727 # chance of having to build a fulltext).
1718 # chance of having to build a fulltext).
1728 parents = [max(parents)]
1719 parents = [max(parents)]
1729 tested.update(parents)
1720 tested.update(parents)
1730 pdeltas = []
1721 pdeltas = []
1731 for p in parents:
1722 for p in parents:
1732 pd = builddelta(p)
1723 pd = builddelta(p)
1733 if self._isgooddelta(pd, textlen):
1724 if self._isgooddelta(pd, textlen):
1734 pdeltas.append(pd)
1725 pdeltas.append(pd)
1735 if pdeltas:
1726 if pdeltas:
1736 delta = min(pdeltas, key=lambda x: x[1])
1727 delta = min(pdeltas, key=lambda x: x[1])
1737 if delta is None and prev not in tested:
1728 if delta is None and prev not in tested:
1738 # other approach failed try against prev to hopefully save us a
1729 # other approach failed try against prev to hopefully save us a
1739 # fulltext.
1730 # fulltext.
1740 candidatedelta = builddelta(prev)
1731 candidatedelta = builddelta(prev)
1741 if self._isgooddelta(candidatedelta, textlen):
1732 if self._isgooddelta(candidatedelta, textlen):
1742 delta = candidatedelta
1733 delta = candidatedelta
1743 if delta is not None:
1734 if delta is not None:
1744 dist, l, data, base, chainbase, chainlen, compresseddeltalen = delta
1735 dist, l, data, base, chainbase, chainlen, compresseddeltalen = delta
1745 else:
1736 else:
1746 rawtext = buildtext()
1737 rawtext = buildtext()
1747 data = self.compress(rawtext)
1738 data = self.compress(rawtext)
1748 l = len(data[1]) + len(data[0])
1739 l = len(data[1]) + len(data[0])
1749 base = chainbase = curr
1740 base = chainbase = curr
1750
1741
1751 e = (offset_type(offset, flags), l, textlen,
1742 e = (offset_type(offset, flags), l, textlen,
1752 base, link, p1r, p2r, node)
1743 base, link, p1r, p2r, node)
1753 self.index.insert(-1, e)
1744 self.index.insert(-1, e)
1754 self.nodemap[node] = curr
1745 self.nodemap[node] = curr
1755
1746
1756 entry = self._io.packentry(e, self.node, self.version, curr)
1747 entry = self._io.packentry(e, self.node, self.version, curr)
1757 self._writeentry(transaction, ifh, dfh, entry, data, link, offset)
1748 self._writeentry(transaction, ifh, dfh, entry, data, link, offset)
1758
1749
1759 if alwayscache and rawtext is None:
1750 if alwayscache and rawtext is None:
1760 rawtext = buildtext()
1751 rawtext = buildtext()
1761
1752
1762 if type(rawtext) == str: # only accept immutable objects
1753 if type(rawtext) == str: # only accept immutable objects
1763 self._cache = (node, curr, rawtext)
1754 self._cache = (node, curr, rawtext)
1764 self._chainbasecache[curr] = chainbase
1755 self._chainbasecache[curr] = chainbase
1765 return node
1756 return node
1766
1757
1767 def _writeentry(self, transaction, ifh, dfh, entry, data, link, offset):
1758 def _writeentry(self, transaction, ifh, dfh, entry, data, link, offset):
1768 # Files opened in a+ mode have inconsistent behavior on various
1759 # Files opened in a+ mode have inconsistent behavior on various
1769 # platforms. Windows requires that a file positioning call be made
1760 # platforms. Windows requires that a file positioning call be made
1770 # when the file handle transitions between reads and writes. See
1761 # when the file handle transitions between reads and writes. See
1771 # 3686fa2b8eee and the mixedfilemodewrapper in windows.py. On other
1762 # 3686fa2b8eee and the mixedfilemodewrapper in windows.py. On other
1772 # platforms, Python or the platform itself can be buggy. Some versions
1763 # platforms, Python or the platform itself can be buggy. Some versions
1773 # of Solaris have been observed to not append at the end of the file
1764 # of Solaris have been observed to not append at the end of the file
1774 # if the file was seeked to before the end. See issue4943 for more.
1765 # if the file was seeked to before the end. See issue4943 for more.
1775 #
1766 #
1776 # We work around this issue by inserting a seek() before writing.
1767 # We work around this issue by inserting a seek() before writing.
1777 # Note: This is likely not necessary on Python 3.
1768 # Note: This is likely not necessary on Python 3.
1778 ifh.seek(0, os.SEEK_END)
1769 ifh.seek(0, os.SEEK_END)
1779 if dfh:
1770 if dfh:
1780 dfh.seek(0, os.SEEK_END)
1771 dfh.seek(0, os.SEEK_END)
1781
1772
1782 curr = len(self) - 1
1773 curr = len(self) - 1
1783 if not self._inline:
1774 if not self._inline:
1784 transaction.add(self.datafile, offset)
1775 transaction.add(self.datafile, offset)
1785 transaction.add(self.indexfile, curr * len(entry))
1776 transaction.add(self.indexfile, curr * len(entry))
1786 if data[0]:
1777 if data[0]:
1787 dfh.write(data[0])
1778 dfh.write(data[0])
1788 dfh.write(data[1])
1779 dfh.write(data[1])
1789 ifh.write(entry)
1780 ifh.write(entry)
1790 else:
1781 else:
1791 offset += curr * self._io.size
1782 offset += curr * self._io.size
1792 transaction.add(self.indexfile, offset, curr)
1783 transaction.add(self.indexfile, offset, curr)
1793 ifh.write(entry)
1784 ifh.write(entry)
1794 ifh.write(data[0])
1785 ifh.write(data[0])
1795 ifh.write(data[1])
1786 ifh.write(data[1])
1796 self.checkinlinesize(transaction, ifh)
1787 self.checkinlinesize(transaction, ifh)
1797
1788
1798 def addgroup(self, cg, linkmapper, transaction, addrevisioncb=None):
1789 def addgroup(self, cg, linkmapper, transaction, addrevisioncb=None):
1799 """
1790 """
1800 add a delta group
1791 add a delta group
1801
1792
1802 given a set of deltas, add them to the revision log. the
1793 given a set of deltas, add them to the revision log. the
1803 first delta is against its parent, which should be in our
1794 first delta is against its parent, which should be in our
1804 log, the rest are against the previous delta.
1795 log, the rest are against the previous delta.
1805
1796
1806 If ``addrevisioncb`` is defined, it will be called with arguments of
1797 If ``addrevisioncb`` is defined, it will be called with arguments of
1807 this revlog and the node that was added.
1798 this revlog and the node that was added.
1808 """
1799 """
1809
1800
1810 # track the base of the current delta log
1801 # track the base of the current delta log
1811 content = []
1802 content = []
1812 node = None
1803 node = None
1813
1804
1814 r = len(self)
1805 r = len(self)
1815 end = 0
1806 end = 0
1816 if r:
1807 if r:
1817 end = self.end(r - 1)
1808 end = self.end(r - 1)
1818 ifh = self.opener(self.indexfile, "a+", checkambig=self._checkambig)
1809 ifh = self.opener(self.indexfile, "a+", checkambig=self._checkambig)
1819 isize = r * self._io.size
1810 isize = r * self._io.size
1820 if self._inline:
1811 if self._inline:
1821 transaction.add(self.indexfile, end + isize, r)
1812 transaction.add(self.indexfile, end + isize, r)
1822 dfh = None
1813 dfh = None
1823 else:
1814 else:
1824 transaction.add(self.indexfile, isize, r)
1815 transaction.add(self.indexfile, isize, r)
1825 transaction.add(self.datafile, end)
1816 transaction.add(self.datafile, end)
1826 dfh = self.opener(self.datafile, "a+")
1817 dfh = self.opener(self.datafile, "a+")
1827 def flush():
1818 def flush():
1828 if dfh:
1819 if dfh:
1829 dfh.flush()
1820 dfh.flush()
1830 ifh.flush()
1821 ifh.flush()
1831 try:
1822 try:
1832 # loop through our set of deltas
1823 # loop through our set of deltas
1833 chain = None
1824 chain = None
1834 for chunkdata in iter(lambda: cg.deltachunk(chain), {}):
1825 for chunkdata in iter(lambda: cg.deltachunk(chain), {}):
1835 node = chunkdata['node']
1826 node = chunkdata['node']
1836 p1 = chunkdata['p1']
1827 p1 = chunkdata['p1']
1837 p2 = chunkdata['p2']
1828 p2 = chunkdata['p2']
1838 cs = chunkdata['cs']
1829 cs = chunkdata['cs']
1839 deltabase = chunkdata['deltabase']
1830 deltabase = chunkdata['deltabase']
1840 delta = chunkdata['delta']
1831 delta = chunkdata['delta']
1841 flags = chunkdata['flags'] or REVIDX_DEFAULT_FLAGS
1832 flags = chunkdata['flags'] or REVIDX_DEFAULT_FLAGS
1842
1833
1843 content.append(node)
1834 content.append(node)
1844
1835
1845 link = linkmapper(cs)
1836 link = linkmapper(cs)
1846 if node in self.nodemap:
1837 if node in self.nodemap:
1847 # this can happen if two branches make the same change
1838 # this can happen if two branches make the same change
1848 chain = node
1839 chain = node
1849 continue
1840 continue
1850
1841
1851 for p in (p1, p2):
1842 for p in (p1, p2):
1852 if p not in self.nodemap:
1843 if p not in self.nodemap:
1853 raise LookupError(p, self.indexfile,
1844 raise LookupError(p, self.indexfile,
1854 _('unknown parent'))
1845 _('unknown parent'))
1855
1846
1856 if deltabase not in self.nodemap:
1847 if deltabase not in self.nodemap:
1857 raise LookupError(deltabase, self.indexfile,
1848 raise LookupError(deltabase, self.indexfile,
1858 _('unknown delta base'))
1849 _('unknown delta base'))
1859
1850
1860 baserev = self.rev(deltabase)
1851 baserev = self.rev(deltabase)
1861
1852
1862 if baserev != nullrev and self.iscensored(baserev):
1853 if baserev != nullrev and self.iscensored(baserev):
1863 # if base is censored, delta must be full replacement in a
1854 # if base is censored, delta must be full replacement in a
1864 # single patch operation
1855 # single patch operation
1865 hlen = struct.calcsize(">lll")
1856 hlen = struct.calcsize(">lll")
1866 oldlen = self.rawsize(baserev)
1857 oldlen = self.rawsize(baserev)
1867 newlen = len(delta) - hlen
1858 newlen = len(delta) - hlen
1868 if delta[:hlen] != mdiff.replacediffheader(oldlen, newlen):
1859 if delta[:hlen] != mdiff.replacediffheader(oldlen, newlen):
1869 raise error.CensoredBaseError(self.indexfile,
1860 raise error.CensoredBaseError(self.indexfile,
1870 self.node(baserev))
1861 self.node(baserev))
1871
1862
1872 if not flags and self._peek_iscensored(baserev, delta, flush):
1863 if not flags and self._peek_iscensored(baserev, delta, flush):
1873 flags |= REVIDX_ISCENSORED
1864 flags |= REVIDX_ISCENSORED
1874
1865
1875 # We assume consumers of addrevisioncb will want to retrieve
1866 # We assume consumers of addrevisioncb will want to retrieve
1876 # the added revision, which will require a call to
1867 # the added revision, which will require a call to
1877 # revision(). revision() will fast path if there is a cache
1868 # revision(). revision() will fast path if there is a cache
1878 # hit. So, we tell _addrevision() to always cache in this case.
1869 # hit. So, we tell _addrevision() to always cache in this case.
1879 # We're only using addgroup() in the context of changegroup
1870 # We're only using addgroup() in the context of changegroup
1880 # generation so the revision data can always be handled as raw
1871 # generation so the revision data can always be handled as raw
1881 # by the flagprocessor.
1872 # by the flagprocessor.
1882 chain = self._addrevision(node, None, transaction, link,
1873 chain = self._addrevision(node, None, transaction, link,
1883 p1, p2, flags, (baserev, delta),
1874 p1, p2, flags, (baserev, delta),
1884 ifh, dfh,
1875 ifh, dfh,
1885 alwayscache=bool(addrevisioncb))
1876 alwayscache=bool(addrevisioncb))
1886
1877
1887 if addrevisioncb:
1878 if addrevisioncb:
1888 addrevisioncb(self, chain)
1879 addrevisioncb(self, chain)
1889
1880
1890 if not dfh and not self._inline:
1881 if not dfh and not self._inline:
1891 # addrevision switched from inline to conventional
1882 # addrevision switched from inline to conventional
1892 # reopen the index
1883 # reopen the index
1893 ifh.close()
1884 ifh.close()
1894 dfh = self.opener(self.datafile, "a+")
1885 dfh = self.opener(self.datafile, "a+")
1895 ifh = self.opener(self.indexfile, "a+",
1886 ifh = self.opener(self.indexfile, "a+",
1896 checkambig=self._checkambig)
1887 checkambig=self._checkambig)
1897 finally:
1888 finally:
1898 if dfh:
1889 if dfh:
1899 dfh.close()
1890 dfh.close()
1900 ifh.close()
1891 ifh.close()
1901
1892
1902 return content
1893 return content
1903
1894
1904 def iscensored(self, rev):
1895 def iscensored(self, rev):
1905 """Check if a file revision is censored."""
1896 """Check if a file revision is censored."""
1906 return False
1897 return False
1907
1898
1908 def _peek_iscensored(self, baserev, delta, flush):
1899 def _peek_iscensored(self, baserev, delta, flush):
1909 """Quickly check if a delta produces a censored revision."""
1900 """Quickly check if a delta produces a censored revision."""
1910 return False
1901 return False
1911
1902
1912 def getstrippoint(self, minlink):
1903 def getstrippoint(self, minlink):
1913 """find the minimum rev that must be stripped to strip the linkrev
1904 """find the minimum rev that must be stripped to strip the linkrev
1914
1905
1915 Returns a tuple containing the minimum rev and a set of all revs that
1906 Returns a tuple containing the minimum rev and a set of all revs that
1916 have linkrevs that will be broken by this strip.
1907 have linkrevs that will be broken by this strip.
1917 """
1908 """
1918 brokenrevs = set()
1909 brokenrevs = set()
1919 strippoint = len(self)
1910 strippoint = len(self)
1920
1911
1921 heads = {}
1912 heads = {}
1922 futurelargelinkrevs = set()
1913 futurelargelinkrevs = set()
1923 for head in self.headrevs():
1914 for head in self.headrevs():
1924 headlinkrev = self.linkrev(head)
1915 headlinkrev = self.linkrev(head)
1925 heads[head] = headlinkrev
1916 heads[head] = headlinkrev
1926 if headlinkrev >= minlink:
1917 if headlinkrev >= minlink:
1927 futurelargelinkrevs.add(headlinkrev)
1918 futurelargelinkrevs.add(headlinkrev)
1928
1919
1929 # This algorithm involves walking down the rev graph, starting at the
1920 # This algorithm involves walking down the rev graph, starting at the
1930 # heads. Since the revs are topologically sorted according to linkrev,
1921 # heads. Since the revs are topologically sorted according to linkrev,
1931 # once all head linkrevs are below the minlink, we know there are
1922 # once all head linkrevs are below the minlink, we know there are
1932 # no more revs that could have a linkrev greater than minlink.
1923 # no more revs that could have a linkrev greater than minlink.
1933 # So we can stop walking.
1924 # So we can stop walking.
1934 while futurelargelinkrevs:
1925 while futurelargelinkrevs:
1935 strippoint -= 1
1926 strippoint -= 1
1936 linkrev = heads.pop(strippoint)
1927 linkrev = heads.pop(strippoint)
1937
1928
1938 if linkrev < minlink:
1929 if linkrev < minlink:
1939 brokenrevs.add(strippoint)
1930 brokenrevs.add(strippoint)
1940 else:
1931 else:
1941 futurelargelinkrevs.remove(linkrev)
1932 futurelargelinkrevs.remove(linkrev)
1942
1933
1943 for p in self.parentrevs(strippoint):
1934 for p in self.parentrevs(strippoint):
1944 if p != nullrev:
1935 if p != nullrev:
1945 plinkrev = self.linkrev(p)
1936 plinkrev = self.linkrev(p)
1946 heads[p] = plinkrev
1937 heads[p] = plinkrev
1947 if plinkrev >= minlink:
1938 if plinkrev >= minlink:
1948 futurelargelinkrevs.add(plinkrev)
1939 futurelargelinkrevs.add(plinkrev)
1949
1940
1950 return strippoint, brokenrevs
1941 return strippoint, brokenrevs
1951
1942
1952 def strip(self, minlink, transaction):
1943 def strip(self, minlink, transaction):
1953 """truncate the revlog on the first revision with a linkrev >= minlink
1944 """truncate the revlog on the first revision with a linkrev >= minlink
1954
1945
1955 This function is called when we're stripping revision minlink and
1946 This function is called when we're stripping revision minlink and
1956 its descendants from the repository.
1947 its descendants from the repository.
1957
1948
1958 We have to remove all revisions with linkrev >= minlink, because
1949 We have to remove all revisions with linkrev >= minlink, because
1959 the equivalent changelog revisions will be renumbered after the
1950 the equivalent changelog revisions will be renumbered after the
1960 strip.
1951 strip.
1961
1952
1962 So we truncate the revlog on the first of these revisions, and
1953 So we truncate the revlog on the first of these revisions, and
1963 trust that the caller has saved the revisions that shouldn't be
1954 trust that the caller has saved the revisions that shouldn't be
1964 removed and that it'll re-add them after this truncation.
1955 removed and that it'll re-add them after this truncation.
1965 """
1956 """
1966 if len(self) == 0:
1957 if len(self) == 0:
1967 return
1958 return
1968
1959
1969 rev, _ = self.getstrippoint(minlink)
1960 rev, _ = self.getstrippoint(minlink)
1970 if rev == len(self):
1961 if rev == len(self):
1971 return
1962 return
1972
1963
1973 # first truncate the files on disk
1964 # first truncate the files on disk
1974 end = self.start(rev)
1965 end = self.start(rev)
1975 if not self._inline:
1966 if not self._inline:
1976 transaction.add(self.datafile, end)
1967 transaction.add(self.datafile, end)
1977 end = rev * self._io.size
1968 end = rev * self._io.size
1978 else:
1969 else:
1979 end += rev * self._io.size
1970 end += rev * self._io.size
1980
1971
1981 transaction.add(self.indexfile, end)
1972 transaction.add(self.indexfile, end)
1982
1973
1983 # then reset internal state in memory to forget those revisions
1974 # then reset internal state in memory to forget those revisions
1984 self._cache = None
1975 self._cache = None
1985 self._chaininfocache = {}
1976 self._chaininfocache = {}
1986 self._chunkclear()
1977 self._chunkclear()
1987 for x in xrange(rev, len(self)):
1978 for x in xrange(rev, len(self)):
1988 del self.nodemap[self.node(x)]
1979 del self.nodemap[self.node(x)]
1989
1980
1990 del self.index[rev:-1]
1981 del self.index[rev:-1]
1991
1982
1992 def checksize(self):
1983 def checksize(self):
1993 expected = 0
1984 expected = 0
1994 if len(self):
1985 if len(self):
1995 expected = max(0, self.end(len(self) - 1))
1986 expected = max(0, self.end(len(self) - 1))
1996
1987
1997 try:
1988 try:
1998 f = self.opener(self.datafile)
1989 f = self.opener(self.datafile)
1999 f.seek(0, 2)
1990 f.seek(0, 2)
2000 actual = f.tell()
1991 actual = f.tell()
2001 f.close()
1992 f.close()
2002 dd = actual - expected
1993 dd = actual - expected
2003 except IOError as inst:
1994 except IOError as inst:
2004 if inst.errno != errno.ENOENT:
1995 if inst.errno != errno.ENOENT:
2005 raise
1996 raise
2006 dd = 0
1997 dd = 0
2007
1998
2008 try:
1999 try:
2009 f = self.opener(self.indexfile)
2000 f = self.opener(self.indexfile)
2010 f.seek(0, 2)
2001 f.seek(0, 2)
2011 actual = f.tell()
2002 actual = f.tell()
2012 f.close()
2003 f.close()
2013 s = self._io.size
2004 s = self._io.size
2014 i = max(0, actual // s)
2005 i = max(0, actual // s)
2015 di = actual - (i * s)
2006 di = actual - (i * s)
2016 if self._inline:
2007 if self._inline:
2017 databytes = 0
2008 databytes = 0
2018 for r in self:
2009 for r in self:
2019 databytes += max(0, self.length(r))
2010 databytes += max(0, self.length(r))
2020 dd = 0
2011 dd = 0
2021 di = actual - len(self) * s - databytes
2012 di = actual - len(self) * s - databytes
2022 except IOError as inst:
2013 except IOError as inst:
2023 if inst.errno != errno.ENOENT:
2014 if inst.errno != errno.ENOENT:
2024 raise
2015 raise
2025 di = 0
2016 di = 0
2026
2017
2027 return (dd, di)
2018 return (dd, di)
2028
2019
2029 def files(self):
2020 def files(self):
2030 res = [self.indexfile]
2021 res = [self.indexfile]
2031 if not self._inline:
2022 if not self._inline:
2032 res.append(self.datafile)
2023 res.append(self.datafile)
2033 return res
2024 return res
2034
2025
2035 DELTAREUSEALWAYS = 'always'
2026 DELTAREUSEALWAYS = 'always'
2036 DELTAREUSESAMEREVS = 'samerevs'
2027 DELTAREUSESAMEREVS = 'samerevs'
2037 DELTAREUSENEVER = 'never'
2028 DELTAREUSENEVER = 'never'
2038
2029
2039 DELTAREUSEALL = set(['always', 'samerevs', 'never'])
2030 DELTAREUSEALL = set(['always', 'samerevs', 'never'])
2040
2031
2041 def clone(self, tr, destrevlog, addrevisioncb=None,
2032 def clone(self, tr, destrevlog, addrevisioncb=None,
2042 deltareuse=DELTAREUSESAMEREVS, aggressivemergedeltas=None):
2033 deltareuse=DELTAREUSESAMEREVS, aggressivemergedeltas=None):
2043 """Copy this revlog to another, possibly with format changes.
2034 """Copy this revlog to another, possibly with format changes.
2044
2035
2045 The destination revlog will contain the same revisions and nodes.
2036 The destination revlog will contain the same revisions and nodes.
2046 However, it may not be bit-for-bit identical due to e.g. delta encoding
2037 However, it may not be bit-for-bit identical due to e.g. delta encoding
2047 differences.
2038 differences.
2048
2039
2049 The ``deltareuse`` argument control how deltas from the existing revlog
2040 The ``deltareuse`` argument control how deltas from the existing revlog
2050 are preserved in the destination revlog. The argument can have the
2041 are preserved in the destination revlog. The argument can have the
2051 following values:
2042 following values:
2052
2043
2053 DELTAREUSEALWAYS
2044 DELTAREUSEALWAYS
2054 Deltas will always be reused (if possible), even if the destination
2045 Deltas will always be reused (if possible), even if the destination
2055 revlog would not select the same revisions for the delta. This is the
2046 revlog would not select the same revisions for the delta. This is the
2056 fastest mode of operation.
2047 fastest mode of operation.
2057 DELTAREUSESAMEREVS
2048 DELTAREUSESAMEREVS
2058 Deltas will be reused if the destination revlog would pick the same
2049 Deltas will be reused if the destination revlog would pick the same
2059 revisions for the delta. This mode strikes a balance between speed
2050 revisions for the delta. This mode strikes a balance between speed
2060 and optimization.
2051 and optimization.
2061 DELTAREUSENEVER
2052 DELTAREUSENEVER
2062 Deltas will never be reused. This is the slowest mode of execution.
2053 Deltas will never be reused. This is the slowest mode of execution.
2063 This mode can be used to recompute deltas (e.g. if the diff/delta
2054 This mode can be used to recompute deltas (e.g. if the diff/delta
2064 algorithm changes).
2055 algorithm changes).
2065
2056
2066 Delta computation can be slow, so the choice of delta reuse policy can
2057 Delta computation can be slow, so the choice of delta reuse policy can
2067 significantly affect run time.
2058 significantly affect run time.
2068
2059
2069 The default policy (``DELTAREUSESAMEREVS``) strikes a balance between
2060 The default policy (``DELTAREUSESAMEREVS``) strikes a balance between
2070 two extremes. Deltas will be reused if they are appropriate. But if the
2061 two extremes. Deltas will be reused if they are appropriate. But if the
2071 delta could choose a better revision, it will do so. This means if you
2062 delta could choose a better revision, it will do so. This means if you
2072 are converting a non-generaldelta revlog to a generaldelta revlog,
2063 are converting a non-generaldelta revlog to a generaldelta revlog,
2073 deltas will be recomputed if the delta's parent isn't a parent of the
2064 deltas will be recomputed if the delta's parent isn't a parent of the
2074 revision.
2065 revision.
2075
2066
2076 In addition to the delta policy, the ``aggressivemergedeltas`` argument
2067 In addition to the delta policy, the ``aggressivemergedeltas`` argument
2077 controls whether to compute deltas against both parents for merges.
2068 controls whether to compute deltas against both parents for merges.
2078 By default, the current default is used.
2069 By default, the current default is used.
2079 """
2070 """
2080 if deltareuse not in self.DELTAREUSEALL:
2071 if deltareuse not in self.DELTAREUSEALL:
2081 raise ValueError(_('value for deltareuse invalid: %s') % deltareuse)
2072 raise ValueError(_('value for deltareuse invalid: %s') % deltareuse)
2082
2073
2083 if len(destrevlog):
2074 if len(destrevlog):
2084 raise ValueError(_('destination revlog is not empty'))
2075 raise ValueError(_('destination revlog is not empty'))
2085
2076
2086 if getattr(self, 'filteredrevs', None):
2077 if getattr(self, 'filteredrevs', None):
2087 raise ValueError(_('source revlog has filtered revisions'))
2078 raise ValueError(_('source revlog has filtered revisions'))
2088 if getattr(destrevlog, 'filteredrevs', None):
2079 if getattr(destrevlog, 'filteredrevs', None):
2089 raise ValueError(_('destination revlog has filtered revisions'))
2080 raise ValueError(_('destination revlog has filtered revisions'))
2090
2081
2091 # lazydeltabase controls whether to reuse a cached delta, if possible.
2082 # lazydeltabase controls whether to reuse a cached delta, if possible.
2092 oldlazydeltabase = destrevlog._lazydeltabase
2083 oldlazydeltabase = destrevlog._lazydeltabase
2093 oldamd = destrevlog._aggressivemergedeltas
2084 oldamd = destrevlog._aggressivemergedeltas
2094
2085
2095 try:
2086 try:
2096 if deltareuse == self.DELTAREUSEALWAYS:
2087 if deltareuse == self.DELTAREUSEALWAYS:
2097 destrevlog._lazydeltabase = True
2088 destrevlog._lazydeltabase = True
2098 elif deltareuse == self.DELTAREUSESAMEREVS:
2089 elif deltareuse == self.DELTAREUSESAMEREVS:
2099 destrevlog._lazydeltabase = False
2090 destrevlog._lazydeltabase = False
2100
2091
2101 destrevlog._aggressivemergedeltas = aggressivemergedeltas or oldamd
2092 destrevlog._aggressivemergedeltas = aggressivemergedeltas or oldamd
2102
2093
2103 populatecachedelta = deltareuse in (self.DELTAREUSEALWAYS,
2094 populatecachedelta = deltareuse in (self.DELTAREUSEALWAYS,
2104 self.DELTAREUSESAMEREVS)
2095 self.DELTAREUSESAMEREVS)
2105
2096
2106 index = self.index
2097 index = self.index
2107 for rev in self:
2098 for rev in self:
2108 entry = index[rev]
2099 entry = index[rev]
2109
2100
2110 # Some classes override linkrev to take filtered revs into
2101 # Some classes override linkrev to take filtered revs into
2111 # account. Use raw entry from index.
2102 # account. Use raw entry from index.
2112 flags = entry[0] & 0xffff
2103 flags = entry[0] & 0xffff
2113 linkrev = entry[4]
2104 linkrev = entry[4]
2114 p1 = index[entry[5]][7]
2105 p1 = index[entry[5]][7]
2115 p2 = index[entry[6]][7]
2106 p2 = index[entry[6]][7]
2116 node = entry[7]
2107 node = entry[7]
2117
2108
2118 # (Possibly) reuse the delta from the revlog if allowed and
2109 # (Possibly) reuse the delta from the revlog if allowed and
2119 # the revlog chunk is a delta.
2110 # the revlog chunk is a delta.
2120 cachedelta = None
2111 cachedelta = None
2121 rawtext = None
2112 rawtext = None
2122 if populatecachedelta:
2113 if populatecachedelta:
2123 dp = self.deltaparent(rev)
2114 dp = self.deltaparent(rev)
2124 if dp != nullrev:
2115 if dp != nullrev:
2125 cachedelta = (dp, str(self._chunk(rev)))
2116 cachedelta = (dp, str(self._chunk(rev)))
2126
2117
2127 if not cachedelta:
2118 if not cachedelta:
2128 rawtext = self.revision(rev, raw=True)
2119 rawtext = self.revision(rev, raw=True)
2129
2120
2130 ifh = destrevlog.opener(destrevlog.indexfile, 'a+',
2121 ifh = destrevlog.opener(destrevlog.indexfile, 'a+',
2131 checkambig=False)
2122 checkambig=False)
2132 dfh = None
2123 dfh = None
2133 if not destrevlog._inline:
2124 if not destrevlog._inline:
2134 dfh = destrevlog.opener(destrevlog.datafile, 'a+')
2125 dfh = destrevlog.opener(destrevlog.datafile, 'a+')
2135 try:
2126 try:
2136 destrevlog._addrevision(node, rawtext, tr, linkrev, p1, p2,
2127 destrevlog._addrevision(node, rawtext, tr, linkrev, p1, p2,
2137 flags, cachedelta, ifh, dfh)
2128 flags, cachedelta, ifh, dfh)
2138 finally:
2129 finally:
2139 if dfh:
2130 if dfh:
2140 dfh.close()
2131 dfh.close()
2141 ifh.close()
2132 ifh.close()
2142
2133
2143 if addrevisioncb:
2134 if addrevisioncb:
2144 addrevisioncb(self, rev, node)
2135 addrevisioncb(self, rev, node)
2145 finally:
2136 finally:
2146 destrevlog._lazydeltabase = oldlazydeltabase
2137 destrevlog._lazydeltabase = oldlazydeltabase
2147 destrevlog._aggressivemergedeltas = oldamd
2138 destrevlog._aggressivemergedeltas = oldamd
@@ -1,349 +1,161 b''
1 Check whether size of generaldelta revlog is not bigger than its
1 Check whether size of generaldelta revlog is not bigger than its
2 regular equivalent. Test would fail if generaldelta was naive
2 regular equivalent. Test would fail if generaldelta was naive
3 implementation of parentdelta: third manifest revision would be fully
3 implementation of parentdelta: third manifest revision would be fully
4 inserted due to big distance from its paren revision (zero).
4 inserted due to big distance from its paren revision (zero).
5
5
6 $ hg init repo --config format.generaldelta=no --config format.usegeneraldelta=no
6 $ hg init repo --config format.generaldelta=no --config format.usegeneraldelta=no
7 $ cd repo
7 $ cd repo
8 $ echo foo > foo
8 $ echo foo > foo
9 $ echo bar > bar
9 $ echo bar > bar
10 $ echo baz > baz
10 $ echo baz > baz
11 $ hg commit -q -Am boo
11 $ hg commit -q -Am boo
12 $ hg clone --pull . ../gdrepo -q --config format.generaldelta=yes
12 $ hg clone --pull . ../gdrepo -q --config format.generaldelta=yes
13 $ for r in 1 2 3; do
13 $ for r in 1 2 3; do
14 > echo $r > foo
14 > echo $r > foo
15 > hg commit -q -m $r
15 > hg commit -q -m $r
16 > hg up -q -r 0
16 > hg up -q -r 0
17 > hg pull . -q -r $r -R ../gdrepo
17 > hg pull . -q -r $r -R ../gdrepo
18 > done
18 > done
19
19
20 $ cd ..
20 $ cd ..
21 >>> from __future__ import print_function
21 >>> from __future__ import print_function
22 >>> import os
22 >>> import os
23 >>> regsize = os.stat("repo/.hg/store/00manifest.i").st_size
23 >>> regsize = os.stat("repo/.hg/store/00manifest.i").st_size
24 >>> gdsize = os.stat("gdrepo/.hg/store/00manifest.i").st_size
24 >>> gdsize = os.stat("gdrepo/.hg/store/00manifest.i").st_size
25 >>> if regsize < gdsize:
25 >>> if regsize < gdsize:
26 ... print('generaldata increased size of manifest')
26 ... print('generaldata increased size of manifest')
27
27
28 Verify rev reordering doesnt create invalid bundles (issue4462)
28 Verify rev reordering doesnt create invalid bundles (issue4462)
29 This requires a commit tree that when pulled will reorder manifest revs such
29 This requires a commit tree that when pulled will reorder manifest revs such
30 that the second manifest to create a file rev will be ordered before the first
30 that the second manifest to create a file rev will be ordered before the first
31 manifest to create that file rev. We also need to do a partial pull to ensure
31 manifest to create that file rev. We also need to do a partial pull to ensure
32 reordering happens. At the end we verify the linkrev points at the earliest
32 reordering happens. At the end we verify the linkrev points at the earliest
33 commit.
33 commit.
34
34
35 $ hg init server --config format.generaldelta=True
35 $ hg init server --config format.generaldelta=True
36 $ cd server
36 $ cd server
37 $ touch a
37 $ touch a
38 $ hg commit -Aqm a
38 $ hg commit -Aqm a
39 $ echo x > x
39 $ echo x > x
40 $ echo y > y
40 $ echo y > y
41 $ hg commit -Aqm xy
41 $ hg commit -Aqm xy
42 $ hg up -q '.^'
42 $ hg up -q '.^'
43 $ echo x > x
43 $ echo x > x
44 $ echo z > z
44 $ echo z > z
45 $ hg commit -Aqm xz
45 $ hg commit -Aqm xz
46 $ hg up -q 1
46 $ hg up -q 1
47 $ echo b > b
47 $ echo b > b
48 $ hg commit -Aqm b
48 $ hg commit -Aqm b
49 $ hg merge -q 2
49 $ hg merge -q 2
50 $ hg commit -Aqm merge
50 $ hg commit -Aqm merge
51 $ echo c > c
51 $ echo c > c
52 $ hg commit -Aqm c
52 $ hg commit -Aqm c
53 $ hg log -G -T '{rev} {shortest(node)} {desc}'
53 $ hg log -G -T '{rev} {shortest(node)} {desc}'
54 @ 5 ebb8 c
54 @ 5 ebb8 c
55 |
55 |
56 o 4 baf7 merge
56 o 4 baf7 merge
57 |\
57 |\
58 | o 3 a129 b
58 | o 3 a129 b
59 | |
59 | |
60 o | 2 958c xz
60 o | 2 958c xz
61 | |
61 | |
62 | o 1 f00c xy
62 | o 1 f00c xy
63 |/
63 |/
64 o 0 3903 a
64 o 0 3903 a
65
65
66 $ cd ..
66 $ cd ..
67 $ hg init client --config format.generaldelta=false --config format.usegeneraldelta=false
67 $ hg init client --config format.generaldelta=false --config format.usegeneraldelta=false
68 $ cd client
68 $ cd client
69 $ hg pull -q ../server -r 4
69 $ hg pull -q ../server -r 4
70 $ hg debugindex x
70 $ hg debugindex x
71 rev offset length base linkrev nodeid p1 p2
71 rev offset length base linkrev nodeid p1 p2
72 0 0 3 0 1 1406e7411862 000000000000 000000000000
72 0 0 3 0 1 1406e7411862 000000000000 000000000000
73
73
74 $ cd ..
74 $ cd ..
75
75
76 Test "usegeneraldelta" config
76 Test "usegeneraldelta" config
77 (repo are general delta, but incoming bundle are not re-deltafied)
77 (repo are general delta, but incoming bundle are not re-deltafied)
78
78
79 delta coming from the server base delta server are not recompressed.
79 delta coming from the server base delta server are not recompressed.
80 (also include the aggressive version for comparison)
80 (also include the aggressive version for comparison)
81
81
82 $ hg clone repo --pull --config format.usegeneraldelta=1 usegd
82 $ hg clone repo --pull --config format.usegeneraldelta=1 usegd
83 requesting all changes
83 requesting all changes
84 adding changesets
84 adding changesets
85 adding manifests
85 adding manifests
86 adding file changes
86 adding file changes
87 added 4 changesets with 6 changes to 3 files (+2 heads)
87 added 4 changesets with 6 changes to 3 files (+2 heads)
88 updating to branch default
88 updating to branch default
89 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
89 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 $ hg clone repo --pull --config format.generaldelta=1 full
90 $ hg clone repo --pull --config format.generaldelta=1 full
91 requesting all changes
91 requesting all changes
92 adding changesets
92 adding changesets
93 adding manifests
93 adding manifests
94 adding file changes
94 adding file changes
95 added 4 changesets with 6 changes to 3 files (+2 heads)
95 added 4 changesets with 6 changes to 3 files (+2 heads)
96 updating to branch default
96 updating to branch default
97 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
97 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 $ hg -R repo debugindex -m
98 $ hg -R repo debugindex -m
99 rev offset length base linkrev nodeid p1 p2
99 rev offset length base linkrev nodeid p1 p2
100 0 0 104 0 0 cef96823c800 000000000000 000000000000
100 0 0 104 0 0 cef96823c800 000000000000 000000000000
101 1 104 57 0 1 58ab9a8d541d cef96823c800 000000000000
101 1 104 57 0 1 58ab9a8d541d cef96823c800 000000000000
102 2 161 57 0 2 134fdc6fd680 cef96823c800 000000000000
102 2 161 57 0 2 134fdc6fd680 cef96823c800 000000000000
103 3 218 104 3 3 723508934dad cef96823c800 000000000000
103 3 218 104 3 3 723508934dad cef96823c800 000000000000
104 $ hg -R usegd debugindex -m
104 $ hg -R usegd debugindex -m
105 rev offset length delta linkrev nodeid p1 p2
105 rev offset length delta linkrev nodeid p1 p2
106 0 0 104 -1 0 cef96823c800 000000000000 000000000000
106 0 0 104 -1 0 cef96823c800 000000000000 000000000000
107 1 104 57 0 1 58ab9a8d541d cef96823c800 000000000000
107 1 104 57 0 1 58ab9a8d541d cef96823c800 000000000000
108 2 161 57 1 2 134fdc6fd680 cef96823c800 000000000000
108 2 161 57 1 2 134fdc6fd680 cef96823c800 000000000000
109 3 218 57 0 3 723508934dad cef96823c800 000000000000
109 3 218 57 0 3 723508934dad cef96823c800 000000000000
110 $ hg -R full debugindex -m
110 $ hg -R full debugindex -m
111 rev offset length delta linkrev nodeid p1 p2
111 rev offset length delta linkrev nodeid p1 p2
112 0 0 104 -1 0 cef96823c800 000000000000 000000000000
112 0 0 104 -1 0 cef96823c800 000000000000 000000000000
113 1 104 57 0 1 58ab9a8d541d cef96823c800 000000000000
113 1 104 57 0 1 58ab9a8d541d cef96823c800 000000000000
114 2 161 57 0 2 134fdc6fd680 cef96823c800 000000000000
114 2 161 57 0 2 134fdc6fd680 cef96823c800 000000000000
115 3 218 57 0 3 723508934dad cef96823c800 000000000000
115 3 218 57 0 3 723508934dad cef96823c800 000000000000
116
116
117 Test format.aggressivemergedeltas
117 Test format.aggressivemergedeltas
118
118
119 $ hg init --config format.generaldelta=1 aggressive
119 $ hg init --config format.generaldelta=1 aggressive
120 $ cd aggressive
120 $ cd aggressive
121 $ cat << EOF >> .hg/hgrc
121 $ cat << EOF >> .hg/hgrc
122 > [format]
122 > [format]
123 > generaldelta = 1
123 > generaldelta = 1
124 > EOF
124 > EOF
125 $ touch a b c d e
125 $ touch a b c d e
126 $ hg commit -Aqm side1
126 $ hg commit -Aqm side1
127 $ hg up -q null
127 $ hg up -q null
128 $ touch x y
128 $ touch x y
129 $ hg commit -Aqm side2
129 $ hg commit -Aqm side2
130
130
131 - Verify non-aggressive merge uses p1 (commit 1) as delta parent
131 - Verify non-aggressive merge uses p1 (commit 1) as delta parent
132 $ hg merge -q 0
132 $ hg merge -q 0
133 $ hg commit -q -m merge
133 $ hg commit -q -m merge
134 $ hg debugindex -m
134 $ hg debugindex -m
135 rev offset length delta linkrev nodeid p1 p2
135 rev offset length delta linkrev nodeid p1 p2
136 0 0 59 -1 0 8dde941edb6e 000000000000 000000000000
136 0 0 59 -1 0 8dde941edb6e 000000000000 000000000000
137 1 59 61 0 1 315c023f341d 000000000000 000000000000
137 1 59 61 0 1 315c023f341d 000000000000 000000000000
138 2 120 65 1 2 2ab389a983eb 315c023f341d 8dde941edb6e
138 2 120 65 1 2 2ab389a983eb 315c023f341d 8dde941edb6e
139
139
140 $ hg strip -q -r . --config extensions.strip=
140 $ hg strip -q -r . --config extensions.strip=
141
141
142 - Verify aggressive merge uses p2 (commit 0) as delta parent
142 - Verify aggressive merge uses p2 (commit 0) as delta parent
143 $ hg up -q -C 1
143 $ hg up -q -C 1
144 $ hg merge -q 0
144 $ hg merge -q 0
145 $ hg commit -q -m merge --config format.aggressivemergedeltas=True
145 $ hg commit -q -m merge --config format.aggressivemergedeltas=True
146 $ hg debugindex -m
146 $ hg debugindex -m
147 rev offset length delta linkrev nodeid p1 p2
147 rev offset length delta linkrev nodeid p1 p2
148 0 0 59 -1 0 8dde941edb6e 000000000000 000000000000
148 0 0 59 -1 0 8dde941edb6e 000000000000 000000000000
149 1 59 61 0 1 315c023f341d 000000000000 000000000000
149 1 59 61 0 1 315c023f341d 000000000000 000000000000
150 2 120 62 0 2 2ab389a983eb 315c023f341d 8dde941edb6e
150 2 120 62 0 2 2ab389a983eb 315c023f341d 8dde941edb6e
151
151
152 Test that strip bundle use bundle2
152 Test that strip bundle use bundle2
153 $ hg --config extensions.strip= strip .
153 $ hg --config extensions.strip= strip .
154 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
154 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
155 saved backup bundle to $TESTTMP/aggressive/.hg/strip-backup/1c5d4dc9a8b8-6c68e60c-backup.hg (glob)
155 saved backup bundle to $TESTTMP/aggressive/.hg/strip-backup/1c5d4dc9a8b8-6c68e60c-backup.hg (glob)
156 $ hg debugbundle .hg/strip-backup/*
156 $ hg debugbundle .hg/strip-backup/*
157 Stream params: sortdict([('Compression', 'BZ')])
157 Stream params: sortdict([('Compression', 'BZ')])
158 changegroup -- "sortdict([('version', '02'), ('nbchanges', '1')])"
158 changegroup -- "sortdict([('version', '02'), ('nbchanges', '1')])"
159 1c5d4dc9a8b8d6e1750966d343e94db665e7a1e9
159 1c5d4dc9a8b8d6e1750966d343e94db665e7a1e9
160
160
161 $ cd ..
161 $ cd ..
162
163 test maxdeltachainspan
164
165 $ hg init source-repo
166 $ cd source-repo
167 $ hg debugbuilddag --new-file '.+5:brancha$.+11:branchb$.+30:branchc<brancha+2<branchb+2'
168 $ cd ..
169 $ hg -R source-repo debugindex -m
170 rev offset length delta linkrev nodeid p1 p2
171 0 0 46 -1 0 19deeef41503 000000000000 000000000000
172 1 46 57 0 1 fffc37b38c40 19deeef41503 000000000000
173 2 103 57 1 2 5822d75c83d9 fffc37b38c40 000000000000
174 3 160 57 2 3 19cf2273e601 5822d75c83d9 000000000000
175 4 217 57 3 4 d45ead487afe 19cf2273e601 000000000000
176 5 274 57 4 5 96e0c2ce55ed d45ead487afe 000000000000
177 6 331 46 -1 6 0c2ea5222c74 000000000000 000000000000
178 7 377 57 6 7 4ca08a89134d 0c2ea5222c74 000000000000
179 8 434 57 7 8 c973dbfd30ac 4ca08a89134d 000000000000
180 9 491 57 8 9 d81d878ff2cd c973dbfd30ac 000000000000
181 10 548 58 9 10 dbee7f0dd760 d81d878ff2cd 000000000000
182 11 606 58 10 11 474be9f1fd4e dbee7f0dd760 000000000000
183 12 664 58 11 12 594a27502c85 474be9f1fd4e 000000000000
184 13 722 58 12 13 a7d25307d6a9 594a27502c85 000000000000
185 14 780 58 13 14 3eb53082272e a7d25307d6a9 000000000000
186 15 838 58 14 15 d1e94c85caf6 3eb53082272e 000000000000
187 16 896 58 15 16 8933d9629788 d1e94c85caf6 000000000000
188 17 954 58 16 17 a33416e52d91 8933d9629788 000000000000
189 18 1012 47 -1 18 4ccbf31021ed 000000000000 000000000000
190 19 1059 58 18 19 dcad7a25656c 4ccbf31021ed 000000000000
191 20 1117 58 19 20 617c4f8be75f dcad7a25656c 000000000000
192 21 1175 58 20 21 975b9c1d75bb 617c4f8be75f 000000000000
193 22 1233 58 21 22 74f09cd33b70 975b9c1d75bb 000000000000
194 23 1291 58 22 23 54e79bfa7ef1 74f09cd33b70 000000000000
195 24 1349 58 23 24 c556e7ff90af 54e79bfa7ef1 000000000000
196 25 1407 58 24 25 42daedfe9c6b c556e7ff90af 000000000000
197 26 1465 58 25 26 f302566947c7 42daedfe9c6b 000000000000
198 27 1523 58 26 27 2346959851cb f302566947c7 000000000000
199 28 1581 58 27 28 ca8d867106b4 2346959851cb 000000000000
200 29 1639 58 28 29 fd9152decab2 ca8d867106b4 000000000000
201 30 1697 58 29 30 3fe34080a79b fd9152decab2 000000000000
202 31 1755 58 30 31 bce61a95078e 3fe34080a79b 000000000000
203 32 1813 58 31 32 1dd9ba54ba15 bce61a95078e 000000000000
204 33 1871 58 32 33 3cd9b90a9972 1dd9ba54ba15 000000000000
205 34 1929 58 33 34 5db8c9754ef5 3cd9b90a9972 000000000000
206 35 1987 58 34 35 ee4a240cc16c 5db8c9754ef5 000000000000
207 36 2045 58 35 36 9e1d38725343 ee4a240cc16c 000000000000
208 37 2103 58 36 37 3463f73086a8 9e1d38725343 000000000000
209 38 2161 58 37 38 88af72fab449 3463f73086a8 000000000000
210 39 2219 58 38 39 472f5ce73785 88af72fab449 000000000000
211 40 2277 58 39 40 c91b8351e5b8 472f5ce73785 000000000000
212 41 2335 58 40 41 9c8289c5c5c0 c91b8351e5b8 000000000000
213 42 2393 58 41 42 a13fd4a09d76 9c8289c5c5c0 000000000000
214 43 2451 58 42 43 2ec2c81cafe0 a13fd4a09d76 000000000000
215 44 2509 58 43 44 f27fdd174392 2ec2c81cafe0 000000000000
216 45 2567 58 44 45 a539ec59fe41 f27fdd174392 000000000000
217 46 2625 58 45 46 5e98b9ecb738 a539ec59fe41 000000000000
218 47 2683 58 46 47 31e6b47899d0 5e98b9ecb738 000000000000
219 48 2741 58 47 48 2cf25d6636bd 31e6b47899d0 000000000000
220 49 2799 197 -1 49 9fff62ea0624 96e0c2ce55ed 000000000000
221 50 2996 58 49 50 467f8e30a066 9fff62ea0624 000000000000
222 51 3054 356 50 51 346db97283df a33416e52d91 000000000000
223 52 3410 58 51 52 4e003fd4d5cd 346db97283df 000000000000
224 $ hg clone --pull source-repo --config experimental.maxdeltachainspan=2800 relax-chain --config format.generaldelta=yes
225 requesting all changes
226 adding changesets
227 adding manifests
228 adding file changes
229 added 53 changesets with 53 changes to 53 files (+2 heads)
230 updating to branch default
231 14 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 $ hg -R relax-chain debugindex -m
233 rev offset length delta linkrev nodeid p1 p2
234 0 0 46 -1 0 19deeef41503 000000000000 000000000000
235 1 46 57 0 1 fffc37b38c40 19deeef41503 000000000000
236 2 103 57 1 2 5822d75c83d9 fffc37b38c40 000000000000
237 3 160 57 2 3 19cf2273e601 5822d75c83d9 000000000000
238 4 217 57 3 4 d45ead487afe 19cf2273e601 000000000000
239 5 274 57 4 5 96e0c2ce55ed d45ead487afe 000000000000
240 6 331 46 -1 6 0c2ea5222c74 000000000000 000000000000
241 7 377 57 6 7 4ca08a89134d 0c2ea5222c74 000000000000
242 8 434 57 7 8 c973dbfd30ac 4ca08a89134d 000000000000
243 9 491 57 8 9 d81d878ff2cd c973dbfd30ac 000000000000
244 10 548 58 9 10 dbee7f0dd760 d81d878ff2cd 000000000000
245 11 606 58 10 11 474be9f1fd4e dbee7f0dd760 000000000000
246 12 664 58 11 12 594a27502c85 474be9f1fd4e 000000000000
247 13 722 58 12 13 a7d25307d6a9 594a27502c85 000000000000
248 14 780 58 13 14 3eb53082272e a7d25307d6a9 000000000000
249 15 838 58 14 15 d1e94c85caf6 3eb53082272e 000000000000
250 16 896 58 15 16 8933d9629788 d1e94c85caf6 000000000000
251 17 954 58 16 17 a33416e52d91 8933d9629788 000000000000
252 18 1012 47 -1 18 4ccbf31021ed 000000000000 000000000000
253 19 1059 58 18 19 dcad7a25656c 4ccbf31021ed 000000000000
254 20 1117 58 19 20 617c4f8be75f dcad7a25656c 000000000000
255 21 1175 58 20 21 975b9c1d75bb 617c4f8be75f 000000000000
256 22 1233 58 21 22 74f09cd33b70 975b9c1d75bb 000000000000
257 23 1291 58 22 23 54e79bfa7ef1 74f09cd33b70 000000000000
258 24 1349 58 23 24 c556e7ff90af 54e79bfa7ef1 000000000000
259 25 1407 58 24 25 42daedfe9c6b c556e7ff90af 000000000000
260 26 1465 58 25 26 f302566947c7 42daedfe9c6b 000000000000
261 27 1523 58 26 27 2346959851cb f302566947c7 000000000000
262 28 1581 58 27 28 ca8d867106b4 2346959851cb 000000000000
263 29 1639 58 28 29 fd9152decab2 ca8d867106b4 000000000000
264 30 1697 58 29 30 3fe34080a79b fd9152decab2 000000000000
265 31 1755 58 30 31 bce61a95078e 3fe34080a79b 000000000000
266 32 1813 58 31 32 1dd9ba54ba15 bce61a95078e 000000000000
267 33 1871 58 32 33 3cd9b90a9972 1dd9ba54ba15 000000000000
268 34 1929 58 33 34 5db8c9754ef5 3cd9b90a9972 000000000000
269 35 1987 58 34 35 ee4a240cc16c 5db8c9754ef5 000000000000
270 36 2045 58 35 36 9e1d38725343 ee4a240cc16c 000000000000
271 37 2103 58 36 37 3463f73086a8 9e1d38725343 000000000000
272 38 2161 58 37 38 88af72fab449 3463f73086a8 000000000000
273 39 2219 58 38 39 472f5ce73785 88af72fab449 000000000000
274 40 2277 58 39 40 c91b8351e5b8 472f5ce73785 000000000000
275 41 2335 58 40 41 9c8289c5c5c0 c91b8351e5b8 000000000000
276 42 2393 58 41 42 a13fd4a09d76 9c8289c5c5c0 000000000000
277 43 2451 58 42 43 2ec2c81cafe0 a13fd4a09d76 000000000000
278 44 2509 58 43 44 f27fdd174392 2ec2c81cafe0 000000000000
279 45 2567 58 44 45 a539ec59fe41 f27fdd174392 000000000000
280 46 2625 58 45 46 5e98b9ecb738 a539ec59fe41 000000000000
281 47 2683 58 46 47 31e6b47899d0 5e98b9ecb738 000000000000
282 48 2741 58 47 48 2cf25d6636bd 31e6b47899d0 000000000000
283 49 2799 197 -1 49 9fff62ea0624 96e0c2ce55ed 000000000000
284 50 2996 58 49 50 467f8e30a066 9fff62ea0624 000000000000
285 51 3054 58 17 51 346db97283df a33416e52d91 000000000000
286 52 3112 369 -1 52 4e003fd4d5cd 346db97283df 000000000000
287 $ hg clone --pull source-repo --config experimental.maxdeltachainspan=0 noconst-chain --config format.generaldelta=yes
288 requesting all changes
289 adding changesets
290 adding manifests
291 adding file changes
292 added 53 changesets with 53 changes to 53 files (+2 heads)
293 updating to branch default
294 14 files updated, 0 files merged, 0 files removed, 0 files unresolved
295 $ hg -R noconst-chain debugindex -m
296 rev offset length delta linkrev nodeid p1 p2
297 0 0 46 -1 0 19deeef41503 000000000000 000000000000
298 1 46 57 0 1 fffc37b38c40 19deeef41503 000000000000
299 2 103 57 1 2 5822d75c83d9 fffc37b38c40 000000000000
300 3 160 57 2 3 19cf2273e601 5822d75c83d9 000000000000
301 4 217 57 3 4 d45ead487afe 19cf2273e601 000000000000
302 5 274 57 4 5 96e0c2ce55ed d45ead487afe 000000000000
303 6 331 46 -1 6 0c2ea5222c74 000000000000 000000000000
304 7 377 57 6 7 4ca08a89134d 0c2ea5222c74 000000000000
305 8 434 57 7 8 c973dbfd30ac 4ca08a89134d 000000000000
306 9 491 57 8 9 d81d878ff2cd c973dbfd30ac 000000000000
307 10 548 58 9 10 dbee7f0dd760 d81d878ff2cd 000000000000
308 11 606 58 10 11 474be9f1fd4e dbee7f0dd760 000000000000
309 12 664 58 11 12 594a27502c85 474be9f1fd4e 000000000000
310 13 722 58 12 13 a7d25307d6a9 594a27502c85 000000000000
311 14 780 58 13 14 3eb53082272e a7d25307d6a9 000000000000
312 15 838 58 14 15 d1e94c85caf6 3eb53082272e 000000000000
313 16 896 58 15 16 8933d9629788 d1e94c85caf6 000000000000
314 17 954 58 16 17 a33416e52d91 8933d9629788 000000000000
315 18 1012 47 -1 18 4ccbf31021ed 000000000000 000000000000
316 19 1059 58 18 19 dcad7a25656c 4ccbf31021ed 000000000000
317 20 1117 58 19 20 617c4f8be75f dcad7a25656c 000000000000
318 21 1175 58 20 21 975b9c1d75bb 617c4f8be75f 000000000000
319 22 1233 58 21 22 74f09cd33b70 975b9c1d75bb 000000000000
320 23 1291 58 22 23 54e79bfa7ef1 74f09cd33b70 000000000000
321 24 1349 58 23 24 c556e7ff90af 54e79bfa7ef1 000000000000
322 25 1407 58 24 25 42daedfe9c6b c556e7ff90af 000000000000
323 26 1465 58 25 26 f302566947c7 42daedfe9c6b 000000000000
324 27 1523 58 26 27 2346959851cb f302566947c7 000000000000
325 28 1581 58 27 28 ca8d867106b4 2346959851cb 000000000000
326 29 1639 58 28 29 fd9152decab2 ca8d867106b4 000000000000
327 30 1697 58 29 30 3fe34080a79b fd9152decab2 000000000000
328 31 1755 58 30 31 bce61a95078e 3fe34080a79b 000000000000
329 32 1813 58 31 32 1dd9ba54ba15 bce61a95078e 000000000000
330 33 1871 58 32 33 3cd9b90a9972 1dd9ba54ba15 000000000000
331 34 1929 58 33 34 5db8c9754ef5 3cd9b90a9972 000000000000
332 35 1987 58 34 35 ee4a240cc16c 5db8c9754ef5 000000000000
333 36 2045 58 35 36 9e1d38725343 ee4a240cc16c 000000000000
334 37 2103 58 36 37 3463f73086a8 9e1d38725343 000000000000
335 38 2161 58 37 38 88af72fab449 3463f73086a8 000000000000
336 39 2219 58 38 39 472f5ce73785 88af72fab449 000000000000
337 40 2277 58 39 40 c91b8351e5b8 472f5ce73785 000000000000
338 41 2335 58 40 41 9c8289c5c5c0 c91b8351e5b8 000000000000
339 42 2393 58 41 42 a13fd4a09d76 9c8289c5c5c0 000000000000
340 43 2451 58 42 43 2ec2c81cafe0 a13fd4a09d76 000000000000
341 44 2509 58 43 44 f27fdd174392 2ec2c81cafe0 000000000000
342 45 2567 58 44 45 a539ec59fe41 f27fdd174392 000000000000
343 46 2625 58 45 46 5e98b9ecb738 a539ec59fe41 000000000000
344 47 2683 58 46 47 31e6b47899d0 5e98b9ecb738 000000000000
345 48 2741 58 47 48 2cf25d6636bd 31e6b47899d0 000000000000
346 49 2799 58 5 49 9fff62ea0624 96e0c2ce55ed 000000000000
347 50 2857 58 49 50 467f8e30a066 9fff62ea0624 000000000000
348 51 2915 58 17 51 346db97283df a33416e52d91 000000000000
349 52 2973 58 51 52 4e003fd4d5cd 346db97283df 000000000000
General Comments 0
You need to be logged in to leave comments. Login now