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