##// END OF EJS Templates
Make bundlerepo use proper index format for revlogng...
Chris Mason -
r2101:c6c019fd default
parent child Browse files
Show More
@@ -1,203 +1,207 b''
1 """
1 """
2 bundlerepo.py - repository class for viewing uncompressed bundles
2 bundlerepo.py - repository class for viewing uncompressed bundles
3
3
4 This provides a read-only repository interface to bundles as if
4 This provides a read-only repository interface to bundles as if
5 they were part of the actual repository.
5 they were part of the actual repository.
6
6
7 Copyright 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
7 Copyright 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
8
8
9 This software may be used and distributed according to the terms
9 This software may be used and distributed according to the terms
10 of the GNU General Public License, incorporated herein by reference.
10 of the GNU General Public License, incorporated herein by reference.
11 """
11 """
12
12
13 from node import *
13 from node import *
14 from i18n import gettext as _
14 from i18n import gettext as _
15 from demandload import demandload
15 from demandload import demandload
16 demandload(globals(), "changegroup util os struct")
16 demandload(globals(), "changegroup util os struct")
17
17
18 import localrepo, changelog, manifest, filelog, revlog
18 import localrepo, changelog, manifest, filelog, revlog
19
19
20 class bundlerevlog(revlog.revlog):
20 class bundlerevlog(revlog.revlog):
21 def __init__(self, opener, indexfile, datafile, bundlefile,
21 def __init__(self, opener, indexfile, datafile, bundlefile,
22 linkmapper=None):
22 linkmapper=None):
23 # How it works:
23 # How it works:
24 # to retrieve a revision, we need to know the offset of
24 # to retrieve a revision, we need to know the offset of
25 # the revision in the bundlefile (an opened file).
25 # the revision in the bundlefile (an opened file).
26 #
26 #
27 # We store this offset in the index (start), to differentiate a
27 # We store this offset in the index (start), to differentiate a
28 # rev in the bundle and from a rev in the revlog, we check
28 # rev in the bundle and from a rev in the revlog, we check
29 # len(index[r]). If the tuple is bigger than 7, it is a bundle
29 # len(index[r]). If the tuple is bigger than 7, it is a bundle
30 # (it is bigger since we store the node to which the delta is)
30 # (it is bigger since we store the node to which the delta is)
31 #
31 #
32 revlog.revlog.__init__(self, opener, indexfile, datafile)
32 revlog.revlog.__init__(self, opener, indexfile, datafile)
33 self.bundlefile = bundlefile
33 self.bundlefile = bundlefile
34 self.basemap = {}
34 self.basemap = {}
35 def chunkpositer():
35 def chunkpositer():
36 for chunk in changegroup.chunkiter(bundlefile):
36 for chunk in changegroup.chunkiter(bundlefile):
37 pos = bundlefile.tell()
37 pos = bundlefile.tell()
38 yield chunk, pos - len(chunk)
38 yield chunk, pos - len(chunk)
39 n = self.count()
39 n = self.count()
40 prev = None
40 prev = None
41 for chunk, start in chunkpositer():
41 for chunk, start in chunkpositer():
42 size = len(chunk)
42 size = len(chunk)
43 if size < 80:
43 if size < 80:
44 raise util.Abort("invalid changegroup")
44 raise util.Abort("invalid changegroup")
45 start += 80
45 start += 80
46 size -= 80
46 size -= 80
47 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
47 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
48 if node in self.nodemap:
48 if node in self.nodemap:
49 prev = node
49 prev = node
50 continue
50 continue
51 for p in (p1, p2):
51 for p in (p1, p2):
52 if not p in self.nodemap:
52 if not p in self.nodemap:
53 raise RevlogError(_("unknown parent %s") % short(p1))
53 raise RevlogError(_("unknown parent %s") % short(p1))
54 if linkmapper is None:
54 if linkmapper is None:
55 link = n
55 link = n
56 else:
56 else:
57 link = linkmapper(cs)
57 link = linkmapper(cs)
58
58
59 if not prev:
59 if not prev:
60 prev = p1
60 prev = p1
61 # start, size, base is not used, link, p1, p2, delta ref
61 # start, size, base is not used, link, p1, p2, delta ref
62 e = (start, size, None, link, p1, p2, node)
62 if self.version == 0:
63 e = (start, size, None, link, p1, p2, node)
64 else:
65 e = (self.offset_type(start, 0), size, -1, None, link,
66 self.rev(p1), self.rev(p2), node)
63 self.basemap[n] = prev
67 self.basemap[n] = prev
64 self.index.append(e)
68 self.index.append(e)
65 self.nodemap[node] = n
69 self.nodemap[node] = n
66 prev = node
70 prev = node
67 n += 1
71 n += 1
68
72
69 def bundle(self, rev):
73 def bundle(self, rev):
70 """is rev from the bundle"""
74 """is rev from the bundle"""
71 if rev < 0:
75 if rev < 0:
72 return False
76 return False
73 return rev in self.basemap
77 return rev in self.basemap
74 def bundlebase(self, rev): return self.basemap[rev]
78 def bundlebase(self, rev): return self.basemap[rev]
75 def chunk(self, rev, df=None):
79 def chunk(self, rev, df=None):
76 # Warning: in case of bundle, the diff is against bundlebase,
80 # Warning: in case of bundle, the diff is against bundlebase,
77 # not against rev - 1
81 # not against rev - 1
78 # XXX: could use some caching
82 # XXX: could use some caching
79 if not self.bundle(rev):
83 if not self.bundle(rev):
80 return revlog.revlog.chunk(self, rev)
84 return revlog.revlog.chunk(self, rev)
81 self.bundlefile.seek(self.start(rev))
85 self.bundlefile.seek(self.start(rev))
82 return self.bundlefile.read(self.length(rev))
86 return self.bundlefile.read(self.length(rev))
83
87
84 def revdiff(self, rev1, rev2):
88 def revdiff(self, rev1, rev2):
85 """return or calculate a delta between two revisions"""
89 """return or calculate a delta between two revisions"""
86 if self.bundle(rev1) and self.bundle(rev2):
90 if self.bundle(rev1) and self.bundle(rev2):
87 # hot path for bundle
91 # hot path for bundle
88 revb = self.rev(self.bundlebase(rev2))
92 revb = self.rev(self.bundlebase(rev2))
89 if revb == rev1:
93 if revb == rev1:
90 return self.chunk(rev2)
94 return self.chunk(rev2)
91 elif not self.bundle(rev1) and not self.bundle(rev2):
95 elif not self.bundle(rev1) and not self.bundle(rev2):
92 return revlog.revlog.chunk(self, rev1, rev2)
96 return revlog.revlog.chunk(self, rev1, rev2)
93
97
94 return self.diff(self.revision(self.node(rev1)),
98 return self.diff(self.revision(self.node(rev1)),
95 self.revision(self.node(rev2)))
99 self.revision(self.node(rev2)))
96
100
97 def revision(self, node):
101 def revision(self, node):
98 """return an uncompressed revision of a given"""
102 """return an uncompressed revision of a given"""
99 if node == nullid: return ""
103 if node == nullid: return ""
100
104
101 text = None
105 text = None
102 chain = []
106 chain = []
103 iter_node = node
107 iter_node = node
104 rev = self.rev(iter_node)
108 rev = self.rev(iter_node)
105 # reconstruct the revision if it is from a changegroup
109 # reconstruct the revision if it is from a changegroup
106 while self.bundle(rev):
110 while self.bundle(rev):
107 if self.cache and self.cache[0] == iter_node:
111 if self.cache and self.cache[0] == iter_node:
108 text = self.cache[2]
112 text = self.cache[2]
109 break
113 break
110 chain.append(rev)
114 chain.append(rev)
111 iter_node = self.bundlebase(rev)
115 iter_node = self.bundlebase(rev)
112 rev = self.rev(iter_node)
116 rev = self.rev(iter_node)
113 if text is None:
117 if text is None:
114 text = revlog.revlog.revision(self, iter_node)
118 text = revlog.revlog.revision(self, iter_node)
115
119
116 while chain:
120 while chain:
117 delta = self.chunk(chain.pop())
121 delta = self.chunk(chain.pop())
118 text = self.patches(text, [delta])
122 text = self.patches(text, [delta])
119
123
120 p1, p2 = self.parents(node)
124 p1, p2 = self.parents(node)
121 if node != revlog.hash(text, p1, p2):
125 if node != revlog.hash(text, p1, p2):
122 raise RevlogError(_("integrity check failed on %s:%d")
126 raise RevlogError(_("integrity check failed on %s:%d")
123 % (self.datafile, self.rev(node)))
127 % (self.datafile, self.rev(node)))
124
128
125 self.cache = (node, rev, text)
129 self.cache = (node, rev, text)
126 return text
130 return text
127
131
128 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
132 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
129 raise NotImplementedError
133 raise NotImplementedError
130 def addgroup(self, revs, linkmapper, transaction, unique=0):
134 def addgroup(self, revs, linkmapper, transaction, unique=0):
131 raise NotImplementedError
135 raise NotImplementedError
132 def strip(self, rev, minlink):
136 def strip(self, rev, minlink):
133 raise NotImplementedError
137 raise NotImplementedError
134 def checksize(self):
138 def checksize(self):
135 raise NotImplementedError
139 raise NotImplementedError
136
140
137 class bundlechangelog(bundlerevlog, changelog.changelog):
141 class bundlechangelog(bundlerevlog, changelog.changelog):
138 def __init__(self, opener, bundlefile):
142 def __init__(self, opener, bundlefile):
139 changelog.changelog.__init__(self, opener)
143 changelog.changelog.__init__(self, opener)
140 bundlerevlog.__init__(self, opener, "00changelog.i", "00changelog.d",
144 bundlerevlog.__init__(self, opener, "00changelog.i", "00changelog.d",
141 bundlefile)
145 bundlefile)
142
146
143 class bundlemanifest(bundlerevlog, manifest.manifest):
147 class bundlemanifest(bundlerevlog, manifest.manifest):
144 def __init__(self, opener, bundlefile, linkmapper):
148 def __init__(self, opener, bundlefile, linkmapper):
145 manifest.manifest.__init__(self, opener)
149 manifest.manifest.__init__(self, opener)
146 bundlerevlog.__init__(self, opener, self.indexfile, self.datafile,
150 bundlerevlog.__init__(self, opener, self.indexfile, self.datafile,
147 bundlefile, linkmapper)
151 bundlefile, linkmapper)
148
152
149 class bundlefilelog(bundlerevlog, filelog.filelog):
153 class bundlefilelog(bundlerevlog, filelog.filelog):
150 def __init__(self, opener, path, bundlefile, linkmapper):
154 def __init__(self, opener, path, bundlefile, linkmapper):
151 filelog.filelog.__init__(self, opener, path)
155 filelog.filelog.__init__(self, opener, path)
152 bundlerevlog.__init__(self, opener, self.indexfile, self.datafile,
156 bundlerevlog.__init__(self, opener, self.indexfile, self.datafile,
153 bundlefile, linkmapper)
157 bundlefile, linkmapper)
154
158
155 class bundlerepository(localrepo.localrepository):
159 class bundlerepository(localrepo.localrepository):
156 def __init__(self, ui, path, bundlename):
160 def __init__(self, ui, path, bundlename):
157 localrepo.localrepository.__init__(self, ui, path)
161 localrepo.localrepository.__init__(self, ui, path)
158 f = open(bundlename, "rb")
162 f = open(bundlename, "rb")
159 s = os.fstat(f.fileno())
163 s = os.fstat(f.fileno())
160 self.bundlefile = f
164 self.bundlefile = f
161 header = self.bundlefile.read(6)
165 header = self.bundlefile.read(6)
162 if not header.startswith("HG"):
166 if not header.startswith("HG"):
163 raise util.Abort(_("%s: not a Mercurial bundle file") % bundlename)
167 raise util.Abort(_("%s: not a Mercurial bundle file") % bundlename)
164 elif not header.startswith("HG10"):
168 elif not header.startswith("HG10"):
165 raise util.Abort(_("%s: unknown bundle version") % bundlename)
169 raise util.Abort(_("%s: unknown bundle version") % bundlename)
166 elif header == "HG10BZ":
170 elif header == "HG10BZ":
167 raise util.Abort(_("%s: compressed bundle not supported")
171 raise util.Abort(_("%s: compressed bundle not supported")
168 % bundlename)
172 % bundlename)
169 elif header == "HG10UN":
173 elif header == "HG10UN":
170 # uncompressed bundle supported
174 # uncompressed bundle supported
171 pass
175 pass
172 else:
176 else:
173 raise util.Abort(_("%s: unknown bundle compression type")
177 raise util.Abort(_("%s: unknown bundle compression type")
174 % bundlename)
178 % bundlename)
175 self.changelog = bundlechangelog(self.opener, self.bundlefile)
179 self.changelog = bundlechangelog(self.opener, self.bundlefile)
176 self.manifest = bundlemanifest(self.opener, self.bundlefile,
180 self.manifest = bundlemanifest(self.opener, self.bundlefile,
177 self.changelog.rev)
181 self.changelog.rev)
178 # dict with the mapping 'filename' -> position in the bundle
182 # dict with the mapping 'filename' -> position in the bundle
179 self.bundlefilespos = {}
183 self.bundlefilespos = {}
180 while 1:
184 while 1:
181 f = changegroup.getchunk(self.bundlefile)
185 f = changegroup.getchunk(self.bundlefile)
182 if not f:
186 if not f:
183 break
187 break
184 self.bundlefilespos[f] = self.bundlefile.tell()
188 self.bundlefilespos[f] = self.bundlefile.tell()
185 for c in changegroup.chunkiter(self.bundlefile):
189 for c in changegroup.chunkiter(self.bundlefile):
186 pass
190 pass
187
191
188 def dev(self):
192 def dev(self):
189 return -1
193 return -1
190
194
191 def file(self, f):
195 def file(self, f):
192 if f[0] == '/':
196 if f[0] == '/':
193 f = f[1:]
197 f = f[1:]
194 if f in self.bundlefilespos:
198 if f in self.bundlefilespos:
195 self.bundlefile.seek(self.bundlefilespos[f])
199 self.bundlefile.seek(self.bundlefilespos[f])
196 return bundlefilelog(self.opener, f, self.bundlefile,
200 return bundlefilelog(self.opener, f, self.bundlefile,
197 self.changelog.rev)
201 self.changelog.rev)
198 else:
202 else:
199 return filelog.filelog(self.opener, f)
203 return filelog.filelog(self.opener, f)
200
204
201 def close(self):
205 def close(self):
202 """Close assigned bundle file immediately."""
206 """Close assigned bundle file immediately."""
203 self.bundlefile.close()
207 self.bundlefile.close()
General Comments 0
You need to be logged in to leave comments. Login now