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