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