##// END OF EJS Templates
Move hg from storing files in data with base64 encoding to full...
mpm@selenic.com -
r110:c37c7f78 default
parent child Browse files
Show More
@@ -1,873 +1,870 b''
1 # hg.py - repository classes for mercurial
1 # hg.py - repository classes for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import sys, struct, sha, socket, os, time, base64, re, urllib2
8 import sys, struct, sha, socket, os, time, re, urllib2
9 import urllib
9 import urllib
10 from mercurial import byterange
10 from mercurial import byterange
11 from mercurial.transaction import *
11 from mercurial.transaction import *
12 from mercurial.revlog import *
12 from mercurial.revlog import *
13 from difflib import SequenceMatcher
13 from difflib import SequenceMatcher
14
14
15 class filelog(revlog):
15 class filelog(revlog):
16 def __init__(self, opener, path):
16 def __init__(self, opener, path):
17 s = self.encodepath(path)
17 revlog.__init__(self, opener, path + ".i", path + ".d")
18 revlog.__init__(self, opener, os.path.join("data", s + "i"),
19 os.path.join("data", s))
20
21 def encodepath(self, path):
22 s = sha.sha(path).digest()
23 s = base64.encodestring(s)[:-3]
24 s = re.sub("\+", "%", s)
25 s = re.sub("/", "_", s)
26 return s
27
18
28 def read(self, node):
19 def read(self, node):
29 return self.revision(node)
20 return self.revision(node)
30 def add(self, text, transaction, link, p1=None, p2=None):
21 def add(self, text, transaction, link, p1=None, p2=None):
31 return self.addrevision(text, transaction, link, p1, p2)
22 return self.addrevision(text, transaction, link, p1, p2)
32
23
33 def annotate(self, node):
24 def annotate(self, node):
34 revs = []
25 revs = []
35 while node != nullid:
26 while node != nullid:
36 revs.append(node)
27 revs.append(node)
37 node = self.parents(node)[0]
28 node = self.parents(node)[0]
38 revs.reverse()
29 revs.reverse()
39 prev = []
30 prev = []
40 annotate = []
31 annotate = []
41
32
42 for node in revs:
33 for node in revs:
43 curr = self.read(node).splitlines(1)
34 curr = self.read(node).splitlines(1)
44 linkrev = self.linkrev(node)
35 linkrev = self.linkrev(node)
45 sm = SequenceMatcher(None, prev, curr)
36 sm = SequenceMatcher(None, prev, curr)
46 new = []
37 new = []
47 for o, m, n, s, t in sm.get_opcodes():
38 for o, m, n, s, t in sm.get_opcodes():
48 if o == 'equal':
39 if o == 'equal':
49 new += annotate[m:n]
40 new += annotate[m:n]
50 else:
41 else:
51 new += [(linkrev, l) for l in curr[s:t]]
42 new += [(linkrev, l) for l in curr[s:t]]
52 annotate, prev = new, curr
43 annotate, prev = new, curr
53 return annotate
44 return annotate
54
45
55 class manifest(revlog):
46 class manifest(revlog):
56 def __init__(self, opener):
47 def __init__(self, opener):
57 self.mapcache = None
48 self.mapcache = None
58 self.listcache = None
49 self.listcache = None
59 self.addlist = None
50 self.addlist = None
60 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
51 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
61
52
62 def read(self, node):
53 def read(self, node):
63 if self.mapcache and self.mapcache[0] == node:
54 if self.mapcache and self.mapcache[0] == node:
64 return self.mapcache[1].copy()
55 return self.mapcache[1].copy()
65 text = self.revision(node)
56 text = self.revision(node)
66 map = {}
57 map = {}
67 self.listcache = (text, text.splitlines(1))
58 self.listcache = (text, text.splitlines(1))
68 for l in self.listcache[1]:
59 for l in self.listcache[1]:
69 (f, n) = l.split('\0')
60 (f, n) = l.split('\0')
70 map[f] = bin(n[:40])
61 map[f] = bin(n[:40])
71 self.mapcache = (node, map)
62 self.mapcache = (node, map)
72 return map
63 return map
73
64
74 def diff(self, a, b):
65 def diff(self, a, b):
75 # this is sneaky, as we're not actually using a and b
66 # this is sneaky, as we're not actually using a and b
76 if self.listcache and len(self.listcache[0]) == len(a):
67 if self.listcache and len(self.listcache[0]) == len(a):
77 d = mdiff.diff(self.listcache[1], self.addlist, 1)
68 d = mdiff.diff(self.listcache[1], self.addlist, 1)
78 if mdiff.patch(a, d) != b:
69 if mdiff.patch(a, d) != b:
79 sys.stderr.write("*** sortdiff failed, falling back ***\n")
70 sys.stderr.write("*** sortdiff failed, falling back ***\n")
80 return mdiff.textdiff(a, b)
71 return mdiff.textdiff(a, b)
81 return d
72 return d
82 else:
73 else:
83 return mdiff.textdiff(a, b)
74 return mdiff.textdiff(a, b)
84
75
85 def add(self, map, transaction, link, p1=None, p2=None):
76 def add(self, map, transaction, link, p1=None, p2=None):
86 files = map.keys()
77 files = map.keys()
87 files.sort()
78 files.sort()
88
79
89 self.addlist = ["%s\000%s\n" % (f, hex(map[f])) for f in files]
80 self.addlist = ["%s\000%s\n" % (f, hex(map[f])) for f in files]
90 text = "".join(self.addlist)
81 text = "".join(self.addlist)
91
82
92 n = self.addrevision(text, transaction, link, p1, p2)
83 n = self.addrevision(text, transaction, link, p1, p2)
93 self.mapcache = (n, map)
84 self.mapcache = (n, map)
94 self.listcache = (text, self.addlist)
85 self.listcache = (text, self.addlist)
95
86
96 return n
87 return n
97
88
98 class changelog(revlog):
89 class changelog(revlog):
99 def __init__(self, opener):
90 def __init__(self, opener):
100 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
91 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
101
92
102 def extract(self, text):
93 def extract(self, text):
103 if not text:
94 if not text:
104 return (nullid, "", "0", [], "")
95 return (nullid, "", "0", [], "")
105 last = text.index("\n\n")
96 last = text.index("\n\n")
106 desc = text[last + 2:]
97 desc = text[last + 2:]
107 l = text[:last].splitlines()
98 l = text[:last].splitlines()
108 manifest = bin(l[0])
99 manifest = bin(l[0])
109 user = l[1]
100 user = l[1]
110 date = l[2]
101 date = l[2]
111 files = l[3:]
102 files = l[3:]
112 return (manifest, user, date, files, desc)
103 return (manifest, user, date, files, desc)
113
104
114 def read(self, node):
105 def read(self, node):
115 return self.extract(self.revision(node))
106 return self.extract(self.revision(node))
116
107
117 def add(self, manifest, list, desc, transaction, p1=None, p2=None):
108 def add(self, manifest, list, desc, transaction, p1=None, p2=None):
118 user = (os.environ.get("HGUSER") or
109 user = (os.environ.get("HGUSER") or
119 os.environ.get("EMAIL") or
110 os.environ.get("EMAIL") or
120 os.environ.get("LOGNAME", "unknown") + '@' + socket.getfqdn())
111 os.environ.get("LOGNAME", "unknown") + '@' + socket.getfqdn())
121 date = "%d %d" % (time.time(), time.timezone)
112 date = "%d %d" % (time.time(), time.timezone)
122 list.sort()
113 list.sort()
123 l = [hex(manifest), user, date] + list + ["", desc]
114 l = [hex(manifest), user, date] + list + ["", desc]
124 text = "\n".join(l)
115 text = "\n".join(l)
125 return self.addrevision(text, transaction, self.count(), p1, p2)
116 return self.addrevision(text, transaction, self.count(), p1, p2)
126
117
127 class dircache:
118 class dircache:
128 def __init__(self, opener, ui):
119 def __init__(self, opener, ui):
129 self.opener = opener
120 self.opener = opener
130 self.dirty = 0
121 self.dirty = 0
131 self.ui = ui
122 self.ui = ui
132 self.map = None
123 self.map = None
133 def __del__(self):
124 def __del__(self):
134 if self.dirty: self.write()
125 if self.dirty: self.write()
135 def __getitem__(self, key):
126 def __getitem__(self, key):
136 try:
127 try:
137 return self.map[key]
128 return self.map[key]
138 except TypeError:
129 except TypeError:
139 self.read()
130 self.read()
140 return self[key]
131 return self[key]
141
132
142 def read(self):
133 def read(self):
143 if self.map is not None: return self.map
134 if self.map is not None: return self.map
144
135
145 self.map = {}
136 self.map = {}
146 try:
137 try:
147 st = self.opener("dircache").read()
138 st = self.opener("dircache").read()
148 except: return
139 except: return
149
140
150 pos = 0
141 pos = 0
151 while pos < len(st):
142 while pos < len(st):
152 e = struct.unpack(">llll", st[pos:pos+16])
143 e = struct.unpack(">llll", st[pos:pos+16])
153 l = e[3]
144 l = e[3]
154 pos += 16
145 pos += 16
155 f = st[pos:pos + l]
146 f = st[pos:pos + l]
156 self.map[f] = e[:3]
147 self.map[f] = e[:3]
157 pos += l
148 pos += l
158
149
159 def update(self, files):
150 def update(self, files):
160 if not files: return
151 if not files: return
161 self.read()
152 self.read()
162 self.dirty = 1
153 self.dirty = 1
163 for f in files:
154 for f in files:
164 try:
155 try:
165 s = os.stat(f)
156 s = os.stat(f)
166 self.map[f] = (s.st_mode, s.st_size, s.st_mtime)
157 self.map[f] = (s.st_mode, s.st_size, s.st_mtime)
167 except IOError:
158 except IOError:
168 self.remove(f)
159 self.remove(f)
169
160
170 def taint(self, files):
161 def taint(self, files):
171 if not files: return
162 if not files: return
172 self.read()
163 self.read()
173 self.dirty = 1
164 self.dirty = 1
174 for f in files:
165 for f in files:
175 self.map[f] = (0, -1, 0)
166 self.map[f] = (0, -1, 0)
176
167
177 def remove(self, files):
168 def remove(self, files):
178 if not files: return
169 if not files: return
179 self.read()
170 self.read()
180 self.dirty = 1
171 self.dirty = 1
181 for f in files:
172 for f in files:
182 try:
173 try:
183 del self.map[f]
174 del self.map[f]
184 except KeyError:
175 except KeyError:
185 self.ui.warn("Not in dircache: %s\n" % f)
176 self.ui.warn("Not in dircache: %s\n" % f)
186 pass
177 pass
187
178
188 def clear(self):
179 def clear(self):
189 self.map = {}
180 self.map = {}
190 self.dirty = 1
181 self.dirty = 1
191
182
192 def write(self):
183 def write(self):
193 st = self.opener("dircache", "w")
184 st = self.opener("dircache", "w")
194 for f, e in self.map.items():
185 for f, e in self.map.items():
195 e = struct.pack(">llll", e[0], e[1], e[2], len(f))
186 e = struct.pack(">llll", e[0], e[1], e[2], len(f))
196 st.write(e + f)
187 st.write(e + f)
197 self.dirty = 0
188 self.dirty = 0
198
189
199 def copy(self):
190 def copy(self):
200 self.read()
191 self.read()
201 return self.map.copy()
192 return self.map.copy()
202
193
203 # used to avoid circular references so destructors work
194 # used to avoid circular references so destructors work
204 def opener(base):
195 def opener(base):
205 p = base
196 p = base
206 def o(path, mode="r"):
197 def o(path, mode="r"):
207 if p[:7] == "http://":
198 if p[:7] == "http://":
208 f = os.path.join(p, urllib.quote(path))
199 f = os.path.join(p, urllib.quote(path))
209 return httprangereader(f)
200 return httprangereader(f)
210
201
211 f = os.path.join(p, path)
202 f = os.path.join(p, path)
212
203
213 if mode != "r" and os.path.isfile(f):
204 if mode != "r":
205 try:
214 s = os.stat(f)
206 s = os.stat(f)
207 except OSError:
208 d = os.path.dirname(f)
209 if not os.path.isdir(d):
210 os.makedirs(d)
211 else:
215 if s.st_nlink > 1:
212 if s.st_nlink > 1:
216 file(f + ".tmp", "w").write(file(f).read())
213 file(f + ".tmp", "w").write(file(f).read())
217 os.rename(f+".tmp", f)
214 os.rename(f+".tmp", f)
218
215
219 return file(f, mode)
216 return file(f, mode)
220
217
221 return o
218 return o
222
219
223 class localrepository:
220 class localrepository:
224 def __init__(self, ui, path=None, create=0):
221 def __init__(self, ui, path=None, create=0):
225 self.remote = 0
222 self.remote = 0
226 if path and path[:7] == "http://":
223 if path and path[:7] == "http://":
227 self.remote = 1
224 self.remote = 1
228 self.path = path
225 self.path = path
229 else:
226 else:
230 if not path:
227 if not path:
231 p = os.getcwd()
228 p = os.getcwd()
232 while not os.path.isdir(os.path.join(p, ".hg")):
229 while not os.path.isdir(os.path.join(p, ".hg")):
233 p = os.path.dirname(p)
230 p = os.path.dirname(p)
234 if p == "/": raise "No repo found"
231 if p == "/": raise "No repo found"
235 path = p
232 path = p
236 self.path = os.path.join(path, ".hg")
233 self.path = os.path.join(path, ".hg")
237
234
238 self.root = path
235 self.root = path
239 self.ui = ui
236 self.ui = ui
240
237
241 if create:
238 if create:
242 os.mkdir(self.path)
239 os.mkdir(self.path)
243 os.mkdir(self.join("data"))
240 os.mkdir(self.join("data"))
244
241
245 self.opener = opener(self.path)
242 self.opener = opener(self.path)
246 self.manifest = manifest(self.opener)
243 self.manifest = manifest(self.opener)
247 self.changelog = changelog(self.opener)
244 self.changelog = changelog(self.opener)
248 self.ignorelist = None
245 self.ignorelist = None
249 self.tags = None
246 self.tags = None
250
247
251 if not self.remote:
248 if not self.remote:
252 self.dircache = dircache(self.opener, ui)
249 self.dircache = dircache(self.opener, ui)
253 try:
250 try:
254 self.current = bin(self.opener("current").read())
251 self.current = bin(self.opener("current").read())
255 except IOError:
252 except IOError:
256 self.current = None
253 self.current = None
257
254
258 def setcurrent(self, node):
255 def setcurrent(self, node):
259 self.current = node
256 self.current = node
260 self.opener("current", "w").write(hex(node))
257 self.opener("current", "w").write(hex(node))
261
258
262 def ignore(self, f):
259 def ignore(self, f):
263 if self.ignorelist is None:
260 if self.ignorelist is None:
264 self.ignorelist = []
261 self.ignorelist = []
265 try:
262 try:
266 l = open(os.path.join(self.root, ".hgignore"))
263 l = open(os.path.join(self.root, ".hgignore"))
267 for pat in l:
264 for pat in l:
268 if pat != "\n":
265 if pat != "\n":
269 self.ignorelist.append(re.compile(pat[:-1]))
266 self.ignorelist.append(re.compile(pat[:-1]))
270 except IOError: pass
267 except IOError: pass
271 for pat in self.ignorelist:
268 for pat in self.ignorelist:
272 if pat.search(f): return True
269 if pat.search(f): return True
273 return False
270 return False
274
271
275 def lookup(self, key):
272 def lookup(self, key):
276 if self.tags is None:
273 if self.tags is None:
277 self.tags = {}
274 self.tags = {}
278 try:
275 try:
279 fl = self.file(".hgtags")
276 fl = self.file(".hgtags")
280 for l in fl.revision(fl.tip()).splitlines():
277 for l in fl.revision(fl.tip()).splitlines():
281 if l:
278 if l:
282 n, k = l.split(" ")
279 n, k = l.split(" ")
283 self.tags[k] = bin(n)
280 self.tags[k] = bin(n)
284 except KeyError: pass
281 except KeyError: pass
285 try:
282 try:
286 return self.tags[key]
283 return self.tags[key]
287 except KeyError:
284 except KeyError:
288 return self.changelog.lookup(key)
285 return self.changelog.lookup(key)
289
286
290 def join(self, f):
287 def join(self, f):
291 return os.path.join(self.path, f)
288 return os.path.join(self.path, f)
292
289
293 def file(self, f):
290 def file(self, f):
294 return filelog(self.opener, f)
291 return filelog(self.opener, f)
295
292
296 def transaction(self):
293 def transaction(self):
297 return transaction(self.opener, self.join("journal"),
294 return transaction(self.opener, self.join("journal"),
298 self.join("undo"))
295 self.join("undo"))
299
296
300 def commit(self, parent, update = None, text = ""):
297 def commit(self, parent, update = None, text = ""):
301 tr = self.transaction()
298 tr = self.transaction()
302
299
303 try:
300 try:
304 remove = [ l[:-1] for l in self.opener("to-remove") ]
301 remove = [ l[:-1] for l in self.opener("to-remove") ]
305 os.unlink(self.join("to-remove"))
302 os.unlink(self.join("to-remove"))
306
303
307 except IOError:
304 except IOError:
308 remove = []
305 remove = []
309
306
310 if update == None:
307 if update == None:
311 update = self.diffdir(self.root, parent)[0]
308 update = self.diffdir(self.root, parent)[0]
312
309
313 # check in files
310 # check in files
314 new = {}
311 new = {}
315 linkrev = self.changelog.count()
312 linkrev = self.changelog.count()
316 for f in update:
313 for f in update:
317 self.ui.note(f + "\n")
314 self.ui.note(f + "\n")
318 try:
315 try:
319 t = file(f).read()
316 t = file(f).read()
320 except IOError:
317 except IOError:
321 remove.append(f)
318 remove.append(f)
322 continue
319 continue
323 r = self.file(f)
320 r = self.file(f)
324 new[f] = r.add(t, tr, linkrev)
321 new[f] = r.add(t, tr, linkrev)
325
322
326 # update manifest
323 # update manifest
327 mmap = self.manifest.read(self.manifest.tip())
324 mmap = self.manifest.read(self.manifest.tip())
328 mmap.update(new)
325 mmap.update(new)
329 for f in remove:
326 for f in remove:
330 del mmap[f]
327 del mmap[f]
331 mnode = self.manifest.add(mmap, tr, linkrev)
328 mnode = self.manifest.add(mmap, tr, linkrev)
332
329
333 # add changeset
330 # add changeset
334 new = new.keys()
331 new = new.keys()
335 new.sort()
332 new.sort()
336
333
337 edittext = text + "\n"+"".join(["HG: changed %s\n" % f for f in new])
334 edittext = text + "\n"+"".join(["HG: changed %s\n" % f for f in new])
338 edittext += "".join(["HG: removed %s\n" % f for f in remove])
335 edittext += "".join(["HG: removed %s\n" % f for f in remove])
339 edittext = self.ui.edit(edittext)
336 edittext = self.ui.edit(edittext)
340
337
341 n = self.changelog.add(mnode, new, edittext, tr)
338 n = self.changelog.add(mnode, new, edittext, tr)
342 tr.close()
339 tr.close()
343
340
344 self.setcurrent(n)
341 self.setcurrent(n)
345 self.dircache.update(new)
342 self.dircache.update(new)
346 self.dircache.remove(remove)
343 self.dircache.remove(remove)
347
344
348 def checkdir(self, path):
345 def checkdir(self, path):
349 d = os.path.dirname(path)
346 d = os.path.dirname(path)
350 if not d: return
347 if not d: return
351 if not os.path.isdir(d):
348 if not os.path.isdir(d):
352 self.checkdir(d)
349 self.checkdir(d)
353 os.mkdir(d)
350 os.mkdir(d)
354
351
355 def checkout(self, node):
352 def checkout(self, node):
356 # checkout is really dumb at the moment
353 # checkout is really dumb at the moment
357 # it ought to basically merge
354 # it ought to basically merge
358 change = self.changelog.read(node)
355 change = self.changelog.read(node)
359 mmap = self.manifest.read(change[0])
356 mmap = self.manifest.read(change[0])
360
357
361 l = mmap.keys()
358 l = mmap.keys()
362 l.sort()
359 l.sort()
363 stats = []
360 stats = []
364 for f in l:
361 for f in l:
365 self.ui.note(f + "\n")
362 self.ui.note(f + "\n")
366 r = self.file(f)
363 r = self.file(f)
367 t = r.revision(mmap[f])
364 t = r.revision(mmap[f])
368 try:
365 try:
369 file(f, "w").write(t)
366 file(f, "w").write(t)
370 except:
367 except:
371 self.checkdir(f)
368 self.checkdir(f)
372 file(f, "w").write(t)
369 file(f, "w").write(t)
373
370
374 self.setcurrent(node)
371 self.setcurrent(node)
375 self.dircache.clear()
372 self.dircache.clear()
376 self.dircache.update(l)
373 self.dircache.update(l)
377
374
378 def diffdir(self, path, changeset):
375 def diffdir(self, path, changeset):
379 changed = []
376 changed = []
380 mf = {}
377 mf = {}
381 added = []
378 added = []
382
379
383 if changeset:
380 if changeset:
384 change = self.changelog.read(changeset)
381 change = self.changelog.read(changeset)
385 mf = self.manifest.read(change[0])
382 mf = self.manifest.read(change[0])
386
383
387 if changeset == self.current:
384 if changeset == self.current:
388 dc = self.dircache.copy()
385 dc = self.dircache.copy()
389 else:
386 else:
390 dc = dict.fromkeys(mf)
387 dc = dict.fromkeys(mf)
391
388
392 def fcmp(fn):
389 def fcmp(fn):
393 t1 = file(os.path.join(self.root, fn)).read()
390 t1 = file(os.path.join(self.root, fn)).read()
394 t2 = self.file(fn).revision(mf[fn])
391 t2 = self.file(fn).revision(mf[fn])
395 return cmp(t1, t2)
392 return cmp(t1, t2)
396
393
397 for dir, subdirs, files in os.walk(self.root):
394 for dir, subdirs, files in os.walk(self.root):
398 d = dir[len(self.root)+1:]
395 d = dir[len(self.root)+1:]
399 if ".hg" in subdirs: subdirs.remove(".hg")
396 if ".hg" in subdirs: subdirs.remove(".hg")
400
397
401 for f in files:
398 for f in files:
402 fn = os.path.join(d, f)
399 fn = os.path.join(d, f)
403 try: s = os.stat(os.path.join(self.root, fn))
400 try: s = os.stat(os.path.join(self.root, fn))
404 except: continue
401 except: continue
405 if fn in dc:
402 if fn in dc:
406 c = dc[fn]
403 c = dc[fn]
407 del dc[fn]
404 del dc[fn]
408 if not c:
405 if not c:
409 if fcmp(fn):
406 if fcmp(fn):
410 changed.append(fn)
407 changed.append(fn)
411 elif c[1] != s.st_size:
408 elif c[1] != s.st_size:
412 changed.append(fn)
409 changed.append(fn)
413 elif c[0] != s.st_mode or c[2] != s.st_mtime:
410 elif c[0] != s.st_mode or c[2] != s.st_mtime:
414 if fcmp(fn):
411 if fcmp(fn):
415 changed.append(fn)
412 changed.append(fn)
416 else:
413 else:
417 if self.ignore(fn): continue
414 if self.ignore(fn): continue
418 added.append(fn)
415 added.append(fn)
419
416
420 deleted = dc.keys()
417 deleted = dc.keys()
421 deleted.sort()
418 deleted.sort()
422
419
423 return (changed, added, deleted)
420 return (changed, added, deleted)
424
421
425 def diffrevs(self, node1, node2):
422 def diffrevs(self, node1, node2):
426 changed, added = [], []
423 changed, added = [], []
427
424
428 change = self.changelog.read(node1)
425 change = self.changelog.read(node1)
429 mf1 = self.manifest.read(change[0])
426 mf1 = self.manifest.read(change[0])
430 change = self.changelog.read(node2)
427 change = self.changelog.read(node2)
431 mf2 = self.manifest.read(change[0])
428 mf2 = self.manifest.read(change[0])
432
429
433 for fn in mf2:
430 for fn in mf2:
434 if mf1.has_key(fn):
431 if mf1.has_key(fn):
435 if mf1[fn] != mf2[fn]:
432 if mf1[fn] != mf2[fn]:
436 changed.append(fn)
433 changed.append(fn)
437 del mf1[fn]
434 del mf1[fn]
438 else:
435 else:
439 added.append(fn)
436 added.append(fn)
440
437
441 deleted = mf1.keys()
438 deleted = mf1.keys()
442 deleted.sort()
439 deleted.sort()
443
440
444 return (changed, added, deleted)
441 return (changed, added, deleted)
445
442
446 def add(self, list):
443 def add(self, list):
447 self.dircache.taint(list)
444 self.dircache.taint(list)
448
445
449 def remove(self, list):
446 def remove(self, list):
450 dl = self.opener("to-remove", "a")
447 dl = self.opener("to-remove", "a")
451 for f in list:
448 for f in list:
452 dl.write(f + "\n")
449 dl.write(f + "\n")
453
450
454 def branches(self, nodes):
451 def branches(self, nodes):
455 if not nodes: nodes = [self.changelog.tip()]
452 if not nodes: nodes = [self.changelog.tip()]
456 b = []
453 b = []
457 for n in nodes:
454 for n in nodes:
458 t = n
455 t = n
459 while n:
456 while n:
460 p = self.changelog.parents(n)
457 p = self.changelog.parents(n)
461 if p[1] != nullid or p[0] == nullid:
458 if p[1] != nullid or p[0] == nullid:
462 b.append((t, n, p[0], p[1]))
459 b.append((t, n, p[0], p[1]))
463 break
460 break
464 n = p[0]
461 n = p[0]
465 return b
462 return b
466
463
467 def between(self, pairs):
464 def between(self, pairs):
468 r = []
465 r = []
469
466
470 for top, bottom in pairs:
467 for top, bottom in pairs:
471 n, l, i = top, [], 0
468 n, l, i = top, [], 0
472 f = 1
469 f = 1
473
470
474 while n != bottom:
471 while n != bottom:
475 p = self.changelog.parents(n)[0]
472 p = self.changelog.parents(n)[0]
476 if i == f:
473 if i == f:
477 l.append(n)
474 l.append(n)
478 f = f * 2
475 f = f * 2
479 n = p
476 n = p
480 i += 1
477 i += 1
481
478
482 r.append(l)
479 r.append(l)
483
480
484 return r
481 return r
485
482
486 def newer(self, nodes):
483 def newer(self, nodes):
487 m = {}
484 m = {}
488 nl = []
485 nl = []
489 pm = {}
486 pm = {}
490 cl = self.changelog
487 cl = self.changelog
491 t = l = cl.count()
488 t = l = cl.count()
492
489
493 # find the lowest numbered node
490 # find the lowest numbered node
494 for n in nodes:
491 for n in nodes:
495 l = min(l, cl.rev(n))
492 l = min(l, cl.rev(n))
496 m[n] = 1
493 m[n] = 1
497
494
498 for i in xrange(l, t):
495 for i in xrange(l, t):
499 n = cl.node(i)
496 n = cl.node(i)
500 if n in m: # explicitly listed
497 if n in m: # explicitly listed
501 pm[n] = 1
498 pm[n] = 1
502 nl.append(n)
499 nl.append(n)
503 continue
500 continue
504 for p in cl.parents(n):
501 for p in cl.parents(n):
505 if p in pm: # parent listed
502 if p in pm: # parent listed
506 pm[n] = 1
503 pm[n] = 1
507 nl.append(n)
504 nl.append(n)
508 break
505 break
509
506
510 return nl
507 return nl
511
508
512 def getchangegroup(self, remote):
509 def getchangegroup(self, remote):
513 tip = remote.branches([])[0]
510 tip = remote.branches([])[0]
514 self.ui.debug("remote tip branch is %s:%s\n" %
511 self.ui.debug("remote tip branch is %s:%s\n" %
515 (short(tip[0]), short(tip[1])))
512 (short(tip[0]), short(tip[1])))
516 m = self.changelog.nodemap
513 m = self.changelog.nodemap
517 unknown = [tip]
514 unknown = [tip]
518 search = []
515 search = []
519 fetch = []
516 fetch = []
520
517
521 if tip[0] in m:
518 if tip[0] in m:
522 self.ui.note("nothing to do!\n")
519 self.ui.note("nothing to do!\n")
523 return None
520 return None
524
521
525 while unknown:
522 while unknown:
526 n = unknown.pop(0)
523 n = unknown.pop(0)
527 if n == nullid: break
524 if n == nullid: break
528 if n[1] and n[1] in m: # do we know the base?
525 if n[1] and n[1] in m: # do we know the base?
529 self.ui.debug("found incomplete branch %s\n" % short(n[1]))
526 self.ui.debug("found incomplete branch %s\n" % short(n[1]))
530 search.append(n) # schedule branch range for scanning
527 search.append(n) # schedule branch range for scanning
531 else:
528 else:
532 if n[2] in m and n[3] in m:
529 if n[2] in m and n[3] in m:
533 if n[1] not in fetch:
530 if n[1] not in fetch:
534 self.ui.debug("found new changeset %s\n" %
531 self.ui.debug("found new changeset %s\n" %
535 short(n[1]))
532 short(n[1]))
536 fetch.append(n[1]) # earliest unknown
533 fetch.append(n[1]) # earliest unknown
537 continue
534 continue
538 for b in remote.branches([n[2], n[3]]):
535 for b in remote.branches([n[2], n[3]]):
539 if b[0] not in m:
536 if b[0] not in m:
540 unknown.append(b)
537 unknown.append(b)
541
538
542 while search:
539 while search:
543 n = search.pop(0)
540 n = search.pop(0)
544 l = remote.between([(n[0], n[1])])[0]
541 l = remote.between([(n[0], n[1])])[0]
545 p = n[0]
542 p = n[0]
546 f = 1
543 f = 1
547 for i in l + [n[1]]:
544 for i in l + [n[1]]:
548 if i in m:
545 if i in m:
549 if f <= 2:
546 if f <= 2:
550 self.ui.debug("found new branch changeset %s\n" %
547 self.ui.debug("found new branch changeset %s\n" %
551 short(p))
548 short(p))
552 fetch.append(p)
549 fetch.append(p)
553 else:
550 else:
554 self.ui.debug("narrowed branch search to %s:%s\n"
551 self.ui.debug("narrowed branch search to %s:%s\n"
555 % (short(p), short(i)))
552 % (short(p), short(i)))
556 search.append((p, i))
553 search.append((p, i))
557 break
554 break
558 p, f = i, f * 2
555 p, f = i, f * 2
559
556
560 for f in fetch:
557 for f in fetch:
561 if f in m:
558 if f in m:
562 raise "already have", short(f[:4])
559 raise "already have", short(f[:4])
563
560
564 self.ui.note("adding new changesets starting at " +
561 self.ui.note("adding new changesets starting at " +
565 " ".join([short(f) for f in fetch]) + "\n")
562 " ".join([short(f) for f in fetch]) + "\n")
566
563
567 return remote.changegroup(fetch)
564 return remote.changegroup(fetch)
568
565
569 def changegroup(self, basenodes):
566 def changegroup(self, basenodes):
570 nodes = self.newer(basenodes)
567 nodes = self.newer(basenodes)
571
568
572 # construct the link map
569 # construct the link map
573 linkmap = {}
570 linkmap = {}
574 for n in nodes:
571 for n in nodes:
575 linkmap[self.changelog.rev(n)] = n
572 linkmap[self.changelog.rev(n)] = n
576
573
577 # construct a list of all changed files
574 # construct a list of all changed files
578 changed = {}
575 changed = {}
579 for n in nodes:
576 for n in nodes:
580 c = self.changelog.read(n)
577 c = self.changelog.read(n)
581 for f in c[3]:
578 for f in c[3]:
582 changed[f] = 1
579 changed[f] = 1
583 changed = changed.keys()
580 changed = changed.keys()
584 changed.sort()
581 changed.sort()
585
582
586 # the changegroup is changesets + manifests + all file revs
583 # the changegroup is changesets + manifests + all file revs
587 revs = [ self.changelog.rev(n) for n in nodes ]
584 revs = [ self.changelog.rev(n) for n in nodes ]
588
585
589 yield self.changelog.group(linkmap)
586 yield self.changelog.group(linkmap)
590 yield self.manifest.group(linkmap)
587 yield self.manifest.group(linkmap)
591
588
592 for f in changed:
589 for f in changed:
593 g = self.file(f).group(linkmap)
590 g = self.file(f).group(linkmap)
594 if not g: raise "couldn't find change to %s" % f
591 if not g: raise "couldn't find change to %s" % f
595 l = struct.pack(">l", len(f))
592 l = struct.pack(">l", len(f))
596 yield "".join([l, f, g])
593 yield "".join([l, f, g])
597
594
598 def addchangegroup(self, generator):
595 def addchangegroup(self, generator):
599 class genread:
596 class genread:
600 def __init__(self, generator):
597 def __init__(self, generator):
601 self.g = generator
598 self.g = generator
602 self.buf = ""
599 self.buf = ""
603 def read(self, l):
600 def read(self, l):
604 while l > len(self.buf):
601 while l > len(self.buf):
605 try:
602 try:
606 self.buf += self.g.next()
603 self.buf += self.g.next()
607 except StopIteration:
604 except StopIteration:
608 break
605 break
609 d, self.buf = self.buf[:l], self.buf[l:]
606 d, self.buf = self.buf[:l], self.buf[l:]
610 return d
607 return d
611
608
612 if not generator: return
609 if not generator: return
613 source = genread(generator)
610 source = genread(generator)
614
611
615 def getchunk(add = 0):
612 def getchunk(add = 0):
616 d = source.read(4)
613 d = source.read(4)
617 if not d: return ""
614 if not d: return ""
618 l = struct.unpack(">l", d)[0]
615 l = struct.unpack(">l", d)[0]
619 return source.read(l - 4 + add)
616 return source.read(l - 4 + add)
620
617
621 tr = self.transaction()
618 tr = self.transaction()
622 simple = True
619 simple = True
623
620
624 self.ui.status("adding changesets\n")
621 self.ui.status("adding changesets\n")
625 # pull off the changeset group
622 # pull off the changeset group
626 def report(x):
623 def report(x):
627 self.ui.debug("add changeset %s\n" % short(x))
624 self.ui.debug("add changeset %s\n" % short(x))
628 return self.changelog.count()
625 return self.changelog.count()
629
626
630 csg = getchunk()
627 csg = getchunk()
631 co = self.changelog.tip()
628 co = self.changelog.tip()
632 cn = self.changelog.addgroup(csg, report, tr)
629 cn = self.changelog.addgroup(csg, report, tr)
633
630
634 self.ui.status("adding manifests\n")
631 self.ui.status("adding manifests\n")
635 # pull off the manifest group
632 # pull off the manifest group
636 mfg = getchunk()
633 mfg = getchunk()
637 mm = self.manifest.tip()
634 mm = self.manifest.tip()
638 mo = self.manifest.addgroup(mfg, lambda x: self.changelog.rev(x), tr)
635 mo = self.manifest.addgroup(mfg, lambda x: self.changelog.rev(x), tr)
639
636
640 # do we need a resolve?
637 # do we need a resolve?
641 if self.changelog.ancestor(co, cn) != co:
638 if self.changelog.ancestor(co, cn) != co:
642 simple = False
639 simple = False
643 resolverev = self.changelog.count()
640 resolverev = self.changelog.count()
644
641
645 # process the files
642 # process the files
646 self.ui.status("adding files\n")
643 self.ui.status("adding files\n")
647 new = {}
644 new = {}
648 while 1:
645 while 1:
649 f = getchunk(4)
646 f = getchunk(4)
650 if not f: break
647 if not f: break
651 fg = getchunk()
648 fg = getchunk()
652 self.ui.debug("adding %s revisions\n" % f)
649 self.ui.debug("adding %s revisions\n" % f)
653 fl = self.file(f)
650 fl = self.file(f)
654 o = fl.tip()
651 o = fl.tip()
655 n = fl.addgroup(fg, lambda x: self.changelog.rev(x), tr)
652 n = fl.addgroup(fg, lambda x: self.changelog.rev(x), tr)
656 if not simple:
653 if not simple:
657 if o == n: continue
654 if o == n: continue
658 # this file has changed between branches, so it must be
655 # this file has changed between branches, so it must be
659 # represented in the merge changeset
656 # represented in the merge changeset
660 new[f] = self.merge3(fl, f, o, n, tr, resolverev)
657 new[f] = self.merge3(fl, f, o, n, tr, resolverev)
661
658
662 # For simple merges, we don't need to resolve manifests or changesets
659 # For simple merges, we don't need to resolve manifests or changesets
663 if simple:
660 if simple:
664 self.ui.debug("simple merge, skipping resolve\n")
661 self.ui.debug("simple merge, skipping resolve\n")
665 tr.close()
662 tr.close()
666 return
663 return
667
664
668 # resolve the manifest to point to all the merged files
665 # resolve the manifest to point to all the merged files
669 self.ui.status("resolving manifests\n")
666 self.ui.status("resolving manifests\n")
670 ma = self.manifest.ancestor(mm, mo)
667 ma = self.manifest.ancestor(mm, mo)
671 omap = self.manifest.read(mo) # other
668 omap = self.manifest.read(mo) # other
672 amap = self.manifest.read(ma) # ancestor
669 amap = self.manifest.read(ma) # ancestor
673 mmap = self.manifest.read(mm) # mine
670 mmap = self.manifest.read(mm) # mine
674 self.ui.debug("ancestor %s local %s remote %s\n" %
671 self.ui.debug("ancestor %s local %s remote %s\n" %
675 (short(ma), short(mm), short(mo)))
672 (short(ma), short(mm), short(mo)))
676 nmap = {}
673 nmap = {}
677
674
678 for f, mid in mmap.iteritems():
675 for f, mid in mmap.iteritems():
679 if f in omap:
676 if f in omap:
680 if mid != omap[f]:
677 if mid != omap[f]:
681 self.ui.debug("%s versions differ\n" % f)
678 self.ui.debug("%s versions differ\n" % f)
682 if f in new: self.ui.debug("%s updated in resolve\n" % f)
679 if f in new: self.ui.debug("%s updated in resolve\n" % f)
683 # use merged version or local version
680 # use merged version or local version
684 nmap[f] = new.get(f, mid)
681 nmap[f] = new.get(f, mid)
685 else:
682 else:
686 nmap[f] = mid # keep ours
683 nmap[f] = mid # keep ours
687 del omap[f]
684 del omap[f]
688 elif f in amap:
685 elif f in amap:
689 if mid != amap[f]:
686 if mid != amap[f]:
690 r = self.ui.prompt(
687 r = self.ui.prompt(
691 ("local changed %s which remote deleted\n" % f) +
688 ("local changed %s which remote deleted\n" % f) +
692 "(k)eep or (d)elete?", "[kd]", "k")
689 "(k)eep or (d)elete?", "[kd]", "k")
693 if r == "k": nmap[f] = mid
690 if r == "k": nmap[f] = mid
694 else:
691 else:
695 self.ui.debug("other deleted %s\n" % f)
692 self.ui.debug("other deleted %s\n" % f)
696 pass # other deleted it
693 pass # other deleted it
697 else:
694 else:
698 self.ui.debug("local created %s\n" %f)
695 self.ui.debug("local created %s\n" %f)
699 nmap[f] = mid # we created it
696 nmap[f] = mid # we created it
700
697
701 del mmap
698 del mmap
702
699
703 for f, oid in omap.iteritems():
700 for f, oid in omap.iteritems():
704 if f in amap:
701 if f in amap:
705 if oid != amap[f]:
702 if oid != amap[f]:
706 r = self.ui.prompt(
703 r = self.ui.prompt(
707 ("remote changed %s which local deleted\n" % f) +
704 ("remote changed %s which local deleted\n" % f) +
708 "(k)eep or (d)elete?", "[kd]", "k")
705 "(k)eep or (d)elete?", "[kd]", "k")
709 if r == "k": nmap[f] = oid
706 if r == "k": nmap[f] = oid
710 else:
707 else:
711 pass # probably safe
708 pass # probably safe
712 else:
709 else:
713 self.ui.debug("remote created %s\n" % f)
710 self.ui.debug("remote created %s\n" % f)
714 nmap[f] = new.get(f, oid) # remote created it
711 nmap[f] = new.get(f, oid) # remote created it
715
712
716 del omap
713 del omap
717 del amap
714 del amap
718
715
719 node = self.manifest.add(nmap, tr, resolverev, mm, mo)
716 node = self.manifest.add(nmap, tr, resolverev, mm, mo)
720
717
721 # Now all files and manifests are merged, we add the changed files
718 # Now all files and manifests are merged, we add the changed files
722 # and manifest id to the changelog
719 # and manifest id to the changelog
723 self.ui.status("committing merge changeset\n")
720 self.ui.status("committing merge changeset\n")
724 new = new.keys()
721 new = new.keys()
725 new.sort()
722 new.sort()
726 if co == cn: cn = -1
723 if co == cn: cn = -1
727
724
728 edittext = "\nHG: merge resolve\n" + \
725 edittext = "\nHG: merge resolve\n" + \
729 "".join(["HG: changed %s\n" % f for f in new])
726 "".join(["HG: changed %s\n" % f for f in new])
730 edittext = self.ui.edit(edittext)
727 edittext = self.ui.edit(edittext)
731 n = self.changelog.add(node, new, edittext, tr, co, cn)
728 n = self.changelog.add(node, new, edittext, tr, co, cn)
732
729
733 tr.close()
730 tr.close()
734
731
735 def merge3(self, fl, fn, my, other, transaction, link):
732 def merge3(self, fl, fn, my, other, transaction, link):
736 """perform a 3-way merge and append the result"""
733 """perform a 3-way merge and append the result"""
737
734
738 def temp(prefix, node):
735 def temp(prefix, node):
739 pre = "%s~%s." % (os.path.basename(fn), prefix)
736 pre = "%s~%s." % (os.path.basename(fn), prefix)
740 (fd, name) = tempfile.mkstemp("", pre)
737 (fd, name) = tempfile.mkstemp("", pre)
741 f = os.fdopen(fd, "w")
738 f = os.fdopen(fd, "w")
742 f.write(fl.revision(node))
739 f.write(fl.revision(node))
743 f.close()
740 f.close()
744 return name
741 return name
745
742
746 base = fl.ancestor(my, other)
743 base = fl.ancestor(my, other)
747 self.ui.note("resolving %s\n" % fn)
744 self.ui.note("resolving %s\n" % fn)
748 self.ui.debug("local %s remote %s ancestor %s\n" %
745 self.ui.debug("local %s remote %s ancestor %s\n" %
749 (short(my), short(other), short(base)))
746 (short(my), short(other), short(base)))
750
747
751 if my == base:
748 if my == base:
752 text = fl.revision(other)
749 text = fl.revision(other)
753 else:
750 else:
754 a = temp("local", my)
751 a = temp("local", my)
755 b = temp("remote", other)
752 b = temp("remote", other)
756 c = temp("parent", base)
753 c = temp("parent", base)
757
754
758 cmd = os.environ["HGMERGE"]
755 cmd = os.environ["HGMERGE"]
759 self.ui.debug("invoking merge with %s\n" % cmd)
756 self.ui.debug("invoking merge with %s\n" % cmd)
760 r = os.system("%s %s %s %s" % (cmd, a, b, c))
757 r = os.system("%s %s %s %s" % (cmd, a, b, c))
761 if r:
758 if r:
762 raise "Merge failed!"
759 raise "Merge failed!"
763
760
764 text = open(a).read()
761 text = open(a).read()
765 os.unlink(a)
762 os.unlink(a)
766 os.unlink(b)
763 os.unlink(b)
767 os.unlink(c)
764 os.unlink(c)
768
765
769 return fl.addrevision(text, transaction, link, my, other)
766 return fl.addrevision(text, transaction, link, my, other)
770
767
771 class remoterepository:
768 class remoterepository:
772 def __init__(self, ui, path):
769 def __init__(self, ui, path):
773 self.url = path.replace("hg://", "http://", 1)
770 self.url = path.replace("hg://", "http://", 1)
774 self.ui = ui
771 self.ui = ui
775
772
776 def do_cmd(self, cmd, **args):
773 def do_cmd(self, cmd, **args):
777 self.ui.debug("sending %s command\n" % cmd)
774 self.ui.debug("sending %s command\n" % cmd)
778 q = {"cmd": cmd}
775 q = {"cmd": cmd}
779 q.update(args)
776 q.update(args)
780 qs = urllib.urlencode(q)
777 qs = urllib.urlencode(q)
781 cu = "%s?%s" % (self.url, qs)
778 cu = "%s?%s" % (self.url, qs)
782 return urllib.urlopen(cu)
779 return urllib.urlopen(cu)
783
780
784 def branches(self, nodes):
781 def branches(self, nodes):
785 n = " ".join(map(hex, nodes))
782 n = " ".join(map(hex, nodes))
786 d = self.do_cmd("branches", nodes=n).read()
783 d = self.do_cmd("branches", nodes=n).read()
787 br = [ map(bin, b.split(" ")) for b in d.splitlines() ]
784 br = [ map(bin, b.split(" ")) for b in d.splitlines() ]
788 return br
785 return br
789
786
790 def between(self, pairs):
787 def between(self, pairs):
791 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
788 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
792 d = self.do_cmd("between", pairs=n).read()
789 d = self.do_cmd("between", pairs=n).read()
793 p = [ map(bin, l.split(" ")) for l in d.splitlines() ]
790 p = [ map(bin, l.split(" ")) for l in d.splitlines() ]
794 return p
791 return p
795
792
796 def changegroup(self, nodes):
793 def changegroup(self, nodes):
797 n = " ".join(map(hex, nodes))
794 n = " ".join(map(hex, nodes))
798 zd = zlib.decompressobj()
795 zd = zlib.decompressobj()
799 f = self.do_cmd("changegroup", roots=n)
796 f = self.do_cmd("changegroup", roots=n)
800 while 1:
797 while 1:
801 d = f.read(4096)
798 d = f.read(4096)
802 if not d:
799 if not d:
803 yield zd.flush()
800 yield zd.flush()
804 break
801 break
805 yield zd.decompress(d)
802 yield zd.decompress(d)
806
803
807 def repository(ui, path=None, create=0):
804 def repository(ui, path=None, create=0):
808 if path and path[:5] == "hg://":
805 if path and path[:5] == "hg://":
809 return remoterepository(ui, path)
806 return remoterepository(ui, path)
810 else:
807 else:
811 return localrepository(ui, path, create)
808 return localrepository(ui, path, create)
812
809
813 class ui:
810 class ui:
814 def __init__(self, verbose=False, debug=False, quiet=False,
811 def __init__(self, verbose=False, debug=False, quiet=False,
815 interactive=True):
812 interactive=True):
816 self.quiet = quiet and not verbose and not debug
813 self.quiet = quiet and not verbose and not debug
817 self.verbose = verbose or debug
814 self.verbose = verbose or debug
818 self.debugflag = debug
815 self.debugflag = debug
819 self.interactive = interactive
816 self.interactive = interactive
820 def write(self, *args):
817 def write(self, *args):
821 for a in args:
818 for a in args:
822 sys.stdout.write(str(a))
819 sys.stdout.write(str(a))
823 def readline(self):
820 def readline(self):
824 return sys.stdin.readline()[:-1]
821 return sys.stdin.readline()[:-1]
825 def prompt(self, msg, pat, default = "y"):
822 def prompt(self, msg, pat, default = "y"):
826 if not self.interactive: return default
823 if not self.interactive: return default
827 while 1:
824 while 1:
828 self.write(msg, " ")
825 self.write(msg, " ")
829 r = self.readline()
826 r = self.readline()
830 if re.match(pat, r):
827 if re.match(pat, r):
831 return r
828 return r
832 else:
829 else:
833 self.write("unrecognized response\n")
830 self.write("unrecognized response\n")
834 def status(self, *msg):
831 def status(self, *msg):
835 if not self.quiet: self.write(*msg)
832 if not self.quiet: self.write(*msg)
836 def warn(self, msg):
833 def warn(self, msg):
837 self.write(*msg)
834 self.write(*msg)
838 def note(self, *msg):
835 def note(self, *msg):
839 if self.verbose: self.write(*msg)
836 if self.verbose: self.write(*msg)
840 def debug(self, *msg):
837 def debug(self, *msg):
841 if self.debugflag: self.write(*msg)
838 if self.debugflag: self.write(*msg)
842 def edit(self, text):
839 def edit(self, text):
843 (fd, name) = tempfile.mkstemp("hg")
840 (fd, name) = tempfile.mkstemp("hg")
844 f = os.fdopen(fd, "w")
841 f = os.fdopen(fd, "w")
845 f.write(text)
842 f.write(text)
846 f.close()
843 f.close()
847
844
848 editor = os.environ.get("EDITOR", "vi")
845 editor = os.environ.get("EDITOR", "vi")
849 r = os.system("%s %s" % (editor, name))
846 r = os.system("%s %s" % (editor, name))
850 if r:
847 if r:
851 raise "Edit failed!"
848 raise "Edit failed!"
852
849
853 t = open(name).read()
850 t = open(name).read()
854 t = re.sub("(?m)^HG:.*\n", "", t)
851 t = re.sub("(?m)^HG:.*\n", "", t)
855
852
856 return t
853 return t
857
854
858
855
859 class httprangereader:
856 class httprangereader:
860 def __init__(self, url):
857 def __init__(self, url):
861 self.url = url
858 self.url = url
862 self.pos = 0
859 self.pos = 0
863 def seek(self, pos):
860 def seek(self, pos):
864 self.pos = pos
861 self.pos = pos
865 def read(self, bytes=None):
862 def read(self, bytes=None):
866 opener = urllib2.build_opener(byterange.HTTPRangeHandler())
863 opener = urllib2.build_opener(byterange.HTTPRangeHandler())
867 urllib2.install_opener(opener)
864 urllib2.install_opener(opener)
868 req = urllib2.Request(self.url)
865 req = urllib2.Request(self.url)
869 end = ''
866 end = ''
870 if bytes: end = self.pos + bytes
867 if bytes: end = self.pos + bytes
871 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
868 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
872 f = urllib2.urlopen(req)
869 f = urllib2.urlopen(req)
873 return f.read()
870 return f.read()
General Comments 0
You need to be logged in to leave comments. Login now