##// END OF EJS Templates
Bail on attempts to do an empty commit
mpm@selenic.com -
r151:1f6c61a6 default
parent child Browse files
Show More
@@ -1,892 +1,896 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()
302
303 try:
301 try:
304 remove = [ l[:-1] for l in self.opener("to-remove") ]
302 remove = [ l[:-1] for l in self.opener("to-remove") ]
305 os.unlink(self.join("to-remove"))
303 os.unlink(self.join("to-remove"))
306
304
307 except IOError:
305 except IOError:
308 remove = []
306 remove = []
309
307
310 if update == None:
308 if update == None:
311 update = self.diffdir(self.root, parent)[0]
309 update = self.diffdir(self.root, parent)[0]
312
310
311 if not update:
312 self.ui.status("nothing changed\n")
313 return
314
315 tr = self.transaction()
316
313 # check in files
317 # check in files
314 new = {}
318 new = {}
315 linkrev = self.changelog.count()
319 linkrev = self.changelog.count()
316 update.sort()
320 update.sort()
317 for f in update:
321 for f in update:
318 self.ui.note(f + "\n")
322 self.ui.note(f + "\n")
319 try:
323 try:
320 t = file(f).read()
324 t = file(f).read()
321 except IOError:
325 except IOError:
322 remove.append(f)
326 remove.append(f)
323 continue
327 continue
324 r = self.file(f)
328 r = self.file(f)
325 new[f] = r.add(t, tr, linkrev)
329 new[f] = r.add(t, tr, linkrev)
326
330
327 # update manifest
331 # update manifest
328 mmap = self.manifest.read(self.manifest.tip())
332 mmap = self.manifest.read(self.manifest.tip())
329 mmap.update(new)
333 mmap.update(new)
330 for f in remove:
334 for f in remove:
331 del mmap[f]
335 del mmap[f]
332 mnode = self.manifest.add(mmap, tr, linkrev)
336 mnode = self.manifest.add(mmap, tr, linkrev)
333
337
334 # add changeset
338 # add changeset
335 new = new.keys()
339 new = new.keys()
336 new.sort()
340 new.sort()
337
341
338 edittext = text + "\n"+"".join(["HG: changed %s\n" % f for f in new])
342 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])
343 edittext += "".join(["HG: removed %s\n" % f for f in remove])
340 edittext = self.ui.edit(edittext)
344 edittext = self.ui.edit(edittext)
341
345
342 n = self.changelog.add(mnode, new, edittext, tr)
346 n = self.changelog.add(mnode, new, edittext, tr)
343 tr.close()
347 tr.close()
344
348
345 self.setcurrent(n)
349 self.setcurrent(n)
346 self.dircache.update(new)
350 self.dircache.update(new)
347 self.dircache.remove(remove)
351 self.dircache.remove(remove)
348
352
349 def checkout(self, node):
353 def checkout(self, node):
350 # checkout is really dumb at the moment
354 # checkout is really dumb at the moment
351 # it ought to basically merge
355 # it ought to basically merge
352 change = self.changelog.read(node)
356 change = self.changelog.read(node)
353 l = self.manifest.read(change[0]).items()
357 l = self.manifest.read(change[0]).items()
354 l.sort()
358 l.sort()
355
359
356 for f,n in l:
360 for f,n in l:
357 if f[0] == "/": continue
361 if f[0] == "/": continue
358 self.ui.note(f, "\n")
362 self.ui.note(f, "\n")
359 t = self.file(f).revision(n)
363 t = self.file(f).revision(n)
360 try:
364 try:
361 file(f, "w").write(t)
365 file(f, "w").write(t)
362 except IOError:
366 except IOError:
363 os.makedirs(os.path.dirname(f))
367 os.makedirs(os.path.dirname(f))
364 file(f, "w").write(t)
368 file(f, "w").write(t)
365
369
366 self.setcurrent(node)
370 self.setcurrent(node)
367 self.dircache.clear()
371 self.dircache.clear()
368 self.dircache.update([f for f,n in l])
372 self.dircache.update([f for f,n in l])
369
373
370 def diffdir(self, path, changeset):
374 def diffdir(self, path, changeset):
371 changed = []
375 changed = []
372 mf = {}
376 mf = {}
373 added = []
377 added = []
374
378
375 if changeset:
379 if changeset:
376 change = self.changelog.read(changeset)
380 change = self.changelog.read(changeset)
377 mf = self.manifest.read(change[0])
381 mf = self.manifest.read(change[0])
378
382
379 if changeset == self.current:
383 if changeset == self.current:
380 dc = self.dircache.copy()
384 dc = self.dircache.copy()
381 else:
385 else:
382 dc = dict.fromkeys(mf)
386 dc = dict.fromkeys(mf)
383
387
384 def fcmp(fn):
388 def fcmp(fn):
385 t1 = file(os.path.join(self.root, fn)).read()
389 t1 = file(os.path.join(self.root, fn)).read()
386 t2 = self.file(fn).revision(mf[fn])
390 t2 = self.file(fn).revision(mf[fn])
387 return cmp(t1, t2)
391 return cmp(t1, t2)
388
392
389 for dir, subdirs, files in os.walk(self.root):
393 for dir, subdirs, files in os.walk(self.root):
390 d = dir[len(self.root)+1:]
394 d = dir[len(self.root)+1:]
391 if ".hg" in subdirs: subdirs.remove(".hg")
395 if ".hg" in subdirs: subdirs.remove(".hg")
392
396
393 for f in files:
397 for f in files:
394 fn = os.path.join(d, f)
398 fn = os.path.join(d, f)
395 try: s = os.stat(os.path.join(self.root, fn))
399 try: s = os.stat(os.path.join(self.root, fn))
396 except: continue
400 except: continue
397 if fn in dc:
401 if fn in dc:
398 c = dc[fn]
402 c = dc[fn]
399 del dc[fn]
403 del dc[fn]
400 if not c:
404 if not c:
401 if fcmp(fn):
405 if fcmp(fn):
402 changed.append(fn)
406 changed.append(fn)
403 elif c[1] != s.st_size:
407 elif c[1] != s.st_size:
404 changed.append(fn)
408 changed.append(fn)
405 elif c[0] != s.st_mode or c[2] != s.st_mtime:
409 elif c[0] != s.st_mode or c[2] != s.st_mtime:
406 if fcmp(fn):
410 if fcmp(fn):
407 changed.append(fn)
411 changed.append(fn)
408 else:
412 else:
409 if self.ignore(fn): continue
413 if self.ignore(fn): continue
410 added.append(fn)
414 added.append(fn)
411
415
412 deleted = dc.keys()
416 deleted = dc.keys()
413 deleted.sort()
417 deleted.sort()
414
418
415 return (changed, added, deleted)
419 return (changed, added, deleted)
416
420
417 def diffrevs(self, node1, node2):
421 def diffrevs(self, node1, node2):
418 changed, added = [], []
422 changed, added = [], []
419
423
420 change = self.changelog.read(node1)
424 change = self.changelog.read(node1)
421 mf1 = self.manifest.read(change[0])
425 mf1 = self.manifest.read(change[0])
422 change = self.changelog.read(node2)
426 change = self.changelog.read(node2)
423 mf2 = self.manifest.read(change[0])
427 mf2 = self.manifest.read(change[0])
424
428
425 for fn in mf2:
429 for fn in mf2:
426 if mf1.has_key(fn):
430 if mf1.has_key(fn):
427 if mf1[fn] != mf2[fn]:
431 if mf1[fn] != mf2[fn]:
428 changed.append(fn)
432 changed.append(fn)
429 del mf1[fn]
433 del mf1[fn]
430 else:
434 else:
431 added.append(fn)
435 added.append(fn)
432
436
433 deleted = mf1.keys()
437 deleted = mf1.keys()
434 deleted.sort()
438 deleted.sort()
435
439
436 return (changed, added, deleted)
440 return (changed, added, deleted)
437
441
438 def add(self, list):
442 def add(self, list):
439 self.dircache.taint(list)
443 self.dircache.taint(list)
440
444
441 def remove(self, list):
445 def remove(self, list):
442 dl = self.opener("to-remove", "a")
446 dl = self.opener("to-remove", "a")
443 for f in list:
447 for f in list:
444 dl.write(f + "\n")
448 dl.write(f + "\n")
445
449
446 def branches(self, nodes):
450 def branches(self, nodes):
447 if not nodes: nodes = [self.changelog.tip()]
451 if not nodes: nodes = [self.changelog.tip()]
448 b = []
452 b = []
449 for n in nodes:
453 for n in nodes:
450 t = n
454 t = n
451 while n:
455 while n:
452 p = self.changelog.parents(n)
456 p = self.changelog.parents(n)
453 if p[1] != nullid or p[0] == nullid:
457 if p[1] != nullid or p[0] == nullid:
454 b.append((t, n, p[0], p[1]))
458 b.append((t, n, p[0], p[1]))
455 break
459 break
456 n = p[0]
460 n = p[0]
457 return b
461 return b
458
462
459 def between(self, pairs):
463 def between(self, pairs):
460 r = []
464 r = []
461
465
462 for top, bottom in pairs:
466 for top, bottom in pairs:
463 n, l, i = top, [], 0
467 n, l, i = top, [], 0
464 f = 1
468 f = 1
465
469
466 while n != bottom:
470 while n != bottom:
467 p = self.changelog.parents(n)[0]
471 p = self.changelog.parents(n)[0]
468 if i == f:
472 if i == f:
469 l.append(n)
473 l.append(n)
470 f = f * 2
474 f = f * 2
471 n = p
475 n = p
472 i += 1
476 i += 1
473
477
474 r.append(l)
478 r.append(l)
475
479
476 return r
480 return r
477
481
478 def newer(self, nodes):
482 def newer(self, nodes):
479 m = {}
483 m = {}
480 nl = []
484 nl = []
481 pm = {}
485 pm = {}
482 cl = self.changelog
486 cl = self.changelog
483 t = l = cl.count()
487 t = l = cl.count()
484
488
485 # find the lowest numbered node
489 # find the lowest numbered node
486 for n in nodes:
490 for n in nodes:
487 l = min(l, cl.rev(n))
491 l = min(l, cl.rev(n))
488 m[n] = 1
492 m[n] = 1
489
493
490 for i in xrange(l, t):
494 for i in xrange(l, t):
491 n = cl.node(i)
495 n = cl.node(i)
492 if n in m: # explicitly listed
496 if n in m: # explicitly listed
493 pm[n] = 1
497 pm[n] = 1
494 nl.append(n)
498 nl.append(n)
495 continue
499 continue
496 for p in cl.parents(n):
500 for p in cl.parents(n):
497 if p in pm: # parent listed
501 if p in pm: # parent listed
498 pm[n] = 1
502 pm[n] = 1
499 nl.append(n)
503 nl.append(n)
500 break
504 break
501
505
502 return nl
506 return nl
503
507
504 def getchangegroup(self, remote):
508 def getchangegroup(self, remote):
505 tip = remote.branches([])[0]
509 tip = remote.branches([])[0]
506 self.ui.debug("remote tip branch is %s:%s\n" %
510 self.ui.debug("remote tip branch is %s:%s\n" %
507 (short(tip[0]), short(tip[1])))
511 (short(tip[0]), short(tip[1])))
508 m = self.changelog.nodemap
512 m = self.changelog.nodemap
509 unknown = [tip]
513 unknown = [tip]
510 search = []
514 search = []
511 fetch = []
515 fetch = []
512 seen = {}
516 seen = {}
513 seenbranch = {}
517 seenbranch = {}
514
518
515 if tip[0] in m:
519 if tip[0] in m:
516 self.ui.note("nothing to do!\n")
520 self.ui.note("nothing to do!\n")
517 return None
521 return None
518
522
519 while unknown:
523 while unknown:
520 n = unknown.pop(0)
524 n = unknown.pop(0)
521 seen[n[0]] = 1
525 seen[n[0]] = 1
522
526
523 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
527 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
524 if n == nullid: break
528 if n == nullid: break
525 if n in seenbranch:
529 if n in seenbranch:
526 self.ui.debug("branch already found\n")
530 self.ui.debug("branch already found\n")
527 continue
531 continue
528 if n[1] and n[1] in m: # do we know the base?
532 if n[1] and n[1] in m: # do we know the base?
529 self.ui.debug("found incomplete branch %s:%s\n"
533 self.ui.debug("found incomplete branch %s:%s\n"
530 % (short(n[0]), short(n[1])))
534 % (short(n[0]), short(n[1])))
531 search.append(n) # schedule branch range for scanning
535 search.append(n) # schedule branch range for scanning
532 seenbranch[n] = 1
536 seenbranch[n] = 1
533 else:
537 else:
534 if n[2] in m and n[3] in m:
538 if n[2] in m and n[3] in m:
535 if n[1] not in fetch:
539 if n[1] not in fetch:
536 self.ui.debug("found new changeset %s\n" %
540 self.ui.debug("found new changeset %s\n" %
537 short(n[1]))
541 short(n[1]))
538 fetch.append(n[1]) # earliest unknown
542 fetch.append(n[1]) # earliest unknown
539 continue
543 continue
540
544
541 r = []
545 r = []
542 for a in n[2:4]:
546 for a in n[2:4]:
543 if a not in seen: r.append(a)
547 if a not in seen: r.append(a)
544
548
545 if r:
549 if r:
546 self.ui.debug("requesting %s\n" %
550 self.ui.debug("requesting %s\n" %
547 " ".join(map(short, r)))
551 " ".join(map(short, r)))
548 for b in remote.branches(r):
552 for b in remote.branches(r):
549 self.ui.debug("received %s:%s\n" %
553 self.ui.debug("received %s:%s\n" %
550 (short(b[0]), short(b[1])))
554 (short(b[0]), short(b[1])))
551 if b[0] not in m and b[0] not in seen:
555 if b[0] not in m and b[0] not in seen:
552 unknown.append(b)
556 unknown.append(b)
553
557
554 while search:
558 while search:
555 n = search.pop(0)
559 n = search.pop(0)
556 l = remote.between([(n[0], n[1])])[0]
560 l = remote.between([(n[0], n[1])])[0]
557 p = n[0]
561 p = n[0]
558 f = 1
562 f = 1
559 for i in l + [n[1]]:
563 for i in l + [n[1]]:
560 if i in m:
564 if i in m:
561 if f <= 2:
565 if f <= 2:
562 self.ui.debug("found new branch changeset %s\n" %
566 self.ui.debug("found new branch changeset %s\n" %
563 short(p))
567 short(p))
564 fetch.append(p)
568 fetch.append(p)
565 else:
569 else:
566 self.ui.debug("narrowed branch search to %s:%s\n"
570 self.ui.debug("narrowed branch search to %s:%s\n"
567 % (short(p), short(i)))
571 % (short(p), short(i)))
568 search.append((p, i))
572 search.append((p, i))
569 break
573 break
570 p, f = i, f * 2
574 p, f = i, f * 2
571
575
572 for f in fetch:
576 for f in fetch:
573 if f in m:
577 if f in m:
574 raise "already have", short(f[:4])
578 raise "already have", short(f[:4])
575
579
576 self.ui.note("adding new changesets starting at " +
580 self.ui.note("adding new changesets starting at " +
577 " ".join([short(f) for f in fetch]) + "\n")
581 " ".join([short(f) for f in fetch]) + "\n")
578
582
579 return remote.changegroup(fetch)
583 return remote.changegroup(fetch)
580
584
581 def changegroup(self, basenodes):
585 def changegroup(self, basenodes):
582 nodes = self.newer(basenodes)
586 nodes = self.newer(basenodes)
583
587
584 # construct the link map
588 # construct the link map
585 linkmap = {}
589 linkmap = {}
586 for n in nodes:
590 for n in nodes:
587 linkmap[self.changelog.rev(n)] = n
591 linkmap[self.changelog.rev(n)] = n
588
592
589 # construct a list of all changed files
593 # construct a list of all changed files
590 changed = {}
594 changed = {}
591 for n in nodes:
595 for n in nodes:
592 c = self.changelog.read(n)
596 c = self.changelog.read(n)
593 for f in c[3]:
597 for f in c[3]:
594 changed[f] = 1
598 changed[f] = 1
595 changed = changed.keys()
599 changed = changed.keys()
596 changed.sort()
600 changed.sort()
597
601
598 # the changegroup is changesets + manifests + all file revs
602 # the changegroup is changesets + manifests + all file revs
599 revs = [ self.changelog.rev(n) for n in nodes ]
603 revs = [ self.changelog.rev(n) for n in nodes ]
600
604
601 yield self.changelog.group(linkmap)
605 yield self.changelog.group(linkmap)
602 yield self.manifest.group(linkmap)
606 yield self.manifest.group(linkmap)
603
607
604 for f in changed:
608 for f in changed:
605 g = self.file(f).group(linkmap)
609 g = self.file(f).group(linkmap)
606 if not g: raise "couldn't find change to %s" % f
610 if not g: raise "couldn't find change to %s" % f
607 l = struct.pack(">l", len(f))
611 l = struct.pack(">l", len(f))
608 yield "".join([l, f, g])
612 yield "".join([l, f, g])
609
613
610 def addchangegroup(self, generator):
614 def addchangegroup(self, generator):
611 class genread:
615 class genread:
612 def __init__(self, generator):
616 def __init__(self, generator):
613 self.g = generator
617 self.g = generator
614 self.buf = ""
618 self.buf = ""
615 def read(self, l):
619 def read(self, l):
616 while l > len(self.buf):
620 while l > len(self.buf):
617 try:
621 try:
618 self.buf += self.g.next()
622 self.buf += self.g.next()
619 except StopIteration:
623 except StopIteration:
620 break
624 break
621 d, self.buf = self.buf[:l], self.buf[l:]
625 d, self.buf = self.buf[:l], self.buf[l:]
622 return d
626 return d
623
627
624 if not generator: return
628 if not generator: return
625 source = genread(generator)
629 source = genread(generator)
626
630
627 def getchunk(add = 0):
631 def getchunk(add = 0):
628 d = source.read(4)
632 d = source.read(4)
629 if not d: return ""
633 if not d: return ""
630 l = struct.unpack(">l", d)[0]
634 l = struct.unpack(">l", d)[0]
631 return source.read(l - 4 + add)
635 return source.read(l - 4 + add)
632
636
633 tr = self.transaction()
637 tr = self.transaction()
634 simple = True
638 simple = True
635 need = {}
639 need = {}
636
640
637 self.ui.status("adding changesets\n")
641 self.ui.status("adding changesets\n")
638 # pull off the changeset group
642 # pull off the changeset group
639 def report(x):
643 def report(x):
640 self.ui.debug("add changeset %s\n" % short(x))
644 self.ui.debug("add changeset %s\n" % short(x))
641 return self.changelog.count()
645 return self.changelog.count()
642
646
643 csg = getchunk()
647 csg = getchunk()
644 co = self.changelog.tip()
648 co = self.changelog.tip()
645 cn = self.changelog.addgroup(csg, report, tr)
649 cn = self.changelog.addgroup(csg, report, tr)
646
650
647 self.ui.status("adding manifests\n")
651 self.ui.status("adding manifests\n")
648 # pull off the manifest group
652 # pull off the manifest group
649 mfg = getchunk()
653 mfg = getchunk()
650 mm = self.manifest.tip()
654 mm = self.manifest.tip()
651 mo = self.manifest.addgroup(mfg, lambda x: self.changelog.rev(x), tr)
655 mo = self.manifest.addgroup(mfg, lambda x: self.changelog.rev(x), tr)
652
656
653 # do we need a resolve?
657 # do we need a resolve?
654 if self.changelog.ancestor(co, cn) != co:
658 if self.changelog.ancestor(co, cn) != co:
655 simple = False
659 simple = False
656 resolverev = self.changelog.count()
660 resolverev = self.changelog.count()
657
661
658 # resolve the manifest to determine which files
662 # resolve the manifest to determine which files
659 # we care about merging
663 # we care about merging
660 self.ui.status("resolving manifests\n")
664 self.ui.status("resolving manifests\n")
661 ma = self.manifest.ancestor(mm, mo)
665 ma = self.manifest.ancestor(mm, mo)
662 omap = self.manifest.read(mo) # other
666 omap = self.manifest.read(mo) # other
663 amap = self.manifest.read(ma) # ancestor
667 amap = self.manifest.read(ma) # ancestor
664 mmap = self.manifest.read(mm) # mine
668 mmap = self.manifest.read(mm) # mine
665 nmap = {}
669 nmap = {}
666
670
667 self.ui.debug(" ancestor %s local %s remote %s\n" %
671 self.ui.debug(" ancestor %s local %s remote %s\n" %
668 (short(ma), short(mm), short(mo)))
672 (short(ma), short(mm), short(mo)))
669
673
670 for f, mid in mmap.iteritems():
674 for f, mid in mmap.iteritems():
671 if f in omap:
675 if f in omap:
672 if mid != omap[f]:
676 if mid != omap[f]:
673 self.ui.debug(" %s versions differ, do resolve\n" % f)
677 self.ui.debug(" %s versions differ, do resolve\n" % f)
674 need[f] = mid # use merged version or local version
678 need[f] = mid # use merged version or local version
675 else:
679 else:
676 nmap[f] = mid # keep ours
680 nmap[f] = mid # keep ours
677 del omap[f]
681 del omap[f]
678 elif f in amap:
682 elif f in amap:
679 if mid != amap[f]:
683 if mid != amap[f]:
680 r = self.ui.prompt(
684 r = self.ui.prompt(
681 (" local changed %s which remote deleted\n" % f) +
685 (" local changed %s which remote deleted\n" % f) +
682 "(k)eep or (d)elete?", "[kd]", "k")
686 "(k)eep or (d)elete?", "[kd]", "k")
683 if r == "k": nmap[f] = mid
687 if r == "k": nmap[f] = mid
684 else:
688 else:
685 self.ui.debug("other deleted %s\n" % f)
689 self.ui.debug("other deleted %s\n" % f)
686 pass # other deleted it
690 pass # other deleted it
687 else:
691 else:
688 self.ui.debug("local created %s\n" %f)
692 self.ui.debug("local created %s\n" %f)
689 nmap[f] = mid # we created it
693 nmap[f] = mid # we created it
690
694
691 del mmap
695 del mmap
692
696
693 for f, oid in omap.iteritems():
697 for f, oid in omap.iteritems():
694 if f in amap:
698 if f in amap:
695 if oid != amap[f]:
699 if oid != amap[f]:
696 r = self.ui.prompt(
700 r = self.ui.prompt(
697 ("remote changed %s which local deleted\n" % f) +
701 ("remote changed %s which local deleted\n" % f) +
698 "(k)eep or (d)elete?", "[kd]", "k")
702 "(k)eep or (d)elete?", "[kd]", "k")
699 if r == "k": nmap[f] = oid
703 if r == "k": nmap[f] = oid
700 else:
704 else:
701 pass # probably safe
705 pass # probably safe
702 else:
706 else:
703 self.ui.debug("remote created %s, do resolve\n" % f)
707 self.ui.debug("remote created %s, do resolve\n" % f)
704 need[f] = oid
708 need[f] = oid
705
709
706 del omap
710 del omap
707 del amap
711 del amap
708
712
709 new = need.keys()
713 new = need.keys()
710 new.sort()
714 new.sort()
711
715
712 # process the files
716 # process the files
713 self.ui.status("adding files\n")
717 self.ui.status("adding files\n")
714 while 1:
718 while 1:
715 f = getchunk(4)
719 f = getchunk(4)
716 if not f: break
720 if not f: break
717 fg = getchunk()
721 fg = getchunk()
718 self.ui.debug("adding %s revisions\n" % f)
722 self.ui.debug("adding %s revisions\n" % f)
719 fl = self.file(f)
723 fl = self.file(f)
720 o = fl.tip()
724 o = fl.tip()
721 n = fl.addgroup(fg, lambda x: self.changelog.rev(x), tr)
725 n = fl.addgroup(fg, lambda x: self.changelog.rev(x), tr)
722 if f in need:
726 if f in need:
723 del need[f]
727 del need[f]
724 # manifest resolve determined we need to merge the tips
728 # manifest resolve determined we need to merge the tips
725 nmap[f] = self.merge3(fl, f, o, n, tr, resolverev)
729 nmap[f] = self.merge3(fl, f, o, n, tr, resolverev)
726
730
727 if need:
731 if need:
728 # we need to do trivial merges on local files
732 # we need to do trivial merges on local files
729 for f in new:
733 for f in new:
730 if f not in need: continue
734 if f not in need: continue
731 fl = self.file(f)
735 fl = self.file(f)
732 nmap[f] = self.merge3(fl, f, need[f], fl.tip(), tr, resolverev)
736 nmap[f] = self.merge3(fl, f, need[f], fl.tip(), tr, resolverev)
733
737
734 # For simple merges, we don't need to resolve manifests or changesets
738 # For simple merges, we don't need to resolve manifests or changesets
735 if simple:
739 if simple:
736 self.ui.debug("simple merge, skipping resolve\n")
740 self.ui.debug("simple merge, skipping resolve\n")
737 tr.close()
741 tr.close()
738 return
742 return
739
743
740 node = self.manifest.add(nmap, tr, resolverev, mm, mo)
744 node = self.manifest.add(nmap, tr, resolverev, mm, mo)
741
745
742 # Now all files and manifests are merged, we add the changed files
746 # Now all files and manifests are merged, we add the changed files
743 # and manifest id to the changelog
747 # and manifest id to the changelog
744 self.ui.status("committing merge changeset\n")
748 self.ui.status("committing merge changeset\n")
745 if co == cn: cn = -1
749 if co == cn: cn = -1
746
750
747 edittext = "\nHG: merge resolve\n" + \
751 edittext = "\nHG: merge resolve\n" + \
748 "".join(["HG: changed %s\n" % f for f in new])
752 "".join(["HG: changed %s\n" % f for f in new])
749 edittext = self.ui.edit(edittext)
753 edittext = self.ui.edit(edittext)
750 n = self.changelog.add(node, new, edittext, tr, co, cn)
754 n = self.changelog.add(node, new, edittext, tr, co, cn)
751
755
752 tr.close()
756 tr.close()
753
757
754 def merge3(self, fl, fn, my, other, transaction, link):
758 def merge3(self, fl, fn, my, other, transaction, link):
755 """perform a 3-way merge and append the result"""
759 """perform a 3-way merge and append the result"""
756
760
757 def temp(prefix, node):
761 def temp(prefix, node):
758 pre = "%s~%s." % (os.path.basename(fn), prefix)
762 pre = "%s~%s." % (os.path.basename(fn), prefix)
759 (fd, name) = tempfile.mkstemp("", pre)
763 (fd, name) = tempfile.mkstemp("", pre)
760 f = os.fdopen(fd, "w")
764 f = os.fdopen(fd, "w")
761 f.write(fl.revision(node))
765 f.write(fl.revision(node))
762 f.close()
766 f.close()
763 return name
767 return name
764
768
765 base = fl.ancestor(my, other)
769 base = fl.ancestor(my, other)
766 self.ui.note("resolving %s\n" % fn)
770 self.ui.note("resolving %s\n" % fn)
767 self.ui.debug("local %s remote %s ancestor %s\n" %
771 self.ui.debug("local %s remote %s ancestor %s\n" %
768 (short(my), short(other), short(base)))
772 (short(my), short(other), short(base)))
769
773
770 if my == base:
774 if my == base:
771 text = fl.revision(other)
775 text = fl.revision(other)
772 else:
776 else:
773 a = temp("local", my)
777 a = temp("local", my)
774 b = temp("remote", other)
778 b = temp("remote", other)
775 c = temp("parent", base)
779 c = temp("parent", base)
776
780
777 cmd = os.environ["HGMERGE"]
781 cmd = os.environ["HGMERGE"]
778 self.ui.debug("invoking merge with %s\n" % cmd)
782 self.ui.debug("invoking merge with %s\n" % cmd)
779 r = os.system("%s %s %s %s %s" % (cmd, a, b, c, fn))
783 r = os.system("%s %s %s %s %s" % (cmd, a, b, c, fn))
780 if r:
784 if r:
781 raise "Merge failed!"
785 raise "Merge failed!"
782
786
783 text = open(a).read()
787 text = open(a).read()
784 os.unlink(a)
788 os.unlink(a)
785 os.unlink(b)
789 os.unlink(b)
786 os.unlink(c)
790 os.unlink(c)
787
791
788 return fl.addrevision(text, transaction, link, my, other)
792 return fl.addrevision(text, transaction, link, my, other)
789
793
790 class remoterepository:
794 class remoterepository:
791 def __init__(self, ui, path):
795 def __init__(self, ui, path):
792 self.url = path.replace("hg://", "http://", 1)
796 self.url = path.replace("hg://", "http://", 1)
793 self.ui = ui
797 self.ui = ui
794
798
795 def do_cmd(self, cmd, **args):
799 def do_cmd(self, cmd, **args):
796 self.ui.debug("sending %s command\n" % cmd)
800 self.ui.debug("sending %s command\n" % cmd)
797 q = {"cmd": cmd}
801 q = {"cmd": cmd}
798 q.update(args)
802 q.update(args)
799 qs = urllib.urlencode(q)
803 qs = urllib.urlencode(q)
800 cu = "%s?%s" % (self.url, qs)
804 cu = "%s?%s" % (self.url, qs)
801 return urllib.urlopen(cu)
805 return urllib.urlopen(cu)
802
806
803 def branches(self, nodes):
807 def branches(self, nodes):
804 n = " ".join(map(hex, nodes))
808 n = " ".join(map(hex, nodes))
805 d = self.do_cmd("branches", nodes=n).read()
809 d = self.do_cmd("branches", nodes=n).read()
806 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
810 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
807 return br
811 return br
808
812
809 def between(self, pairs):
813 def between(self, pairs):
810 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
814 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
811 d = self.do_cmd("between", pairs=n).read()
815 d = self.do_cmd("between", pairs=n).read()
812 p = [ map(bin, l.split(" ")) for l in d.splitlines() ]
816 p = [ map(bin, l.split(" ")) for l in d.splitlines() ]
813 return p
817 return p
814
818
815 def changegroup(self, nodes):
819 def changegroup(self, nodes):
816 n = " ".join(map(hex, nodes))
820 n = " ".join(map(hex, nodes))
817 zd = zlib.decompressobj()
821 zd = zlib.decompressobj()
818 f = self.do_cmd("changegroup", roots=n)
822 f = self.do_cmd("changegroup", roots=n)
819 while 1:
823 while 1:
820 d = f.read(4096)
824 d = f.read(4096)
821 if not d:
825 if not d:
822 yield zd.flush()
826 yield zd.flush()
823 break
827 break
824 yield zd.decompress(d)
828 yield zd.decompress(d)
825
829
826 def repository(ui, path=None, create=0):
830 def repository(ui, path=None, create=0):
827 if path and path[:5] == "hg://":
831 if path and path[:5] == "hg://":
828 return remoterepository(ui, path)
832 return remoterepository(ui, path)
829 else:
833 else:
830 return localrepository(ui, path, create)
834 return localrepository(ui, path, create)
831
835
832 class ui:
836 class ui:
833 def __init__(self, verbose=False, debug=False, quiet=False,
837 def __init__(self, verbose=False, debug=False, quiet=False,
834 interactive=True):
838 interactive=True):
835 self.quiet = quiet and not verbose and not debug
839 self.quiet = quiet and not verbose and not debug
836 self.verbose = verbose or debug
840 self.verbose = verbose or debug
837 self.debugflag = debug
841 self.debugflag = debug
838 self.interactive = interactive
842 self.interactive = interactive
839 def write(self, *args):
843 def write(self, *args):
840 for a in args:
844 for a in args:
841 sys.stdout.write(str(a))
845 sys.stdout.write(str(a))
842 def readline(self):
846 def readline(self):
843 return sys.stdin.readline()[:-1]
847 return sys.stdin.readline()[:-1]
844 def prompt(self, msg, pat, default = "y"):
848 def prompt(self, msg, pat, default = "y"):
845 if not self.interactive: return default
849 if not self.interactive: return default
846 while 1:
850 while 1:
847 self.write(msg, " ")
851 self.write(msg, " ")
848 r = self.readline()
852 r = self.readline()
849 if re.match(pat, r):
853 if re.match(pat, r):
850 return r
854 return r
851 else:
855 else:
852 self.write("unrecognized response\n")
856 self.write("unrecognized response\n")
853 def status(self, *msg):
857 def status(self, *msg):
854 if not self.quiet: self.write(*msg)
858 if not self.quiet: self.write(*msg)
855 def warn(self, msg):
859 def warn(self, msg):
856 self.write(*msg)
860 self.write(*msg)
857 def note(self, *msg):
861 def note(self, *msg):
858 if self.verbose: self.write(*msg)
862 if self.verbose: self.write(*msg)
859 def debug(self, *msg):
863 def debug(self, *msg):
860 if self.debugflag: self.write(*msg)
864 if self.debugflag: self.write(*msg)
861 def edit(self, text):
865 def edit(self, text):
862 (fd, name) = tempfile.mkstemp("hg")
866 (fd, name) = tempfile.mkstemp("hg")
863 f = os.fdopen(fd, "w")
867 f = os.fdopen(fd, "w")
864 f.write(text)
868 f.write(text)
865 f.close()
869 f.close()
866
870
867 editor = os.environ.get("EDITOR", "vi")
871 editor = os.environ.get("EDITOR", "vi")
868 r = os.system("%s %s" % (editor, name))
872 r = os.system("%s %s" % (editor, name))
869 if r:
873 if r:
870 raise "Edit failed!"
874 raise "Edit failed!"
871
875
872 t = open(name).read()
876 t = open(name).read()
873 t = re.sub("(?m)^HG:.*\n", "", t)
877 t = re.sub("(?m)^HG:.*\n", "", t)
874
878
875 return t
879 return t
876
880
877
881
878 class httprangereader:
882 class httprangereader:
879 def __init__(self, url):
883 def __init__(self, url):
880 self.url = url
884 self.url = url
881 self.pos = 0
885 self.pos = 0
882 def seek(self, pos):
886 def seek(self, pos):
883 self.pos = pos
887 self.pos = pos
884 def read(self, bytes=None):
888 def read(self, bytes=None):
885 opener = urllib2.build_opener(byterange.HTTPRangeHandler())
889 opener = urllib2.build_opener(byterange.HTTPRangeHandler())
886 urllib2.install_opener(opener)
890 urllib2.install_opener(opener)
887 req = urllib2.Request(self.url)
891 req = urllib2.Request(self.url)
888 end = ''
892 end = ''
889 if bytes: end = self.pos + bytes
893 if bytes: end = self.pos + bytes
890 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
894 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
891 f = urllib2.urlopen(req)
895 f = urllib2.urlopen(req)
892 return f.read()
896 return f.read()
General Comments 0
You need to be logged in to leave comments. Login now