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