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