##// END OF EJS Templates
Fix bundle repos to use an index tuple consistent with revlogng...
mason@suse.com -
r2074:01ee43dd default
parent child Browse files
Show More
@@ -1,201 +1,203 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 def chunkpositer():
35 def chunkpositer():
35 for chunk in changegroup.chunkiter(bundlefile):
36 for chunk in changegroup.chunkiter(bundlefile):
36 pos = bundlefile.tell()
37 pos = bundlefile.tell()
37 yield chunk, pos - len(chunk)
38 yield chunk, pos - len(chunk)
38 n = self.count()
39 n = self.count()
39 prev = None
40 prev = None
40 for chunk, start in chunkpositer():
41 for chunk, start in chunkpositer():
41 size = len(chunk)
42 size = len(chunk)
42 if size < 80:
43 if size < 80:
43 raise util.Abort("invalid changegroup")
44 raise util.Abort("invalid changegroup")
44 start += 80
45 start += 80
45 size -= 80
46 size -= 80
46 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
47 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
47 if node in self.nodemap:
48 if node in self.nodemap:
48 prev = node
49 prev = node
49 continue
50 continue
50 for p in (p1, p2):
51 for p in (p1, p2):
51 if not p in self.nodemap:
52 if not p in self.nodemap:
52 raise RevlogError(_("unknown parent %s") % short(p1))
53 raise RevlogError(_("unknown parent %s") % short(p1))
53 if linkmapper is None:
54 if linkmapper is None:
54 link = n
55 link = n
55 else:
56 else:
56 link = linkmapper(cs)
57 link = linkmapper(cs)
57
58
58 if not prev:
59 if not prev:
59 prev = p1
60 prev = p1
60 # start, size, base is not used, link, p1, p2, delta ref
61 # start, size, base is not used, link, p1, p2, delta ref
61 e = (start, size, None, link, p1, p2, node, prev)
62 e = (start, size, None, link, p1, p2, node)
63 self.basemap[n] = prev
62 self.index.append(e)
64 self.index.append(e)
63 self.nodemap[node] = n
65 self.nodemap[node] = n
64 prev = node
66 prev = node
65 n += 1
67 n += 1
66
68
67 def bundle(self, rev):
69 def bundle(self, rev):
68 """is rev from the bundle"""
70 """is rev from the bundle"""
69 if rev < 0:
71 if rev < 0:
70 return False
72 return False
71 return len(self.index[rev]) > 7
73 return rev in self.basemap
72 def bundlebase(self, rev): return self.index[rev][7]
74 def bundlebase(self, rev): return self.basemap[rev]
73 def chunk(self, rev):
75 def chunk(self, rev, df=None):
74 # Warning: in case of bundle, the diff is against bundlebase,
76 # Warning: in case of bundle, the diff is against bundlebase,
75 # not against rev - 1
77 # not against rev - 1
76 # XXX: could use some caching
78 # XXX: could use some caching
77 if not self.bundle(rev):
79 if not self.bundle(rev):
78 return revlog.revlog.chunk(self, rev)
80 return revlog.revlog.chunk(self, rev)
79 self.bundlefile.seek(self.start(rev))
81 self.bundlefile.seek(self.start(rev))
80 return self.bundlefile.read(self.length(rev))
82 return self.bundlefile.read(self.length(rev))
81
83
82 def revdiff(self, rev1, rev2):
84 def revdiff(self, rev1, rev2):
83 """return or calculate a delta between two revisions"""
85 """return or calculate a delta between two revisions"""
84 if self.bundle(rev1) and self.bundle(rev2):
86 if self.bundle(rev1) and self.bundle(rev2):
85 # hot path for bundle
87 # hot path for bundle
86 revb = self.rev(self.bundlebase(rev2))
88 revb = self.rev(self.bundlebase(rev2))
87 if revb == rev1:
89 if revb == rev1:
88 return self.chunk(rev2)
90 return self.chunk(rev2)
89 elif not self.bundle(rev1) and not self.bundle(rev2):
91 elif not self.bundle(rev1) and not self.bundle(rev2):
90 return revlog.revlog.chunk(self, rev1, rev2)
92 return revlog.revlog.chunk(self, rev1, rev2)
91
93
92 return self.diff(self.revision(self.node(rev1)),
94 return self.diff(self.revision(self.node(rev1)),
93 self.revision(self.node(rev2)))
95 self.revision(self.node(rev2)))
94
96
95 def revision(self, node):
97 def revision(self, node):
96 """return an uncompressed revision of a given"""
98 """return an uncompressed revision of a given"""
97 if node == nullid: return ""
99 if node == nullid: return ""
98
100
99 text = None
101 text = None
100 chain = []
102 chain = []
101 iter_node = node
103 iter_node = node
102 rev = self.rev(iter_node)
104 rev = self.rev(iter_node)
103 # reconstruct the revision if it is from a changegroup
105 # reconstruct the revision if it is from a changegroup
104 while self.bundle(rev):
106 while self.bundle(rev):
105 if self.cache and self.cache[0] == iter_node:
107 if self.cache and self.cache[0] == iter_node:
106 text = self.cache[2]
108 text = self.cache[2]
107 break
109 break
108 chain.append(rev)
110 chain.append(rev)
109 iter_node = self.bundlebase(rev)
111 iter_node = self.bundlebase(rev)
110 rev = self.rev(iter_node)
112 rev = self.rev(iter_node)
111 if text is None:
113 if text is None:
112 text = revlog.revlog.revision(self, iter_node)
114 text = revlog.revlog.revision(self, iter_node)
113
115
114 while chain:
116 while chain:
115 delta = self.chunk(chain.pop())
117 delta = self.chunk(chain.pop())
116 text = self.patches(text, [delta])
118 text = self.patches(text, [delta])
117
119
118 p1, p2 = self.parents(node)
120 p1, p2 = self.parents(node)
119 if node != revlog.hash(text, p1, p2):
121 if node != revlog.hash(text, p1, p2):
120 raise RevlogError(_("integrity check failed on %s:%d")
122 raise RevlogError(_("integrity check failed on %s:%d")
121 % (self.datafile, self.rev(node)))
123 % (self.datafile, self.rev(node)))
122
124
123 self.cache = (node, rev, text)
125 self.cache = (node, rev, text)
124 return text
126 return text
125
127
126 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
128 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
127 raise NotImplementedError
129 raise NotImplementedError
128 def addgroup(self, revs, linkmapper, transaction, unique=0):
130 def addgroup(self, revs, linkmapper, transaction, unique=0):
129 raise NotImplementedError
131 raise NotImplementedError
130 def strip(self, rev, minlink):
132 def strip(self, rev, minlink):
131 raise NotImplementedError
133 raise NotImplementedError
132 def checksize(self):
134 def checksize(self):
133 raise NotImplementedError
135 raise NotImplementedError
134
136
135 class bundlechangelog(bundlerevlog, changelog.changelog):
137 class bundlechangelog(bundlerevlog, changelog.changelog):
136 def __init__(self, opener, bundlefile):
138 def __init__(self, opener, bundlefile):
137 changelog.changelog.__init__(self, opener)
139 changelog.changelog.__init__(self, opener)
138 bundlerevlog.__init__(self, opener, "00changelog.i", "00changelog.d",
140 bundlerevlog.__init__(self, opener, "00changelog.i", "00changelog.d",
139 bundlefile)
141 bundlefile)
140
142
141 class bundlemanifest(bundlerevlog, manifest.manifest):
143 class bundlemanifest(bundlerevlog, manifest.manifest):
142 def __init__(self, opener, bundlefile, linkmapper):
144 def __init__(self, opener, bundlefile, linkmapper):
143 manifest.manifest.__init__(self, opener)
145 manifest.manifest.__init__(self, opener)
144 bundlerevlog.__init__(self, opener, self.indexfile, self.datafile,
146 bundlerevlog.__init__(self, opener, self.indexfile, self.datafile,
145 bundlefile, linkmapper)
147 bundlefile, linkmapper)
146
148
147 class bundlefilelog(bundlerevlog, filelog.filelog):
149 class bundlefilelog(bundlerevlog, filelog.filelog):
148 def __init__(self, opener, path, bundlefile, linkmapper):
150 def __init__(self, opener, path, bundlefile, linkmapper):
149 filelog.filelog.__init__(self, opener, path)
151 filelog.filelog.__init__(self, opener, path)
150 bundlerevlog.__init__(self, opener, self.indexfile, self.datafile,
152 bundlerevlog.__init__(self, opener, self.indexfile, self.datafile,
151 bundlefile, linkmapper)
153 bundlefile, linkmapper)
152
154
153 class bundlerepository(localrepo.localrepository):
155 class bundlerepository(localrepo.localrepository):
154 def __init__(self, ui, path, bundlename):
156 def __init__(self, ui, path, bundlename):
155 localrepo.localrepository.__init__(self, ui, path)
157 localrepo.localrepository.__init__(self, ui, path)
156 f = open(bundlename, "rb")
158 f = open(bundlename, "rb")
157 s = os.fstat(f.fileno())
159 s = os.fstat(f.fileno())
158 self.bundlefile = f
160 self.bundlefile = f
159 header = self.bundlefile.read(6)
161 header = self.bundlefile.read(6)
160 if not header.startswith("HG"):
162 if not header.startswith("HG"):
161 raise util.Abort(_("%s: not a Mercurial bundle file") % bundlename)
163 raise util.Abort(_("%s: not a Mercurial bundle file") % bundlename)
162 elif not header.startswith("HG10"):
164 elif not header.startswith("HG10"):
163 raise util.Abort(_("%s: unknown bundle version") % bundlename)
165 raise util.Abort(_("%s: unknown bundle version") % bundlename)
164 elif header == "HG10BZ":
166 elif header == "HG10BZ":
165 raise util.Abort(_("%s: compressed bundle not supported")
167 raise util.Abort(_("%s: compressed bundle not supported")
166 % bundlename)
168 % bundlename)
167 elif header == "HG10UN":
169 elif header == "HG10UN":
168 # uncompressed bundle supported
170 # uncompressed bundle supported
169 pass
171 pass
170 else:
172 else:
171 raise util.Abort(_("%s: unknown bundle compression type")
173 raise util.Abort(_("%s: unknown bundle compression type")
172 % bundlename)
174 % bundlename)
173 self.changelog = bundlechangelog(self.opener, self.bundlefile)
175 self.changelog = bundlechangelog(self.opener, self.bundlefile)
174 self.manifest = bundlemanifest(self.opener, self.bundlefile,
176 self.manifest = bundlemanifest(self.opener, self.bundlefile,
175 self.changelog.rev)
177 self.changelog.rev)
176 # dict with the mapping 'filename' -> position in the bundle
178 # dict with the mapping 'filename' -> position in the bundle
177 self.bundlefilespos = {}
179 self.bundlefilespos = {}
178 while 1:
180 while 1:
179 f = changegroup.getchunk(self.bundlefile)
181 f = changegroup.getchunk(self.bundlefile)
180 if not f:
182 if not f:
181 break
183 break
182 self.bundlefilespos[f] = self.bundlefile.tell()
184 self.bundlefilespos[f] = self.bundlefile.tell()
183 for c in changegroup.chunkiter(self.bundlefile):
185 for c in changegroup.chunkiter(self.bundlefile):
184 pass
186 pass
185
187
186 def dev(self):
188 def dev(self):
187 return -1
189 return -1
188
190
189 def file(self, f):
191 def file(self, f):
190 if f[0] == '/':
192 if f[0] == '/':
191 f = f[1:]
193 f = f[1:]
192 if f in self.bundlefilespos:
194 if f in self.bundlefilespos:
193 self.bundlefile.seek(self.bundlefilespos[f])
195 self.bundlefile.seek(self.bundlefilespos[f])
194 return bundlefilelog(self.opener, f, self.bundlefile,
196 return bundlefilelog(self.opener, f, self.bundlefile,
195 self.changelog.rev)
197 self.changelog.rev)
196 else:
198 else:
197 return filelog.filelog(self.opener, f)
199 return filelog.filelog(self.opener, f)
198
200
199 def close(self):
201 def close(self):
200 """Close assigned bundle file immediately."""
202 """Close assigned bundle file immediately."""
201 self.bundlefile.close()
203 self.bundlefile.close()
General Comments 0
You need to be logged in to leave comments. Login now