##// END OF EJS Templates
bundlerepo: avoid exception in __del__ when the bundle doesn't exist...
Alexis S. L. Carvalho -
r3429:b19360aa default
parent child Browse files
Show More
@@ -1,254 +1,256
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 bz2 tempfile")
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 revlog.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 62 if self.version == 0:
63 63 e = (start, size, None, link, p1, p2, node)
64 64 else:
65 65 e = (self.offset_type(start, 0), size, -1, None, link,
66 66 self.rev(p1), self.rev(p2), node)
67 67 self.basemap[n] = prev
68 68 self.index.append(e)
69 69 self.nodemap[node] = n
70 70 prev = node
71 71 n += 1
72 72
73 73 def bundle(self, rev):
74 74 """is rev from the bundle"""
75 75 if rev < 0:
76 76 return False
77 77 return rev in self.basemap
78 78 def bundlebase(self, rev): return self.basemap[rev]
79 79 def chunk(self, rev, df=None, cachelen=4096):
80 80 # Warning: in case of bundle, the diff is against bundlebase,
81 81 # not against rev - 1
82 82 # XXX: could use some caching
83 83 if not self.bundle(rev):
84 84 return revlog.revlog.chunk(self, rev, df, cachelen)
85 85 self.bundlefile.seek(self.start(rev))
86 86 return self.bundlefile.read(self.length(rev))
87 87
88 88 def revdiff(self, rev1, rev2):
89 89 """return or calculate a delta between two revisions"""
90 90 if self.bundle(rev1) and self.bundle(rev2):
91 91 # hot path for bundle
92 92 revb = self.rev(self.bundlebase(rev2))
93 93 if revb == rev1:
94 94 return self.chunk(rev2)
95 95 elif not self.bundle(rev1) and not self.bundle(rev2):
96 96 return revlog.revlog.chunk(self, rev1, rev2)
97 97
98 98 return self.diff(self.revision(self.node(rev1)),
99 99 self.revision(self.node(rev2)))
100 100
101 101 def revision(self, node):
102 102 """return an uncompressed revision of a given"""
103 103 if node == nullid: return ""
104 104
105 105 text = None
106 106 chain = []
107 107 iter_node = node
108 108 rev = self.rev(iter_node)
109 109 # reconstruct the revision if it is from a changegroup
110 110 while self.bundle(rev):
111 111 if self.cache and self.cache[0] == iter_node:
112 112 text = self.cache[2]
113 113 break
114 114 chain.append(rev)
115 115 iter_node = self.bundlebase(rev)
116 116 rev = self.rev(iter_node)
117 117 if text is None:
118 118 text = revlog.revlog.revision(self, iter_node)
119 119
120 120 while chain:
121 121 delta = self.chunk(chain.pop())
122 122 text = self.patches(text, [delta])
123 123
124 124 p1, p2 = self.parents(node)
125 125 if node != revlog.hash(text, p1, p2):
126 126 raise revlog.RevlogError(_("integrity check failed on %s:%d")
127 127 % (self.datafile, self.rev(node)))
128 128
129 129 self.cache = (node, self.rev(node), text)
130 130 return text
131 131
132 132 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
133 133 raise NotImplementedError
134 134 def addgroup(self, revs, linkmapper, transaction, unique=0):
135 135 raise NotImplementedError
136 136 def strip(self, rev, minlink):
137 137 raise NotImplementedError
138 138 def checksize(self):
139 139 raise NotImplementedError
140 140
141 141 class bundlechangelog(bundlerevlog, changelog.changelog):
142 142 def __init__(self, opener, bundlefile):
143 143 changelog.changelog.__init__(self, opener)
144 144 bundlerevlog.__init__(self, opener, "00changelog.i", "00changelog.d",
145 145 bundlefile)
146 146
147 147 class bundlemanifest(bundlerevlog, manifest.manifest):
148 148 def __init__(self, opener, bundlefile, linkmapper):
149 149 manifest.manifest.__init__(self, opener)
150 150 bundlerevlog.__init__(self, opener, self.indexfile, self.datafile,
151 151 bundlefile, linkmapper)
152 152
153 153 class bundlefilelog(bundlerevlog, filelog.filelog):
154 154 def __init__(self, opener, path, bundlefile, linkmapper):
155 155 filelog.filelog.__init__(self, opener, path)
156 156 bundlerevlog.__init__(self, opener, self.indexfile, self.datafile,
157 157 bundlefile, linkmapper)
158 158
159 159 class bundlerepository(localrepo.localrepository):
160 160 def __init__(self, ui, path, bundlename):
161 161 localrepo.localrepository.__init__(self, ui, path)
162 162
163 163 self._url = 'bundle:' + bundlename
164 164 if path: self._url += '+' + path
165 165
166 166 self.tempfile = None
167 167 self.bundlefile = open(bundlename, "rb")
168 168 header = self.bundlefile.read(6)
169 169 if not header.startswith("HG"):
170 170 raise util.Abort(_("%s: not a Mercurial bundle file") % bundlename)
171 171 elif not header.startswith("HG10"):
172 172 raise util.Abort(_("%s: unknown bundle version") % bundlename)
173 173 elif header == "HG10BZ":
174 174 fdtemp, temp = tempfile.mkstemp(prefix="hg-bundle-",
175 175 suffix=".hg10un", dir=self.path)
176 176 self.tempfile = temp
177 177 fptemp = os.fdopen(fdtemp, 'wb')
178 178 def generator(f):
179 179 zd = bz2.BZ2Decompressor()
180 180 zd.decompress("BZ")
181 181 for chunk in f:
182 182 yield zd.decompress(chunk)
183 183 gen = generator(util.filechunkiter(self.bundlefile, 4096))
184 184
185 185 try:
186 186 fptemp.write("HG10UN")
187 187 for chunk in gen:
188 188 fptemp.write(chunk)
189 189 finally:
190 190 fptemp.close()
191 191 self.bundlefile.close()
192 192
193 193 self.bundlefile = open(self.tempfile, "rb")
194 194 # seek right after the header
195 195 self.bundlefile.seek(6)
196 196 elif header == "HG10UN":
197 197 # nothing to do
198 198 pass
199 199 else:
200 200 raise util.Abort(_("%s: unknown bundle compression type")
201 201 % bundlename)
202 202 self.changelog = bundlechangelog(self.opener, self.bundlefile)
203 203 self.manifest = bundlemanifest(self.opener, self.bundlefile,
204 204 self.changelog.rev)
205 205 # dict with the mapping 'filename' -> position in the bundle
206 206 self.bundlefilespos = {}
207 207 while 1:
208 208 f = changegroup.getchunk(self.bundlefile)
209 209 if not f:
210 210 break
211 211 self.bundlefilespos[f] = self.bundlefile.tell()
212 212 for c in changegroup.chunkiter(self.bundlefile):
213 213 pass
214 214
215 215 def url(self):
216 216 return self._url
217 217
218 218 def dev(self):
219 219 return -1
220 220
221 221 def file(self, f):
222 222 if f[0] == '/':
223 223 f = f[1:]
224 224 if f in self.bundlefilespos:
225 225 self.bundlefile.seek(self.bundlefilespos[f])
226 226 return bundlefilelog(self.opener, f, self.bundlefile,
227 227 self.changelog.rev)
228 228 else:
229 229 return filelog.filelog(self.opener, f)
230 230
231 231 def close(self):
232 232 """Close assigned bundle file immediately."""
233 233 self.bundlefile.close()
234 234
235 235 def __del__(self):
236 if not self.bundlefile.closed:
237 self.bundlefile.close()
238 if self.tempfile is not None:
239 os.unlink(self.tempfile)
236 bundlefile = getattr(self, 'bundlefile', None)
237 if bundlefile and not bundlefile.closed:
238 bundlefile.close()
239 tempfile = getattr(self, 'tempfile', None)
240 if tempfile is not None:
241 os.unlink(tempfile)
240 242
241 243 def instance(ui, path, create):
242 244 if create:
243 245 raise util.Abort(_('cannot create new bundle repository'))
244 246 path = util.drop_scheme('file', path)
245 247 if path.startswith('bundle:'):
246 248 path = util.drop_scheme('bundle', path)
247 249 s = path.split("+", 1)
248 250 if len(s) == 1:
249 251 repopath, bundlename = "", s[0]
250 252 else:
251 253 repopath, bundlename = s
252 254 else:
253 255 repopath, bundlename = '', path
254 256 return bundlerepository(ui, repopath, bundlename)
@@ -1,60 +1,61
1 1 #!/bin/sh
2 2
3 3 hg init test
4 4 cd test
5 5 echo 0 > afile
6 6 hg add afile
7 7 hg commit -m "0.0" -d "1000000 0"
8 8 echo 1 >> afile
9 9 hg commit -m "0.1" -d "1000000 0"
10 10 echo 2 >> afile
11 11 hg commit -m "0.2" -d "1000000 0"
12 12 echo 3 >> afile
13 13 hg commit -m "0.3" -d "1000000 0"
14 14 hg update -C 0
15 15 echo 1 >> afile
16 16 hg commit -m "1.1" -d "1000000 0"
17 17 echo 2 >> afile
18 18 hg commit -m "1.2" -d "1000000 0"
19 19 echo "a line" > fred
20 20 echo 3 >> afile
21 21 hg add fred
22 22 hg commit -m "1.3" -d "1000000 0"
23 23 hg mv afile adifferentfile
24 24 hg commit -m "1.3m" -d "1000000 0"
25 25 hg update -C 3
26 26 hg mv afile anotherfile
27 27 hg commit -m "0.3m" -d "1000000 0"
28 28 hg verify
29 29 cd ..
30 30 hg init empty
31 31 hg -R test bundle full.hg empty
32 32 hg -R test unbundle full.hg
33 33 hg -R empty heads
34 34 hg -R empty verify
35 35
36 36 hg --cwd test pull ../full.hg
37 37 hg --cwd empty pull ../full.hg
38 38 hg -R empty rollback
39 39 hg --cwd empty pull ../full.hg
40 40
41 41 rm -rf empty
42 42 hg init empty
43 43 cd empty
44 44 hg -R bundle://../full.hg log
45 45 echo '[hooks]' >> .hg/hgrc
46 46 echo 'changegroup = echo changegroup: u=$HG_URL' >> .hg/hgrc
47 47 #doesn't work (yet ?)
48 48 #hg -R bundle://../full.hg verify
49 49 hg pull bundle://../full.hg
50 50 cd ..
51 51
52 52 rm -rf empty
53 53 hg init empty
54 54 hg clone -r 3 test partial
55 55 hg clone partial partial2
56 56 cd partial
57 57 hg -R bundle://../full.hg log
58 58 hg incoming bundle://../full.hg
59 59 hg -R bundle://../full.hg outgoing ../partial2
60 hg -R bundle://../does-not-exist.hg outgoing ../partial2
60 61 cd ..
@@ -1,210 +1,211
1 1 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2 2 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
3 3 checking changesets
4 4 checking manifests
5 5 crosschecking files in changesets and manifests
6 6 checking files
7 7 4 files, 9 changesets, 7 total revisions
8 8 searching for changes
9 9 adding changesets
10 10 adding manifests
11 11 adding file changes
12 12 added 0 changesets with 0 changes to 4 files
13 13 (run 'hg update' to get a working copy)
14 14 changeset: -1:000000000000
15 15 tag: tip
16 16 user:
17 17 date: Thu Jan 01 00:00:00 1970 +0000
18 18
19 19 checking changesets
20 20 checking manifests
21 21 crosschecking files in changesets and manifests
22 22 checking files
23 23 0 files, 0 changesets, 0 total revisions
24 24 pulling from ../full.hg
25 25 searching for changes
26 26 no changes found
27 27 pulling from ../full.hg
28 28 requesting all changes
29 29 adding changesets
30 30 adding manifests
31 31 adding file changes
32 32 added 9 changesets with 7 changes to 4 files (+1 heads)
33 33 (run 'hg heads' to see heads, 'hg merge' to merge)
34 34 rolling back last transaction
35 35 pulling from ../full.hg
36 36 requesting all changes
37 37 adding changesets
38 38 adding manifests
39 39 adding file changes
40 40 added 9 changesets with 7 changes to 4 files (+1 heads)
41 41 (run 'hg heads' to see heads, 'hg merge' to merge)
42 42 changeset: 8:836ac62537ab
43 43 tag: tip
44 44 parent: 3:ac69c658229d
45 45 user: test
46 46 date: Mon Jan 12 13:46:40 1970 +0000
47 47 summary: 0.3m
48 48
49 49 changeset: 7:80fe151401c2
50 50 user: test
51 51 date: Mon Jan 12 13:46:40 1970 +0000
52 52 summary: 1.3m
53 53
54 54 changeset: 6:1e3f6b843bd6
55 55 user: test
56 56 date: Mon Jan 12 13:46:40 1970 +0000
57 57 summary: 1.3
58 58
59 59 changeset: 5:024e4e7df376
60 60 user: test
61 61 date: Mon Jan 12 13:46:40 1970 +0000
62 62 summary: 1.2
63 63
64 64 changeset: 4:5f4f3ceb285e
65 65 parent: 0:5649c9d34dd8
66 66 user: test
67 67 date: Mon Jan 12 13:46:40 1970 +0000
68 68 summary: 1.1
69 69
70 70 changeset: 3:ac69c658229d
71 71 user: test
72 72 date: Mon Jan 12 13:46:40 1970 +0000
73 73 summary: 0.3
74 74
75 75 changeset: 2:d62976ca1e50
76 76 user: test
77 77 date: Mon Jan 12 13:46:40 1970 +0000
78 78 summary: 0.2
79 79
80 80 changeset: 1:10b2180f755b
81 81 user: test
82 82 date: Mon Jan 12 13:46:40 1970 +0000
83 83 summary: 0.1
84 84
85 85 changeset: 0:5649c9d34dd8
86 86 user: test
87 87 date: Mon Jan 12 13:46:40 1970 +0000
88 88 summary: 0.0
89 89
90 90 changegroup: u=bundle:../full.hg
91 91 pulling from bundle://../full.hg
92 92 requesting all changes
93 93 adding changesets
94 94 adding manifests
95 95 adding file changes
96 96 added 9 changesets with 7 changes to 4 files (+1 heads)
97 97 (run 'hg heads' to see heads, 'hg merge' to merge)
98 98 requesting all changes
99 99 adding changesets
100 100 adding manifests
101 101 adding file changes
102 102 added 4 changesets with 4 changes to 1 files
103 103 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
104 104 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
105 105 changeset: 8:836ac62537ab
106 106 tag: tip
107 107 parent: 3:ac69c658229d
108 108 user: test
109 109 date: Mon Jan 12 13:46:40 1970 +0000
110 110 summary: 0.3m
111 111
112 112 changeset: 7:80fe151401c2
113 113 user: test
114 114 date: Mon Jan 12 13:46:40 1970 +0000
115 115 summary: 1.3m
116 116
117 117 changeset: 6:1e3f6b843bd6
118 118 user: test
119 119 date: Mon Jan 12 13:46:40 1970 +0000
120 120 summary: 1.3
121 121
122 122 changeset: 5:024e4e7df376
123 123 user: test
124 124 date: Mon Jan 12 13:46:40 1970 +0000
125 125 summary: 1.2
126 126
127 127 changeset: 4:5f4f3ceb285e
128 128 parent: 0:5649c9d34dd8
129 129 user: test
130 130 date: Mon Jan 12 13:46:40 1970 +0000
131 131 summary: 1.1
132 132
133 133 changeset: 3:ac69c658229d
134 134 user: test
135 135 date: Mon Jan 12 13:46:40 1970 +0000
136 136 summary: 0.3
137 137
138 138 changeset: 2:d62976ca1e50
139 139 user: test
140 140 date: Mon Jan 12 13:46:40 1970 +0000
141 141 summary: 0.2
142 142
143 143 changeset: 1:10b2180f755b
144 144 user: test
145 145 date: Mon Jan 12 13:46:40 1970 +0000
146 146 summary: 0.1
147 147
148 148 changeset: 0:5649c9d34dd8
149 149 user: test
150 150 date: Mon Jan 12 13:46:40 1970 +0000
151 151 summary: 0.0
152 152
153 153 searching for changes
154 154 changeset: 4:5f4f3ceb285e
155 155 parent: 0:5649c9d34dd8
156 156 user: test
157 157 date: Mon Jan 12 13:46:40 1970 +0000
158 158 summary: 1.1
159 159
160 160 changeset: 5:024e4e7df376
161 161 user: test
162 162 date: Mon Jan 12 13:46:40 1970 +0000
163 163 summary: 1.2
164 164
165 165 changeset: 6:1e3f6b843bd6
166 166 user: test
167 167 date: Mon Jan 12 13:46:40 1970 +0000
168 168 summary: 1.3
169 169
170 170 changeset: 7:80fe151401c2
171 171 user: test
172 172 date: Mon Jan 12 13:46:40 1970 +0000
173 173 summary: 1.3m
174 174
175 175 changeset: 8:836ac62537ab
176 176 tag: tip
177 177 parent: 3:ac69c658229d
178 178 user: test
179 179 date: Mon Jan 12 13:46:40 1970 +0000
180 180 summary: 0.3m
181 181
182 182 searching for changes
183 183 changeset: 4:5f4f3ceb285e
184 184 parent: 0:5649c9d34dd8
185 185 user: test
186 186 date: Mon Jan 12 13:46:40 1970 +0000
187 187 summary: 1.1
188 188
189 189 changeset: 5:024e4e7df376
190 190 user: test
191 191 date: Mon Jan 12 13:46:40 1970 +0000
192 192 summary: 1.2
193 193
194 194 changeset: 6:1e3f6b843bd6
195 195 user: test
196 196 date: Mon Jan 12 13:46:40 1970 +0000
197 197 summary: 1.3
198 198
199 199 changeset: 7:80fe151401c2
200 200 user: test
201 201 date: Mon Jan 12 13:46:40 1970 +0000
202 202 summary: 1.3m
203 203
204 204 changeset: 8:836ac62537ab
205 205 tag: tip
206 206 parent: 3:ac69c658229d
207 207 user: test
208 208 date: Mon Jan 12 13:46:40 1970 +0000
209 209 summary: 0.3m
210 210
211 abort: No such file or directory: ../does-not-exist.hg
General Comments 0
You need to be logged in to leave comments. Login now