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