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