##// END OF EJS Templates
Merge with TAH...
mpm@selenic.com -
r299:7c239fad merge default
parent child Browse files
Show More
@@ -1,1264 +1,1271 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, os
8 import sys, struct, os
9 from revlog import *
9 from revlog import *
10 from demandload import *
10 from demandload import *
11 demandload(globals(), "re lock urllib urllib2 transaction time socket")
11 demandload(globals(), "re lock urllib urllib2 transaction time socket")
12 demandload(globals(), "tempfile byterange difflib")
12 demandload(globals(), "tempfile byterange difflib")
13
13
14 def is_exec(f):
14 def is_exec(f):
15 return (os.stat(f).st_mode & 0100 != 0)
15 return (os.stat(f).st_mode & 0100 != 0)
16
16
17 def set_exec(f, mode):
17 def set_exec(f, mode):
18 s = os.stat(f).st_mode
18 s = os.stat(f).st_mode
19 if (s & 0100 != 0) == mode:
19 if (s & 0100 != 0) == mode:
20 return
20 return
21 os.chmod(f, s & 0666 | (mode * 0111))
21 if mode:
22 # Turn on +x for every +r bit when making a file executable
23 # and obey umask.
24 umask = os.umask(0)
25 os.umask(umask)
26 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
27 else:
28 os.chmod(f, s & 0666)
22
29
23 class filelog(revlog):
30 class filelog(revlog):
24 def __init__(self, opener, path):
31 def __init__(self, opener, path):
25 revlog.__init__(self, opener,
32 revlog.__init__(self, opener,
26 os.path.join("data", path + ".i"),
33 os.path.join("data", path + ".i"),
27 os.path.join("data", path + ".d"))
34 os.path.join("data", path + ".d"))
28
35
29 def read(self, node):
36 def read(self, node):
30 return self.revision(node)
37 return self.revision(node)
31 def add(self, text, transaction, link, p1=None, p2=None):
38 def add(self, text, transaction, link, p1=None, p2=None):
32 return self.addrevision(text, transaction, link, p1, p2)
39 return self.addrevision(text, transaction, link, p1, p2)
33
40
34 def annotate(self, node):
41 def annotate(self, node):
35
42
36 def decorate(text, rev):
43 def decorate(text, rev):
37 return [(rev, l) for l in text.splitlines(1)]
44 return [(rev, l) for l in text.splitlines(1)]
38
45
39 def strip(annotation):
46 def strip(annotation):
40 return [e[1] for e in annotation]
47 return [e[1] for e in annotation]
41
48
42 def pair(parent, child):
49 def pair(parent, child):
43 new = []
50 new = []
44 sm = difflib.SequenceMatcher(None, strip(parent), strip(child))
51 sm = difflib.SequenceMatcher(None, strip(parent), strip(child))
45 for o, m, n, s, t in sm.get_opcodes():
52 for o, m, n, s, t in sm.get_opcodes():
46 if o == 'equal':
53 if o == 'equal':
47 new += parent[m:n]
54 new += parent[m:n]
48 else:
55 else:
49 new += child[s:t]
56 new += child[s:t]
50 return new
57 return new
51
58
52 # find all ancestors
59 # find all ancestors
53 needed = {node:1}
60 needed = {node:1}
54 visit = [node]
61 visit = [node]
55 while visit:
62 while visit:
56 n = visit.pop(0)
63 n = visit.pop(0)
57 for p in self.parents(n):
64 for p in self.parents(n):
58 if p not in needed:
65 if p not in needed:
59 needed[p] = 1
66 needed[p] = 1
60 visit.append(p)
67 visit.append(p)
61 else:
68 else:
62 # count how many times we'll use this
69 # count how many times we'll use this
63 needed[p] += 1
70 needed[p] += 1
64
71
65 # sort by revision which is a topological order
72 # sort by revision which is a topological order
66 visit = needed.keys()
73 visit = needed.keys()
67 visit = [ (self.rev(n), n) for n in visit ]
74 visit = [ (self.rev(n), n) for n in visit ]
68 visit.sort()
75 visit.sort()
69 visit = [ p[1] for p in visit ]
76 visit = [ p[1] for p in visit ]
70 hist = {}
77 hist = {}
71
78
72 for n in visit:
79 for n in visit:
73 curr = decorate(self.read(n), self.linkrev(n))
80 curr = decorate(self.read(n), self.linkrev(n))
74 for p in self.parents(n):
81 for p in self.parents(n):
75 if p != nullid:
82 if p != nullid:
76 curr = pair(hist[p], curr)
83 curr = pair(hist[p], curr)
77 # trim the history of unneeded revs
84 # trim the history of unneeded revs
78 needed[p] -= 1
85 needed[p] -= 1
79 if not needed[p]:
86 if not needed[p]:
80 del hist[p]
87 del hist[p]
81 hist[n] = curr
88 hist[n] = curr
82
89
83 return hist[n]
90 return hist[n]
84
91
85 class manifest(revlog):
92 class manifest(revlog):
86 def __init__(self, opener):
93 def __init__(self, opener):
87 self.mapcache = None
94 self.mapcache = None
88 self.listcache = None
95 self.listcache = None
89 self.addlist = None
96 self.addlist = None
90 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
97 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
91
98
92 def read(self, node):
99 def read(self, node):
93 if self.mapcache and self.mapcache[0] == node:
100 if self.mapcache and self.mapcache[0] == node:
94 return self.mapcache[1].copy()
101 return self.mapcache[1].copy()
95 text = self.revision(node)
102 text = self.revision(node)
96 map = {}
103 map = {}
97 flag = {}
104 flag = {}
98 self.listcache = (text, text.splitlines(1))
105 self.listcache = (text, text.splitlines(1))
99 for l in self.listcache[1]:
106 for l in self.listcache[1]:
100 (f, n) = l.split('\0')
107 (f, n) = l.split('\0')
101 map[f] = bin(n[:40])
108 map[f] = bin(n[:40])
102 flag[f] = (n[40:-1] == "x")
109 flag[f] = (n[40:-1] == "x")
103 self.mapcache = (node, map, flag)
110 self.mapcache = (node, map, flag)
104 return map
111 return map
105
112
106 def readflags(self, node):
113 def readflags(self, node):
107 if self.mapcache or self.mapcache[0] != node:
114 if self.mapcache or self.mapcache[0] != node:
108 self.read(node)
115 self.read(node)
109 return self.mapcache[2]
116 return self.mapcache[2]
110
117
111 def diff(self, a, b):
118 def diff(self, a, b):
112 # this is sneaky, as we're not actually using a and b
119 # this is sneaky, as we're not actually using a and b
113 if self.listcache and self.addlist and self.listcache[0] == a:
120 if self.listcache and self.addlist and self.listcache[0] == a:
114 d = mdiff.diff(self.listcache[1], self.addlist, 1)
121 d = mdiff.diff(self.listcache[1], self.addlist, 1)
115 if mdiff.patch(a, d) != b:
122 if mdiff.patch(a, d) != b:
116 sys.stderr.write("*** sortdiff failed, falling back ***\n")
123 sys.stderr.write("*** sortdiff failed, falling back ***\n")
117 return mdiff.textdiff(a, b)
124 return mdiff.textdiff(a, b)
118 return d
125 return d
119 else:
126 else:
120 return mdiff.textdiff(a, b)
127 return mdiff.textdiff(a, b)
121
128
122 def add(self, map, flags, transaction, link, p1=None, p2=None):
129 def add(self, map, flags, transaction, link, p1=None, p2=None):
123 files = map.keys()
130 files = map.keys()
124 files.sort()
131 files.sort()
125
132
126 self.addlist = ["%s\000%s%s\n" %
133 self.addlist = ["%s\000%s%s\n" %
127 (f, hex(map[f]), flags[f] and "x" or '')
134 (f, hex(map[f]), flags[f] and "x" or '')
128 for f in files]
135 for f in files]
129 text = "".join(self.addlist)
136 text = "".join(self.addlist)
130
137
131 n = self.addrevision(text, transaction, link, p1, p2)
138 n = self.addrevision(text, transaction, link, p1, p2)
132 self.mapcache = (n, map)
139 self.mapcache = (n, map)
133 self.listcache = (text, self.addlist)
140 self.listcache = (text, self.addlist)
134 self.addlist = None
141 self.addlist = None
135
142
136 return n
143 return n
137
144
138 class changelog(revlog):
145 class changelog(revlog):
139 def __init__(self, opener):
146 def __init__(self, opener):
140 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
147 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
141
148
142 def extract(self, text):
149 def extract(self, text):
143 if not text:
150 if not text:
144 return (nullid, "", "0", [], "")
151 return (nullid, "", "0", [], "")
145 last = text.index("\n\n")
152 last = text.index("\n\n")
146 desc = text[last + 2:]
153 desc = text[last + 2:]
147 l = text[:last].splitlines()
154 l = text[:last].splitlines()
148 manifest = bin(l[0])
155 manifest = bin(l[0])
149 user = l[1]
156 user = l[1]
150 date = l[2]
157 date = l[2]
151 files = l[3:]
158 files = l[3:]
152 return (manifest, user, date, files, desc)
159 return (manifest, user, date, files, desc)
153
160
154 def read(self, node):
161 def read(self, node):
155 return self.extract(self.revision(node))
162 return self.extract(self.revision(node))
156
163
157 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
164 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
158 user=None, date=None):
165 user=None, date=None):
159 user = (user or
166 user = (user or
160 os.environ.get("HGUSER") or
167 os.environ.get("HGUSER") or
161 os.environ.get("EMAIL") or
168 os.environ.get("EMAIL") or
162 os.environ.get("LOGNAME", "unknown") + '@' + socket.getfqdn())
169 os.environ.get("LOGNAME", "unknown") + '@' + socket.getfqdn())
163 date = date or "%d %d" % (time.time(), time.timezone)
170 date = date or "%d %d" % (time.time(), time.timezone)
164 list.sort()
171 list.sort()
165 l = [hex(manifest), user, date] + list + ["", desc]
172 l = [hex(manifest), user, date] + list + ["", desc]
166 text = "\n".join(l)
173 text = "\n".join(l)
167 return self.addrevision(text, transaction, self.count(), p1, p2)
174 return self.addrevision(text, transaction, self.count(), p1, p2)
168
175
169 class dirstate:
176 class dirstate:
170 def __init__(self, opener, ui, root):
177 def __init__(self, opener, ui, root):
171 self.opener = opener
178 self.opener = opener
172 self.root = root
179 self.root = root
173 self.dirty = 0
180 self.dirty = 0
174 self.ui = ui
181 self.ui = ui
175 self.map = None
182 self.map = None
176 self.pl = None
183 self.pl = None
177
184
178 def __del__(self):
185 def __del__(self):
179 if self.dirty:
186 if self.dirty:
180 self.write()
187 self.write()
181
188
182 def __getitem__(self, key):
189 def __getitem__(self, key):
183 try:
190 try:
184 return self.map[key]
191 return self.map[key]
185 except TypeError:
192 except TypeError:
186 self.read()
193 self.read()
187 return self[key]
194 return self[key]
188
195
189 def __contains__(self, key):
196 def __contains__(self, key):
190 if not self.map: self.read()
197 if not self.map: self.read()
191 return key in self.map
198 return key in self.map
192
199
193 def parents(self):
200 def parents(self):
194 if not self.pl:
201 if not self.pl:
195 self.read()
202 self.read()
196 return self.pl
203 return self.pl
197
204
198 def setparents(self, p1, p2 = nullid):
205 def setparents(self, p1, p2 = nullid):
199 self.dirty = 1
206 self.dirty = 1
200 self.pl = p1, p2
207 self.pl = p1, p2
201
208
202 def state(self, key):
209 def state(self, key):
203 try:
210 try:
204 return self[key][0]
211 return self[key][0]
205 except KeyError:
212 except KeyError:
206 return "?"
213 return "?"
207
214
208 def read(self):
215 def read(self):
209 if self.map is not None: return self.map
216 if self.map is not None: return self.map
210
217
211 self.map = {}
218 self.map = {}
212 self.pl = [nullid, nullid]
219 self.pl = [nullid, nullid]
213 try:
220 try:
214 st = self.opener("dirstate").read()
221 st = self.opener("dirstate").read()
215 except: return
222 except: return
216
223
217 self.pl = [st[:20], st[20: 40]]
224 self.pl = [st[:20], st[20: 40]]
218
225
219 pos = 40
226 pos = 40
220 while pos < len(st):
227 while pos < len(st):
221 e = struct.unpack(">cllll", st[pos:pos+17])
228 e = struct.unpack(">cllll", st[pos:pos+17])
222 l = e[4]
229 l = e[4]
223 pos += 17
230 pos += 17
224 f = st[pos:pos + l]
231 f = st[pos:pos + l]
225 self.map[f] = e[:4]
232 self.map[f] = e[:4]
226 pos += l
233 pos += l
227
234
228 def update(self, files, state):
235 def update(self, files, state):
229 ''' current states:
236 ''' current states:
230 n normal
237 n normal
231 m needs merging
238 m needs merging
232 r marked for removal
239 r marked for removal
233 a marked for addition'''
240 a marked for addition'''
234
241
235 if not files: return
242 if not files: return
236 self.read()
243 self.read()
237 self.dirty = 1
244 self.dirty = 1
238 for f in files:
245 for f in files:
239 if state == "r":
246 if state == "r":
240 self.map[f] = ('r', 0, 0, 0)
247 self.map[f] = ('r', 0, 0, 0)
241 else:
248 else:
242 s = os.stat(os.path.join(self.root, f))
249 s = os.stat(os.path.join(self.root, f))
243 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
250 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
244
251
245 def forget(self, files):
252 def forget(self, files):
246 if not files: return
253 if not files: return
247 self.read()
254 self.read()
248 self.dirty = 1
255 self.dirty = 1
249 for f in files:
256 for f in files:
250 try:
257 try:
251 del self.map[f]
258 del self.map[f]
252 except KeyError:
259 except KeyError:
253 self.ui.warn("not in dirstate: %s!\n" % f)
260 self.ui.warn("not in dirstate: %s!\n" % f)
254 pass
261 pass
255
262
256 def clear(self):
263 def clear(self):
257 self.map = {}
264 self.map = {}
258 self.dirty = 1
265 self.dirty = 1
259
266
260 def write(self):
267 def write(self):
261 st = self.opener("dirstate", "w")
268 st = self.opener("dirstate", "w")
262 st.write("".join(self.pl))
269 st.write("".join(self.pl))
263 for f, e in self.map.items():
270 for f, e in self.map.items():
264 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
271 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
265 st.write(e + f)
272 st.write(e + f)
266 self.dirty = 0
273 self.dirty = 0
267
274
268 def copy(self):
275 def copy(self):
269 self.read()
276 self.read()
270 return self.map.copy()
277 return self.map.copy()
271
278
272 # used to avoid circular references so destructors work
279 # used to avoid circular references so destructors work
273 def opener(base):
280 def opener(base):
274 p = base
281 p = base
275 def o(path, mode="r"):
282 def o(path, mode="r"):
276 if p[:7] == "http://":
283 if p[:7] == "http://":
277 f = os.path.join(p, urllib.quote(path))
284 f = os.path.join(p, urllib.quote(path))
278 return httprangereader(f)
285 return httprangereader(f)
279
286
280 f = os.path.join(p, path)
287 f = os.path.join(p, path)
281
288
282 mode += "b" # for that other OS
289 mode += "b" # for that other OS
283
290
284 if mode[0] != "r":
291 if mode[0] != "r":
285 try:
292 try:
286 s = os.stat(f)
293 s = os.stat(f)
287 except OSError:
294 except OSError:
288 d = os.path.dirname(f)
295 d = os.path.dirname(f)
289 if not os.path.isdir(d):
296 if not os.path.isdir(d):
290 os.makedirs(d)
297 os.makedirs(d)
291 else:
298 else:
292 if s.st_nlink > 1:
299 if s.st_nlink > 1:
293 file(f + ".tmp", "w").write(file(f).read())
300 file(f + ".tmp", "w").write(file(f).read())
294 os.rename(f+".tmp", f)
301 os.rename(f+".tmp", f)
295
302
296 return file(f, mode)
303 return file(f, mode)
297
304
298 return o
305 return o
299
306
300 class localrepository:
307 class localrepository:
301 def __init__(self, ui, path=None, create=0):
308 def __init__(self, ui, path=None, create=0):
302 self.remote = 0
309 self.remote = 0
303 if path and path[:7] == "http://":
310 if path and path[:7] == "http://":
304 self.remote = 1
311 self.remote = 1
305 self.path = path
312 self.path = path
306 else:
313 else:
307 if not path:
314 if not path:
308 p = os.getcwd()
315 p = os.getcwd()
309 while not os.path.isdir(os.path.join(p, ".hg")):
316 while not os.path.isdir(os.path.join(p, ".hg")):
310 p = os.path.dirname(p)
317 p = os.path.dirname(p)
311 if p == "/": raise "No repo found"
318 if p == "/": raise "No repo found"
312 path = p
319 path = p
313 self.path = os.path.join(path, ".hg")
320 self.path = os.path.join(path, ".hg")
314
321
315 self.root = path
322 self.root = path
316 self.ui = ui
323 self.ui = ui
317
324
318 if create:
325 if create:
319 os.mkdir(self.path)
326 os.mkdir(self.path)
320 os.mkdir(self.join("data"))
327 os.mkdir(self.join("data"))
321
328
322 self.opener = opener(self.path)
329 self.opener = opener(self.path)
323 self.wopener = opener(self.root)
330 self.wopener = opener(self.root)
324 self.manifest = manifest(self.opener)
331 self.manifest = manifest(self.opener)
325 self.changelog = changelog(self.opener)
332 self.changelog = changelog(self.opener)
326 self.ignorelist = None
333 self.ignorelist = None
327 self.tags = None
334 self.tags = None
328
335
329 if not self.remote:
336 if not self.remote:
330 self.dirstate = dirstate(self.opener, ui, self.root)
337 self.dirstate = dirstate(self.opener, ui, self.root)
331
338
332 def ignore(self, f):
339 def ignore(self, f):
333 if self.ignorelist is None:
340 if self.ignorelist is None:
334 self.ignorelist = []
341 self.ignorelist = []
335 try:
342 try:
336 l = self.wfile(".hgignore")
343 l = self.wfile(".hgignore")
337 for pat in l:
344 for pat in l:
338 if pat != "\n":
345 if pat != "\n":
339 self.ignorelist.append(re.compile(pat[:-1]))
346 self.ignorelist.append(re.compile(pat[:-1]))
340 except IOError: pass
347 except IOError: pass
341 for pat in self.ignorelist:
348 for pat in self.ignorelist:
342 if pat.search(f): return True
349 if pat.search(f): return True
343 return False
350 return False
344
351
345 def lookup(self, key):
352 def lookup(self, key):
346 if self.tags is None:
353 if self.tags is None:
347 self.tags = {}
354 self.tags = {}
348 try:
355 try:
349 # read each head of the tags file, ending with the tip
356 # read each head of the tags file, ending with the tip
350 # and add each tag found to the map, with "newer" ones
357 # and add each tag found to the map, with "newer" ones
351 # taking precedence
358 # taking precedence
352 fl = self.file(".hgtags")
359 fl = self.file(".hgtags")
353 h = fl.heads()
360 h = fl.heads()
354 h.reverse()
361 h.reverse()
355 for r in h:
362 for r in h:
356 for l in fl.revision(r).splitlines():
363 for l in fl.revision(r).splitlines():
357 if l:
364 if l:
358 n, k = l.split(" ")
365 n, k = l.split(" ")
359 self.tags[k] = bin(n)
366 self.tags[k] = bin(n)
360 except KeyError: pass
367 except KeyError: pass
361 try:
368 try:
362 return self.tags[key]
369 return self.tags[key]
363 except KeyError:
370 except KeyError:
364 return self.changelog.lookup(key)
371 return self.changelog.lookup(key)
365
372
366 def join(self, f):
373 def join(self, f):
367 return os.path.join(self.path, f)
374 return os.path.join(self.path, f)
368
375
369 def wjoin(self, f):
376 def wjoin(self, f):
370 return os.path.join(self.root, f)
377 return os.path.join(self.root, f)
371
378
372 def file(self, f):
379 def file(self, f):
373 if f[0] == '/': f = f[1:]
380 if f[0] == '/': f = f[1:]
374 return filelog(self.opener, f)
381 return filelog(self.opener, f)
375
382
376 def wfile(self, f, mode='r'):
383 def wfile(self, f, mode='r'):
377 return self.wopener(f, mode)
384 return self.wopener(f, mode)
378
385
379 def transaction(self):
386 def transaction(self):
380 # save dirstate for undo
387 # save dirstate for undo
381 try:
388 try:
382 ds = self.opener("dirstate").read()
389 ds = self.opener("dirstate").read()
383 except IOError:
390 except IOError:
384 ds = ""
391 ds = ""
385 self.opener("undo.dirstate", "w").write(ds)
392 self.opener("undo.dirstate", "w").write(ds)
386
393
387 return transaction.transaction(self.opener, self.join("journal"),
394 return transaction.transaction(self.opener, self.join("journal"),
388 self.join("undo"))
395 self.join("undo"))
389
396
390 def recover(self):
397 def recover(self):
391 lock = self.lock()
398 lock = self.lock()
392 if os.path.exists(self.join("recover")):
399 if os.path.exists(self.join("recover")):
393 self.ui.status("attempting to rollback interrupted transaction\n")
400 self.ui.status("attempting to rollback interrupted transaction\n")
394 return transaction.rollback(self.opener, self.join("recover"))
401 return transaction.rollback(self.opener, self.join("recover"))
395 else:
402 else:
396 self.ui.warn("no interrupted transaction available\n")
403 self.ui.warn("no interrupted transaction available\n")
397
404
398 def undo(self):
405 def undo(self):
399 lock = self.lock()
406 lock = self.lock()
400 if os.path.exists(self.join("undo")):
407 if os.path.exists(self.join("undo")):
401 self.ui.status("attempting to rollback last transaction\n")
408 self.ui.status("attempting to rollback last transaction\n")
402 transaction.rollback(self.opener, self.join("undo"))
409 transaction.rollback(self.opener, self.join("undo"))
403 self.dirstate = None
410 self.dirstate = None
404 os.rename(self.join("undo.dirstate"), self.join("dirstate"))
411 os.rename(self.join("undo.dirstate"), self.join("dirstate"))
405 self.dirstate = dirstate(self.opener, self.ui, self.root)
412 self.dirstate = dirstate(self.opener, self.ui, self.root)
406 else:
413 else:
407 self.ui.warn("no undo information available\n")
414 self.ui.warn("no undo information available\n")
408
415
409 def lock(self, wait = 1):
416 def lock(self, wait = 1):
410 try:
417 try:
411 return lock.lock(self.join("lock"), 0)
418 return lock.lock(self.join("lock"), 0)
412 except lock.LockHeld, inst:
419 except lock.LockHeld, inst:
413 if wait:
420 if wait:
414 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
421 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
415 return lock.lock(self.join("lock"), wait)
422 return lock.lock(self.join("lock"), wait)
416 raise inst
423 raise inst
417
424
418 def rawcommit(self, files, text, user, date, p1=None, p2=None):
425 def rawcommit(self, files, text, user, date, p1=None, p2=None):
419 p1 = p1 or self.dirstate.parents()[0] or nullid
426 p1 = p1 or self.dirstate.parents()[0] or nullid
420 p2 = p2 or self.dirstate.parents()[1] or nullid
427 p2 = p2 or self.dirstate.parents()[1] or nullid
421 pchange = self.changelog.read(p1)
428 pchange = self.changelog.read(p1)
422 pmmap = self.manifest.read(pchange[0])
429 pmmap = self.manifest.read(pchange[0])
423 tr = self.transaction()
430 tr = self.transaction()
424 mmap = {}
431 mmap = {}
425 linkrev = self.changelog.count()
432 linkrev = self.changelog.count()
426 for f in files:
433 for f in files:
427 try:
434 try:
428 t = file(f).read()
435 t = file(f).read()
429 except IOError:
436 except IOError:
430 self.ui.warn("Read file %s error, skipped\n" % f)
437 self.ui.warn("Read file %s error, skipped\n" % f)
431 continue
438 continue
432 r = self.file(f)
439 r = self.file(f)
433 # FIXME - need to find both parents properly
440 # FIXME - need to find both parents properly
434 prev = pmmap.get(f, nullid)
441 prev = pmmap.get(f, nullid)
435 mmap[f] = r.add(t, tr, linkrev, prev)
442 mmap[f] = r.add(t, tr, linkrev, prev)
436
443
437 mnode = self.manifest.add(mmap, tr, linkrev, pchange[0])
444 mnode = self.manifest.add(mmap, tr, linkrev, pchange[0])
438 n = self.changelog.add(mnode, files, text, tr, p1, p2, user ,date, )
445 n = self.changelog.add(mnode, files, text, tr, p1, p2, user ,date, )
439 tr.close()
446 tr.close()
440 self.dirstate.setparents(p1, p2)
447 self.dirstate.setparents(p1, p2)
441 self.dirstate.clear()
448 self.dirstate.clear()
442 self.dirstate.update(mmap.keys(), "n")
449 self.dirstate.update(mmap.keys(), "n")
443
450
444 def commit(self, files = None, text = ""):
451 def commit(self, files = None, text = ""):
445 commit = []
452 commit = []
446 remove = []
453 remove = []
447 if files:
454 if files:
448 for f in files:
455 for f in files:
449 s = self.dirstate.state(f)
456 s = self.dirstate.state(f)
450 if s in 'nmai':
457 if s in 'nmai':
451 commit.append(f)
458 commit.append(f)
452 elif s == 'r':
459 elif s == 'r':
453 remove.append(f)
460 remove.append(f)
454 else:
461 else:
455 self.ui.warn("%s not tracked!\n" % f)
462 self.ui.warn("%s not tracked!\n" % f)
456 else:
463 else:
457 (c, a, d, u) = self.diffdir(self.root)
464 (c, a, d, u) = self.diffdir(self.root)
458 commit = c + a
465 commit = c + a
459 remove = d
466 remove = d
460
467
461 if not commit and not remove:
468 if not commit and not remove:
462 self.ui.status("nothing changed\n")
469 self.ui.status("nothing changed\n")
463 return
470 return
464
471
465 p1, p2 = self.dirstate.parents()
472 p1, p2 = self.dirstate.parents()
466 c1 = self.changelog.read(p1)
473 c1 = self.changelog.read(p1)
467 c2 = self.changelog.read(p2)
474 c2 = self.changelog.read(p2)
468 m1 = self.manifest.read(c1[0])
475 m1 = self.manifest.read(c1[0])
469 mf1 = self.manifest.readflags(c1[0])
476 mf1 = self.manifest.readflags(c1[0])
470 m2 = self.manifest.read(c2[0])
477 m2 = self.manifest.read(c2[0])
471 lock = self.lock()
478 lock = self.lock()
472 tr = self.transaction()
479 tr = self.transaction()
473
480
474 # check in files
481 # check in files
475 new = {}
482 new = {}
476 linkrev = self.changelog.count()
483 linkrev = self.changelog.count()
477 commit.sort()
484 commit.sort()
478 for f in commit:
485 for f in commit:
479 self.ui.note(f + "\n")
486 self.ui.note(f + "\n")
480 try:
487 try:
481 fp = self.wjoin(f)
488 fp = self.wjoin(f)
482 mf1[f] = is_exec(fp)
489 mf1[f] = is_exec(fp)
483 t = file(fp).read()
490 t = file(fp).read()
484 except IOError:
491 except IOError:
485 self.warn("trouble committing %s!\n" % f)
492 self.warn("trouble committing %s!\n" % f)
486 raise
493 raise
487
494
488 r = self.file(f)
495 r = self.file(f)
489 fp1 = m1.get(f, nullid)
496 fp1 = m1.get(f, nullid)
490 fp2 = m2.get(f, nullid)
497 fp2 = m2.get(f, nullid)
491 new[f] = r.add(t, tr, linkrev, fp1, fp2)
498 new[f] = r.add(t, tr, linkrev, fp1, fp2)
492
499
493 # update manifest
500 # update manifest
494 m1.update(new)
501 m1.update(new)
495 for f in remove: del m1[f]
502 for f in remove: del m1[f]
496 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0])
503 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0])
497
504
498 # add changeset
505 # add changeset
499 new = new.keys()
506 new = new.keys()
500 new.sort()
507 new.sort()
501
508
502 if not text:
509 if not text:
503 edittext = "\n" + "HG: manifest hash %s\n" % hex(mn)
510 edittext = "\n" + "HG: manifest hash %s\n" % hex(mn)
504 edittext += "".join(["HG: changed %s\n" % f for f in new])
511 edittext += "".join(["HG: changed %s\n" % f for f in new])
505 edittext += "".join(["HG: removed %s\n" % f for f in remove])
512 edittext += "".join(["HG: removed %s\n" % f for f in remove])
506 edittext = self.ui.edit(edittext)
513 edittext = self.ui.edit(edittext)
507 if not edittext.rstrip():
514 if not edittext.rstrip():
508 return 1
515 return 1
509 text = edittext
516 text = edittext
510
517
511 n = self.changelog.add(mn, new, text, tr, p1, p2)
518 n = self.changelog.add(mn, new, text, tr, p1, p2)
512 tr.close()
519 tr.close()
513
520
514 self.dirstate.setparents(n)
521 self.dirstate.setparents(n)
515 self.dirstate.update(new, "n")
522 self.dirstate.update(new, "n")
516 self.dirstate.forget(remove)
523 self.dirstate.forget(remove)
517
524
518 def diffdir(self, path, changeset = None):
525 def diffdir(self, path, changeset = None):
519 changed = []
526 changed = []
520 added = []
527 added = []
521 unknown = []
528 unknown = []
522 mf = {}
529 mf = {}
523
530
524 if changeset:
531 if changeset:
525 change = self.changelog.read(changeset)
532 change = self.changelog.read(changeset)
526 mf = self.manifest.read(change[0])
533 mf = self.manifest.read(change[0])
527 dc = dict.fromkeys(mf)
534 dc = dict.fromkeys(mf)
528 else:
535 else:
529 changeset = self.dirstate.parents()[0]
536 changeset = self.dirstate.parents()[0]
530 change = self.changelog.read(changeset)
537 change = self.changelog.read(changeset)
531 mf = self.manifest.read(change[0])
538 mf = self.manifest.read(change[0])
532 dc = self.dirstate.copy()
539 dc = self.dirstate.copy()
533
540
534 def fcmp(fn):
541 def fcmp(fn):
535 t1 = self.wfile(fn).read()
542 t1 = self.wfile(fn).read()
536 t2 = self.file(fn).revision(mf[fn])
543 t2 = self.file(fn).revision(mf[fn])
537 return cmp(t1, t2)
544 return cmp(t1, t2)
538
545
539 for dir, subdirs, files in os.walk(self.root):
546 for dir, subdirs, files in os.walk(self.root):
540 d = dir[len(self.root)+1:]
547 d = dir[len(self.root)+1:]
541 if ".hg" in subdirs: subdirs.remove(".hg")
548 if ".hg" in subdirs: subdirs.remove(".hg")
542
549
543 for f in files:
550 for f in files:
544 fn = os.path.join(d, f)
551 fn = os.path.join(d, f)
545 try: s = os.stat(os.path.join(self.root, fn))
552 try: s = os.stat(os.path.join(self.root, fn))
546 except: continue
553 except: continue
547 if fn in dc:
554 if fn in dc:
548 c = dc[fn]
555 c = dc[fn]
549 del dc[fn]
556 del dc[fn]
550 if not c:
557 if not c:
551 if fcmp(fn):
558 if fcmp(fn):
552 changed.append(fn)
559 changed.append(fn)
553 elif c[0] == 'm':
560 elif c[0] == 'm':
554 changed.append(fn)
561 changed.append(fn)
555 elif c[0] == 'a':
562 elif c[0] == 'a':
556 added.append(fn)
563 added.append(fn)
557 elif c[0] == 'r':
564 elif c[0] == 'r':
558 unknown.append(fn)
565 unknown.append(fn)
559 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
566 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
560 changed.append(fn)
567 changed.append(fn)
561 elif c[1] != s.st_mode or c[3] != s.st_mtime:
568 elif c[1] != s.st_mode or c[3] != s.st_mtime:
562 if fcmp(fn):
569 if fcmp(fn):
563 changed.append(fn)
570 changed.append(fn)
564 else:
571 else:
565 if self.ignore(fn): continue
572 if self.ignore(fn): continue
566 unknown.append(fn)
573 unknown.append(fn)
567
574
568 deleted = dc.keys()
575 deleted = dc.keys()
569 deleted.sort()
576 deleted.sort()
570
577
571 return (changed, added, deleted, unknown)
578 return (changed, added, deleted, unknown)
572
579
573 def diffrevs(self, node1, node2):
580 def diffrevs(self, node1, node2):
574 changed, added = [], []
581 changed, added = [], []
575
582
576 change = self.changelog.read(node1)
583 change = self.changelog.read(node1)
577 mf1 = self.manifest.read(change[0])
584 mf1 = self.manifest.read(change[0])
578 change = self.changelog.read(node2)
585 change = self.changelog.read(node2)
579 mf2 = self.manifest.read(change[0])
586 mf2 = self.manifest.read(change[0])
580
587
581 for fn in mf2:
588 for fn in mf2:
582 if mf1.has_key(fn):
589 if mf1.has_key(fn):
583 if mf1[fn] != mf2[fn]:
590 if mf1[fn] != mf2[fn]:
584 changed.append(fn)
591 changed.append(fn)
585 del mf1[fn]
592 del mf1[fn]
586 else:
593 else:
587 added.append(fn)
594 added.append(fn)
588
595
589 deleted = mf1.keys()
596 deleted = mf1.keys()
590 deleted.sort()
597 deleted.sort()
591
598
592 return (changed, added, deleted)
599 return (changed, added, deleted)
593
600
594 def add(self, list):
601 def add(self, list):
595 for f in list:
602 for f in list:
596 p = self.wjoin(f)
603 p = self.wjoin(f)
597 if not os.path.isfile(p):
604 if not os.path.isfile(p):
598 self.ui.warn("%s does not exist!\n" % f)
605 self.ui.warn("%s does not exist!\n" % f)
599 elif self.dirstate.state(f) == 'n':
606 elif self.dirstate.state(f) == 'n':
600 self.ui.warn("%s already tracked!\n" % f)
607 self.ui.warn("%s already tracked!\n" % f)
601 else:
608 else:
602 self.dirstate.update([f], "a")
609 self.dirstate.update([f], "a")
603
610
604 def forget(self, list):
611 def forget(self, list):
605 for f in list:
612 for f in list:
606 if self.dirstate.state(f) not in 'ai':
613 if self.dirstate.state(f) not in 'ai':
607 self.ui.warn("%s not added!\n" % f)
614 self.ui.warn("%s not added!\n" % f)
608 else:
615 else:
609 self.dirstate.forget([f])
616 self.dirstate.forget([f])
610
617
611 def remove(self, list):
618 def remove(self, list):
612 for f in list:
619 for f in list:
613 p = self.wjoin(f)
620 p = self.wjoin(f)
614 if os.path.isfile(p):
621 if os.path.isfile(p):
615 self.ui.warn("%s still exists!\n" % f)
622 self.ui.warn("%s still exists!\n" % f)
616 elif f not in self.dirstate:
623 elif f not in self.dirstate:
617 self.ui.warn("%s not tracked!\n" % f)
624 self.ui.warn("%s not tracked!\n" % f)
618 else:
625 else:
619 self.dirstate.update([f], "r")
626 self.dirstate.update([f], "r")
620
627
621 def heads(self):
628 def heads(self):
622 return self.changelog.heads()
629 return self.changelog.heads()
623
630
624 def branches(self, nodes):
631 def branches(self, nodes):
625 if not nodes: nodes = [self.changelog.tip()]
632 if not nodes: nodes = [self.changelog.tip()]
626 b = []
633 b = []
627 for n in nodes:
634 for n in nodes:
628 t = n
635 t = n
629 while n:
636 while n:
630 p = self.changelog.parents(n)
637 p = self.changelog.parents(n)
631 if p[1] != nullid or p[0] == nullid:
638 if p[1] != nullid or p[0] == nullid:
632 b.append((t, n, p[0], p[1]))
639 b.append((t, n, p[0], p[1]))
633 break
640 break
634 n = p[0]
641 n = p[0]
635 return b
642 return b
636
643
637 def between(self, pairs):
644 def between(self, pairs):
638 r = []
645 r = []
639
646
640 for top, bottom in pairs:
647 for top, bottom in pairs:
641 n, l, i = top, [], 0
648 n, l, i = top, [], 0
642 f = 1
649 f = 1
643
650
644 while n != bottom:
651 while n != bottom:
645 p = self.changelog.parents(n)[0]
652 p = self.changelog.parents(n)[0]
646 if i == f:
653 if i == f:
647 l.append(n)
654 l.append(n)
648 f = f * 2
655 f = f * 2
649 n = p
656 n = p
650 i += 1
657 i += 1
651
658
652 r.append(l)
659 r.append(l)
653
660
654 return r
661 return r
655
662
656 def newer(self, nodes):
663 def newer(self, nodes):
657 m = {}
664 m = {}
658 nl = []
665 nl = []
659 pm = {}
666 pm = {}
660 cl = self.changelog
667 cl = self.changelog
661 t = l = cl.count()
668 t = l = cl.count()
662
669
663 # find the lowest numbered node
670 # find the lowest numbered node
664 for n in nodes:
671 for n in nodes:
665 l = min(l, cl.rev(n))
672 l = min(l, cl.rev(n))
666 m[n] = 1
673 m[n] = 1
667
674
668 for i in xrange(l, t):
675 for i in xrange(l, t):
669 n = cl.node(i)
676 n = cl.node(i)
670 if n in m: # explicitly listed
677 if n in m: # explicitly listed
671 pm[n] = 1
678 pm[n] = 1
672 nl.append(n)
679 nl.append(n)
673 continue
680 continue
674 for p in cl.parents(n):
681 for p in cl.parents(n):
675 if p in pm: # parent listed
682 if p in pm: # parent listed
676 pm[n] = 1
683 pm[n] = 1
677 nl.append(n)
684 nl.append(n)
678 break
685 break
679
686
680 return nl
687 return nl
681
688
682 def getchangegroup(self, remote):
689 def getchangegroup(self, remote):
683 m = self.changelog.nodemap
690 m = self.changelog.nodemap
684 search = []
691 search = []
685 fetch = []
692 fetch = []
686 seen = {}
693 seen = {}
687 seenbranch = {}
694 seenbranch = {}
688
695
689 # if we have an empty repo, fetch everything
696 # if we have an empty repo, fetch everything
690 if self.changelog.tip() == nullid:
697 if self.changelog.tip() == nullid:
691 self.ui.status("requesting all changes\n")
698 self.ui.status("requesting all changes\n")
692 return remote.changegroup([nullid])
699 return remote.changegroup([nullid])
693
700
694 # otherwise, assume we're closer to the tip than the root
701 # otherwise, assume we're closer to the tip than the root
695 self.ui.status("searching for changes\n")
702 self.ui.status("searching for changes\n")
696 heads = remote.heads()
703 heads = remote.heads()
697 unknown = []
704 unknown = []
698 for h in heads:
705 for h in heads:
699 if h not in m:
706 if h not in m:
700 unknown.append(h)
707 unknown.append(h)
701
708
702 if not unknown:
709 if not unknown:
703 self.ui.status("nothing to do!\n")
710 self.ui.status("nothing to do!\n")
704 return None
711 return None
705
712
706 unknown = remote.branches(unknown)
713 unknown = remote.branches(unknown)
707 while unknown:
714 while unknown:
708 n = unknown.pop(0)
715 n = unknown.pop(0)
709 seen[n[0]] = 1
716 seen[n[0]] = 1
710
717
711 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
718 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
712 if n == nullid: break
719 if n == nullid: break
713 if n in seenbranch:
720 if n in seenbranch:
714 self.ui.debug("branch already found\n")
721 self.ui.debug("branch already found\n")
715 continue
722 continue
716 if n[1] and n[1] in m: # do we know the base?
723 if n[1] and n[1] in m: # do we know the base?
717 self.ui.debug("found incomplete branch %s:%s\n"
724 self.ui.debug("found incomplete branch %s:%s\n"
718 % (short(n[0]), short(n[1])))
725 % (short(n[0]), short(n[1])))
719 search.append(n) # schedule branch range for scanning
726 search.append(n) # schedule branch range for scanning
720 seenbranch[n] = 1
727 seenbranch[n] = 1
721 else:
728 else:
722 if n[2] in m and n[3] in m:
729 if n[2] in m and n[3] in m:
723 if n[1] not in fetch:
730 if n[1] not in fetch:
724 self.ui.debug("found new changeset %s\n" %
731 self.ui.debug("found new changeset %s\n" %
725 short(n[1]))
732 short(n[1]))
726 fetch.append(n[1]) # earliest unknown
733 fetch.append(n[1]) # earliest unknown
727 continue
734 continue
728
735
729 r = []
736 r = []
730 for a in n[2:4]:
737 for a in n[2:4]:
731 if a not in seen: r.append(a)
738 if a not in seen: r.append(a)
732
739
733 if r:
740 if r:
734 self.ui.debug("requesting %s\n" %
741 self.ui.debug("requesting %s\n" %
735 " ".join(map(short, r)))
742 " ".join(map(short, r)))
736 for b in remote.branches(r):
743 for b in remote.branches(r):
737 self.ui.debug("received %s:%s\n" %
744 self.ui.debug("received %s:%s\n" %
738 (short(b[0]), short(b[1])))
745 (short(b[0]), short(b[1])))
739 if b[0] not in m and b[0] not in seen:
746 if b[0] not in m and b[0] not in seen:
740 unknown.append(b)
747 unknown.append(b)
741
748
742 while search:
749 while search:
743 n = search.pop(0)
750 n = search.pop(0)
744 l = remote.between([(n[0], n[1])])[0]
751 l = remote.between([(n[0], n[1])])[0]
745 p = n[0]
752 p = n[0]
746 f = 1
753 f = 1
747 for i in l + [n[1]]:
754 for i in l + [n[1]]:
748 if i in m:
755 if i in m:
749 if f <= 2:
756 if f <= 2:
750 self.ui.debug("found new branch changeset %s\n" %
757 self.ui.debug("found new branch changeset %s\n" %
751 short(p))
758 short(p))
752 fetch.append(p)
759 fetch.append(p)
753 else:
760 else:
754 self.ui.debug("narrowed branch search to %s:%s\n"
761 self.ui.debug("narrowed branch search to %s:%s\n"
755 % (short(p), short(i)))
762 % (short(p), short(i)))
756 search.append((p, i))
763 search.append((p, i))
757 break
764 break
758 p, f = i, f * 2
765 p, f = i, f * 2
759
766
760 for f in fetch:
767 for f in fetch:
761 if f in m:
768 if f in m:
762 raise "already have", short(f[:4])
769 raise "already have", short(f[:4])
763
770
764 self.ui.note("adding new changesets starting at " +
771 self.ui.note("adding new changesets starting at " +
765 " ".join([short(f) for f in fetch]) + "\n")
772 " ".join([short(f) for f in fetch]) + "\n")
766
773
767 return remote.changegroup(fetch)
774 return remote.changegroup(fetch)
768
775
769 def changegroup(self, basenodes):
776 def changegroup(self, basenodes):
770 nodes = self.newer(basenodes)
777 nodes = self.newer(basenodes)
771
778
772 # construct the link map
779 # construct the link map
773 linkmap = {}
780 linkmap = {}
774 for n in nodes:
781 for n in nodes:
775 linkmap[self.changelog.rev(n)] = n
782 linkmap[self.changelog.rev(n)] = n
776
783
777 # construct a list of all changed files
784 # construct a list of all changed files
778 changed = {}
785 changed = {}
779 for n in nodes:
786 for n in nodes:
780 c = self.changelog.read(n)
787 c = self.changelog.read(n)
781 for f in c[3]:
788 for f in c[3]:
782 changed[f] = 1
789 changed[f] = 1
783 changed = changed.keys()
790 changed = changed.keys()
784 changed.sort()
791 changed.sort()
785
792
786 # the changegroup is changesets + manifests + all file revs
793 # the changegroup is changesets + manifests + all file revs
787 revs = [ self.changelog.rev(n) for n in nodes ]
794 revs = [ self.changelog.rev(n) for n in nodes ]
788
795
789 for y in self.changelog.group(linkmap): yield y
796 for y in self.changelog.group(linkmap): yield y
790 for y in self.manifest.group(linkmap): yield y
797 for y in self.manifest.group(linkmap): yield y
791 for f in changed:
798 for f in changed:
792 yield struct.pack(">l", len(f) + 4) + f
799 yield struct.pack(">l", len(f) + 4) + f
793 g = self.file(f).group(linkmap)
800 g = self.file(f).group(linkmap)
794 for y in g:
801 for y in g:
795 yield y
802 yield y
796
803
797 def addchangegroup(self, generator):
804 def addchangegroup(self, generator):
798
805
799 class genread:
806 class genread:
800 def __init__(self, generator):
807 def __init__(self, generator):
801 self.g = generator
808 self.g = generator
802 self.buf = ""
809 self.buf = ""
803 def read(self, l):
810 def read(self, l):
804 while l > len(self.buf):
811 while l > len(self.buf):
805 try:
812 try:
806 self.buf += self.g.next()
813 self.buf += self.g.next()
807 except StopIteration:
814 except StopIteration:
808 break
815 break
809 d, self.buf = self.buf[:l], self.buf[l:]
816 d, self.buf = self.buf[:l], self.buf[l:]
810 return d
817 return d
811
818
812 def getchunk():
819 def getchunk():
813 d = source.read(4)
820 d = source.read(4)
814 if not d: return ""
821 if not d: return ""
815 l = struct.unpack(">l", d)[0]
822 l = struct.unpack(">l", d)[0]
816 if l <= 4: return ""
823 if l <= 4: return ""
817 return source.read(l - 4)
824 return source.read(l - 4)
818
825
819 def getgroup():
826 def getgroup():
820 while 1:
827 while 1:
821 c = getchunk()
828 c = getchunk()
822 if not c: break
829 if not c: break
823 yield c
830 yield c
824
831
825 def csmap(x):
832 def csmap(x):
826 self.ui.debug("add changeset %s\n" % short(x))
833 self.ui.debug("add changeset %s\n" % short(x))
827 return self.changelog.count()
834 return self.changelog.count()
828
835
829 def revmap(x):
836 def revmap(x):
830 return self.changelog.rev(x)
837 return self.changelog.rev(x)
831
838
832 if not generator: return
839 if not generator: return
833 changesets = files = revisions = 0
840 changesets = files = revisions = 0
834
841
835 source = genread(generator)
842 source = genread(generator)
836 lock = self.lock()
843 lock = self.lock()
837 tr = self.transaction()
844 tr = self.transaction()
838
845
839 # pull off the changeset group
846 # pull off the changeset group
840 self.ui.status("adding changesets\n")
847 self.ui.status("adding changesets\n")
841 co = self.changelog.tip()
848 co = self.changelog.tip()
842 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
849 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
843 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
850 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
844
851
845 # pull off the manifest group
852 # pull off the manifest group
846 self.ui.status("adding manifests\n")
853 self.ui.status("adding manifests\n")
847 mm = self.manifest.tip()
854 mm = self.manifest.tip()
848 mo = self.manifest.addgroup(getgroup(), revmap, tr)
855 mo = self.manifest.addgroup(getgroup(), revmap, tr)
849
856
850 # process the files
857 # process the files
851 self.ui.status("adding file revisions\n")
858 self.ui.status("adding file revisions\n")
852 while 1:
859 while 1:
853 f = getchunk()
860 f = getchunk()
854 if not f: break
861 if not f: break
855 self.ui.debug("adding %s revisions\n" % f)
862 self.ui.debug("adding %s revisions\n" % f)
856 fl = self.file(f)
863 fl = self.file(f)
857 o = fl.tip()
864 o = fl.tip()
858 n = fl.addgroup(getgroup(), revmap, tr)
865 n = fl.addgroup(getgroup(), revmap, tr)
859 revisions += fl.rev(n) - fl.rev(o)
866 revisions += fl.rev(n) - fl.rev(o)
860 files += 1
867 files += 1
861
868
862 self.ui.status(("modified %d files, added %d changesets" +
869 self.ui.status(("modified %d files, added %d changesets" +
863 " and %d new revisions\n")
870 " and %d new revisions\n")
864 % (files, changesets, revisions))
871 % (files, changesets, revisions))
865
872
866 tr.close()
873 tr.close()
867 return
874 return
868
875
869 def update(self, node, allow=False, force=False):
876 def update(self, node, allow=False, force=False):
870 pl = self.dirstate.parents()
877 pl = self.dirstate.parents()
871 if not force and pl[1] != nullid:
878 if not force and pl[1] != nullid:
872 self.ui.warn("aborting: outstanding uncommitted merges\n")
879 self.ui.warn("aborting: outstanding uncommitted merges\n")
873 return
880 return
874
881
875 p1, p2 = pl[0], node
882 p1, p2 = pl[0], node
876 m1n = self.changelog.read(p1)[0]
883 m1n = self.changelog.read(p1)[0]
877 m2n = self.changelog.read(p2)[0]
884 m2n = self.changelog.read(p2)[0]
878 man = self.manifest.ancestor(m1n, m2n)
885 man = self.manifest.ancestor(m1n, m2n)
879 m1 = self.manifest.read(m1n)
886 m1 = self.manifest.read(m1n)
880 mf1 = self.manifest.readflags(m1n)
887 mf1 = self.manifest.readflags(m1n)
881 m2 = self.manifest.read(m2n)
888 m2 = self.manifest.read(m2n)
882 mf2 = self.manifest.readflags(m2n)
889 mf2 = self.manifest.readflags(m2n)
883 ma = self.manifest.read(man)
890 ma = self.manifest.read(man)
884 mfa = self.manifest.readflags(m2n)
891 mfa = self.manifest.readflags(m2n)
885
892
886 (c, a, d, u) = self.diffdir(self.root)
893 (c, a, d, u) = self.diffdir(self.root)
887
894
888 # resolve the manifest to determine which files
895 # resolve the manifest to determine which files
889 # we care about merging
896 # we care about merging
890 self.ui.note("resolving manifests\n")
897 self.ui.note("resolving manifests\n")
891 self.ui.debug(" ancestor %s local %s remote %s\n" %
898 self.ui.debug(" ancestor %s local %s remote %s\n" %
892 (short(man), short(m1n), short(m2n)))
899 (short(man), short(m1n), short(m2n)))
893
900
894 merge = {}
901 merge = {}
895 get = {}
902 get = {}
896 remove = []
903 remove = []
897
904
898 # construct a working dir manifest
905 # construct a working dir manifest
899 mw = m1.copy()
906 mw = m1.copy()
900 mfw = mf1.copy()
907 mfw = mf1.copy()
901 for f in a + c + u:
908 for f in a + c + u:
902 mw[f] = ""
909 mw[f] = ""
903 mfw[f] = is_exec(self.wjoin(f))
910 mfw[f] = is_exec(self.wjoin(f))
904 for f in d:
911 for f in d:
905 if f in mw: del mw[f]
912 if f in mw: del mw[f]
906
913
907 for f, n in mw.iteritems():
914 for f, n in mw.iteritems():
908 if f in m2:
915 if f in m2:
909 s = 0
916 s = 0
910
917
911 # are files different?
918 # are files different?
912 if n != m2[f]:
919 if n != m2[f]:
913 a = ma.get(f, nullid)
920 a = ma.get(f, nullid)
914 # are both different from the ancestor?
921 # are both different from the ancestor?
915 if n != a and m2[f] != a:
922 if n != a and m2[f] != a:
916 self.ui.debug(" %s versions differ, resolve\n" % f)
923 self.ui.debug(" %s versions differ, resolve\n" % f)
917 merge[f] = (m1.get(f, nullid), m2[f])
924 merge[f] = (m1.get(f, nullid), m2[f])
918 # merge executable bits
925 # merge executable bits
919 # "if we changed or they changed, change in merge"
926 # "if we changed or they changed, change in merge"
920 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
927 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
921 mode = ((a^b) | (a^c)) ^ a
928 mode = ((a^b) | (a^c)) ^ a
922 merge[f] = (m1.get(f, nullid), m2[f], mode)
929 merge[f] = (m1.get(f, nullid), m2[f], mode)
923 s = 1
930 s = 1
924 # is this an unmodified file or are we clobbering?
931 # is this an unmodified file or are we clobbering?
925 elif mw[f] == m1[f] or force:
932 elif mw[f] == m1[f] or force:
926 self.ui.debug(" remote %s is newer, get\n" % f)
933 self.ui.debug(" remote %s is newer, get\n" % f)
927 get[f] = m2[f]
934 get[f] = m2[f]
928 s = 1
935 s = 1
929
936
930 if not s and mfw[f] != mf2[f]:
937 if not s and mfw[f] != mf2[f]:
931 if force:
938 if force:
932 self.ui.debug(" updating permissions for %s\n" % f)
939 self.ui.debug(" updating permissions for %s\n" % f)
933 set_exec(self.wjoin(f), mf2[f])
940 set_exec(self.wjoin(f), mf2[f])
934 else:
941 else:
935 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
942 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
936 mode = ((a^b) | (a^c)) ^ a
943 mode = ((a^b) | (a^c)) ^ a
937 if mode != b:
944 if mode != b:
938 self.ui.debug(" updating permissions for %s\n" % f)
945 self.ui.debug(" updating permissions for %s\n" % f)
939 set_exec(self.wjoin(f), mode)
946 set_exec(self.wjoin(f), mode)
940
947
941 del m2[f]
948 del m2[f]
942 elif f in ma:
949 elif f in ma:
943 if not force and n != ma[f]:
950 if not force and n != ma[f]:
944 r = self.ui.prompt(
951 r = self.ui.prompt(
945 (" local changed %s which remote deleted\n" % f) +
952 (" local changed %s which remote deleted\n" % f) +
946 "(k)eep or (d)elete?", "[kd]", "k")
953 "(k)eep or (d)elete?", "[kd]", "k")
947 if r == "d":
954 if r == "d":
948 remove.append(f)
955 remove.append(f)
949 else:
956 else:
950 self.ui.debug("other deleted %s\n" % f)
957 self.ui.debug("other deleted %s\n" % f)
951 remove.append(f) # other deleted it
958 remove.append(f) # other deleted it
952 else:
959 else:
953 if n == m1.get(f, nullid): # same as parent
960 if n == m1.get(f, nullid): # same as parent
954 self.ui.debug("remote deleted %s\n" % f)
961 self.ui.debug("remote deleted %s\n" % f)
955 remove.append(f)
962 remove.append(f)
956 else:
963 else:
957 self.ui.debug("working dir created %s, keeping\n" % f)
964 self.ui.debug("working dir created %s, keeping\n" % f)
958
965
959 for f, n in m2.iteritems():
966 for f, n in m2.iteritems():
960 if f[0] == "/": continue
967 if f[0] == "/": continue
961 if not force and f in ma and n != ma[f]:
968 if not force and f in ma and n != ma[f]:
962 r = self.ui.prompt(
969 r = self.ui.prompt(
963 ("remote changed %s which local deleted\n" % f) +
970 ("remote changed %s which local deleted\n" % f) +
964 "(k)eep or (d)elete?", "[kd]", "k")
971 "(k)eep or (d)elete?", "[kd]", "k")
965 if r == "d": remove.append(f)
972 if r == "d": remove.append(f)
966 else:
973 else:
967 self.ui.debug("remote created %s\n" % f)
974 self.ui.debug("remote created %s\n" % f)
968 get[f] = n
975 get[f] = n
969
976
970 del mw, m1, m2, ma
977 del mw, m1, m2, ma
971
978
972 if force:
979 if force:
973 for f in merge:
980 for f in merge:
974 get[f] = merge[f][1]
981 get[f] = merge[f][1]
975 merge = {}
982 merge = {}
976
983
977 if not merge:
984 if not merge:
978 # we don't need to do any magic, just jump to the new rev
985 # we don't need to do any magic, just jump to the new rev
979 mode = 'n'
986 mode = 'n'
980 p1, p2 = p2, nullid
987 p1, p2 = p2, nullid
981 else:
988 else:
982 if not allow:
989 if not allow:
983 self.ui.status("the following files conflict:\n")
990 self.ui.status("the following files conflict:\n")
984 for f in merge:
991 for f in merge:
985 self.ui.status(" %s\n" % f)
992 self.ui.status(" %s\n" % f)
986 self.ui.warn("aborting update due to conflicting files!\n")
993 self.ui.warn("aborting update due to conflicting files!\n")
987 self.ui.status("(use update -m to allow a merge)\n")
994 self.ui.status("(use update -m to allow a merge)\n")
988 return 1
995 return 1
989 # we have to remember what files we needed to get/change
996 # we have to remember what files we needed to get/change
990 # because any file that's different from either one of its
997 # because any file that's different from either one of its
991 # parents must be in the changeset
998 # parents must be in the changeset
992 mode = 'm'
999 mode = 'm'
993
1000
994 self.dirstate.setparents(p1, p2)
1001 self.dirstate.setparents(p1, p2)
995
1002
996 # get the files we don't need to change
1003 # get the files we don't need to change
997 files = get.keys()
1004 files = get.keys()
998 files.sort()
1005 files.sort()
999 for f in files:
1006 for f in files:
1000 if f[0] == "/": continue
1007 if f[0] == "/": continue
1001 self.ui.note("getting %s\n" % f)
1008 self.ui.note("getting %s\n" % f)
1002 t = self.file(f).read(get[f])
1009 t = self.file(f).read(get[f])
1003 try:
1010 try:
1004 self.wfile(f, "w").write(t)
1011 self.wfile(f, "w").write(t)
1005 except IOError:
1012 except IOError:
1006 os.makedirs(os.path.dirname(wp))
1013 os.makedirs(os.path.dirname(self.wjoin(f)))
1007 self.wfile(f, "w").write(t)
1014 self.wfile(f, "w").write(t)
1008 set_exec(self.wjoin(f), mf2[f])
1015 set_exec(self.wjoin(f), mf2[f])
1009 self.dirstate.update([f], mode)
1016 self.dirstate.update([f], mode)
1010
1017
1011 # merge the tricky bits
1018 # merge the tricky bits
1012 files = merge.keys()
1019 files = merge.keys()
1013 files.sort()
1020 files.sort()
1014 for f in files:
1021 for f in files:
1015 self.ui.status("merging %s\n" % f)
1022 self.ui.status("merging %s\n" % f)
1016 m, o, flag = merge[f]
1023 m, o, flag = merge[f]
1017 self.merge3(f, m, o)
1024 self.merge3(f, m, o)
1018 set_exec(wp, flag)
1025 set_exec(self.wjoin(f), flag)
1019 self.dirstate.update([f], 'm')
1026 self.dirstate.update([f], 'm')
1020
1027
1021 for f in remove:
1028 for f in remove:
1022 self.ui.note("removing %s\n" % f)
1029 self.ui.note("removing %s\n" % f)
1023 os.unlink(f)
1030 os.unlink(f)
1024 if mode == 'n':
1031 if mode == 'n':
1025 self.dirstate.forget(remove)
1032 self.dirstate.forget(remove)
1026 else:
1033 else:
1027 self.dirstate.update(remove, 'r')
1034 self.dirstate.update(remove, 'r')
1028
1035
1029 def merge3(self, fn, my, other):
1036 def merge3(self, fn, my, other):
1030 """perform a 3-way merge in the working directory"""
1037 """perform a 3-way merge in the working directory"""
1031
1038
1032 def temp(prefix, node):
1039 def temp(prefix, node):
1033 pre = "%s~%s." % (os.path.basename(fn), prefix)
1040 pre = "%s~%s." % (os.path.basename(fn), prefix)
1034 (fd, name) = tempfile.mkstemp("", pre)
1041 (fd, name) = tempfile.mkstemp("", pre)
1035 f = os.fdopen(fd, "w")
1042 f = os.fdopen(fd, "w")
1036 f.write(fl.revision(node))
1043 f.write(fl.revision(node))
1037 f.close()
1044 f.close()
1038 return name
1045 return name
1039
1046
1040 fl = self.file(fn)
1047 fl = self.file(fn)
1041 base = fl.ancestor(my, other)
1048 base = fl.ancestor(my, other)
1042 a = self.wjoin(fn)
1049 a = self.wjoin(fn)
1043 b = temp("other", other)
1050 b = temp("other", other)
1044 c = temp("base", base)
1051 c = temp("base", base)
1045
1052
1046 self.ui.note("resolving %s\n" % fn)
1053 self.ui.note("resolving %s\n" % fn)
1047 self.ui.debug("file %s: other %s ancestor %s\n" %
1054 self.ui.debug("file %s: other %s ancestor %s\n" %
1048 (fn, short(other), short(base)))
1055 (fn, short(other), short(base)))
1049
1056
1050 cmd = os.environ.get("HGMERGE", "hgmerge")
1057 cmd = os.environ.get("HGMERGE", "hgmerge")
1051 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1058 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1052 if r:
1059 if r:
1053 self.ui.warn("merging %s failed!\n" % fn)
1060 self.ui.warn("merging %s failed!\n" % fn)
1054
1061
1055 os.unlink(b)
1062 os.unlink(b)
1056 os.unlink(c)
1063 os.unlink(c)
1057
1064
1058 def verify(self):
1065 def verify(self):
1059 filelinkrevs = {}
1066 filelinkrevs = {}
1060 filenodes = {}
1067 filenodes = {}
1061 manifestchangeset = {}
1068 manifestchangeset = {}
1062 changesets = revisions = files = 0
1069 changesets = revisions = files = 0
1063 errors = 0
1070 errors = 0
1064
1071
1065 self.ui.status("checking changesets\n")
1072 self.ui.status("checking changesets\n")
1066 for i in range(self.changelog.count()):
1073 for i in range(self.changelog.count()):
1067 changesets += 1
1074 changesets += 1
1068 n = self.changelog.node(i)
1075 n = self.changelog.node(i)
1069 for p in self.changelog.parents(n):
1076 for p in self.changelog.parents(n):
1070 if p not in self.changelog.nodemap:
1077 if p not in self.changelog.nodemap:
1071 self.ui.warn("changeset %s has unknown parent %s\n" %
1078 self.ui.warn("changeset %s has unknown parent %s\n" %
1072 (short(n), short(p)))
1079 (short(n), short(p)))
1073 errors += 1
1080 errors += 1
1074 try:
1081 try:
1075 changes = self.changelog.read(n)
1082 changes = self.changelog.read(n)
1076 except Exception, inst:
1083 except Exception, inst:
1077 self.ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
1084 self.ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
1078 errors += 1
1085 errors += 1
1079
1086
1080 manifestchangeset[changes[0]] = n
1087 manifestchangeset[changes[0]] = n
1081 for f in changes[3]:
1088 for f in changes[3]:
1082 filelinkrevs.setdefault(f, []).append(i)
1089 filelinkrevs.setdefault(f, []).append(i)
1083
1090
1084 self.ui.status("checking manifests\n")
1091 self.ui.status("checking manifests\n")
1085 for i in range(self.manifest.count()):
1092 for i in range(self.manifest.count()):
1086 n = self.manifest.node(i)
1093 n = self.manifest.node(i)
1087 for p in self.manifest.parents(n):
1094 for p in self.manifest.parents(n):
1088 if p not in self.manifest.nodemap:
1095 if p not in self.manifest.nodemap:
1089 self.ui.warn("manifest %s has unknown parent %s\n" %
1096 self.ui.warn("manifest %s has unknown parent %s\n" %
1090 (short(n), short(p)))
1097 (short(n), short(p)))
1091 errors += 1
1098 errors += 1
1092 ca = self.changelog.node(self.manifest.linkrev(n))
1099 ca = self.changelog.node(self.manifest.linkrev(n))
1093 cc = manifestchangeset[n]
1100 cc = manifestchangeset[n]
1094 if ca != cc:
1101 if ca != cc:
1095 self.ui.warn("manifest %s points to %s, not %s\n" %
1102 self.ui.warn("manifest %s points to %s, not %s\n" %
1096 (hex(n), hex(ca), hex(cc)))
1103 (hex(n), hex(ca), hex(cc)))
1097 errors += 1
1104 errors += 1
1098
1105
1099 try:
1106 try:
1100 delta = mdiff.patchtext(self.manifest.delta(n))
1107 delta = mdiff.patchtext(self.manifest.delta(n))
1101 except KeyboardInterrupt:
1108 except KeyboardInterrupt:
1102 print "aborted"
1109 print "aborted"
1103 sys.exit(0)
1110 sys.exit(0)
1104 except Exception, inst:
1111 except Exception, inst:
1105 self.ui.warn("unpacking manifest %s: %s\n"
1112 self.ui.warn("unpacking manifest %s: %s\n"
1106 % (short(n), inst))
1113 % (short(n), inst))
1107 errors += 1
1114 errors += 1
1108
1115
1109 ff = [ l.split('\0') for l in delta.splitlines() ]
1116 ff = [ l.split('\0') for l in delta.splitlines() ]
1110 for f, fn in ff:
1117 for f, fn in ff:
1111 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1118 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1112
1119
1113 self.ui.status("crosschecking files in changesets and manifests\n")
1120 self.ui.status("crosschecking files in changesets and manifests\n")
1114 for f in filenodes:
1121 for f in filenodes:
1115 if f not in filelinkrevs:
1122 if f not in filelinkrevs:
1116 self.ui.warn("file %s in manifest but not in changesets\n" % f)
1123 self.ui.warn("file %s in manifest but not in changesets\n" % f)
1117 errors += 1
1124 errors += 1
1118
1125
1119 for f in filelinkrevs:
1126 for f in filelinkrevs:
1120 if f not in filenodes:
1127 if f not in filenodes:
1121 self.ui.warn("file %s in changeset but not in manifest\n" % f)
1128 self.ui.warn("file %s in changeset but not in manifest\n" % f)
1122 errors += 1
1129 errors += 1
1123
1130
1124 self.ui.status("checking files\n")
1131 self.ui.status("checking files\n")
1125 ff = filenodes.keys()
1132 ff = filenodes.keys()
1126 ff.sort()
1133 ff.sort()
1127 for f in ff:
1134 for f in ff:
1128 if f == "/dev/null": continue
1135 if f == "/dev/null": continue
1129 files += 1
1136 files += 1
1130 fl = self.file(f)
1137 fl = self.file(f)
1131 nodes = { nullid: 1 }
1138 nodes = { nullid: 1 }
1132 for i in range(fl.count()):
1139 for i in range(fl.count()):
1133 revisions += 1
1140 revisions += 1
1134 n = fl.node(i)
1141 n = fl.node(i)
1135
1142
1136 if n not in filenodes[f]:
1143 if n not in filenodes[f]:
1137 self.ui.warn("%s: %d:%s not in manifests\n"
1144 self.ui.warn("%s: %d:%s not in manifests\n"
1138 % (f, i, short(n)))
1145 % (f, i, short(n)))
1139 print len(filenodes[f].keys()), fl.count(), f
1146 print len(filenodes[f].keys()), fl.count(), f
1140 errors += 1
1147 errors += 1
1141 else:
1148 else:
1142 del filenodes[f][n]
1149 del filenodes[f][n]
1143
1150
1144 flr = fl.linkrev(n)
1151 flr = fl.linkrev(n)
1145 if flr not in filelinkrevs[f]:
1152 if flr not in filelinkrevs[f]:
1146 self.ui.warn("%s:%s points to unexpected changeset %d\n"
1153 self.ui.warn("%s:%s points to unexpected changeset %d\n"
1147 % (f, short(n), fl.linkrev(n)))
1154 % (f, short(n), fl.linkrev(n)))
1148 errors += 1
1155 errors += 1
1149 else:
1156 else:
1150 filelinkrevs[f].remove(flr)
1157 filelinkrevs[f].remove(flr)
1151
1158
1152 # verify contents
1159 # verify contents
1153 try:
1160 try:
1154 t = fl.read(n)
1161 t = fl.read(n)
1155 except Exception, inst:
1162 except Exception, inst:
1156 self.ui.warn("unpacking file %s %s: %s\n"
1163 self.ui.warn("unpacking file %s %s: %s\n"
1157 % (f, short(n), inst))
1164 % (f, short(n), inst))
1158 errors += 1
1165 errors += 1
1159
1166
1160 # verify parents
1167 # verify parents
1161 (p1, p2) = fl.parents(n)
1168 (p1, p2) = fl.parents(n)
1162 if p1 not in nodes:
1169 if p1 not in nodes:
1163 self.ui.warn("file %s:%s unknown parent 1 %s" %
1170 self.ui.warn("file %s:%s unknown parent 1 %s" %
1164 (f, short(n), short(p1)))
1171 (f, short(n), short(p1)))
1165 errors += 1
1172 errors += 1
1166 if p2 not in nodes:
1173 if p2 not in nodes:
1167 self.ui.warn("file %s:%s unknown parent 2 %s" %
1174 self.ui.warn("file %s:%s unknown parent 2 %s" %
1168 (f, short(n), short(p1)))
1175 (f, short(n), short(p1)))
1169 errors += 1
1176 errors += 1
1170 nodes[n] = 1
1177 nodes[n] = 1
1171
1178
1172 # cross-check
1179 # cross-check
1173 for node in filenodes[f]:
1180 for node in filenodes[f]:
1174 self.ui.warn("node %s in manifests not in %s\n"
1181 self.ui.warn("node %s in manifests not in %s\n"
1175 % (hex(n), f))
1182 % (hex(n), f))
1176 errors += 1
1183 errors += 1
1177
1184
1178 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1185 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1179 (files, changesets, revisions))
1186 (files, changesets, revisions))
1180
1187
1181 if errors:
1188 if errors:
1182 self.ui.warn("%d integrity errors encountered!\n" % errors)
1189 self.ui.warn("%d integrity errors encountered!\n" % errors)
1183 return 1
1190 return 1
1184
1191
1185 class remoterepository:
1192 class remoterepository:
1186 def __init__(self, ui, path):
1193 def __init__(self, ui, path):
1187 self.url = path
1194 self.url = path
1188 self.ui = ui
1195 self.ui = ui
1189
1196
1190 def do_cmd(self, cmd, **args):
1197 def do_cmd(self, cmd, **args):
1191 self.ui.debug("sending %s command\n" % cmd)
1198 self.ui.debug("sending %s command\n" % cmd)
1192 q = {"cmd": cmd}
1199 q = {"cmd": cmd}
1193 q.update(args)
1200 q.update(args)
1194 qs = urllib.urlencode(q)
1201 qs = urllib.urlencode(q)
1195 cu = "%s?%s" % (self.url, qs)
1202 cu = "%s?%s" % (self.url, qs)
1196 return urllib.urlopen(cu)
1203 return urllib.urlopen(cu)
1197
1204
1198 def heads(self):
1205 def heads(self):
1199 d = self.do_cmd("heads").read()
1206 d = self.do_cmd("heads").read()
1200 try:
1207 try:
1201 return map(bin, d[:-1].split(" "))
1208 return map(bin, d[:-1].split(" "))
1202 except:
1209 except:
1203 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1210 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1204 raise
1211 raise
1205
1212
1206 def branches(self, nodes):
1213 def branches(self, nodes):
1207 n = " ".join(map(hex, nodes))
1214 n = " ".join(map(hex, nodes))
1208 d = self.do_cmd("branches", nodes=n).read()
1215 d = self.do_cmd("branches", nodes=n).read()
1209 try:
1216 try:
1210 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1217 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1211 return br
1218 return br
1212 except:
1219 except:
1213 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1220 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1214 raise
1221 raise
1215
1222
1216 def between(self, pairs):
1223 def between(self, pairs):
1217 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1224 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1218 d = self.do_cmd("between", pairs=n).read()
1225 d = self.do_cmd("between", pairs=n).read()
1219 try:
1226 try:
1220 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1227 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1221 return p
1228 return p
1222 except:
1229 except:
1223 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1230 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1224 raise
1231 raise
1225
1232
1226 def changegroup(self, nodes):
1233 def changegroup(self, nodes):
1227 n = " ".join(map(hex, nodes))
1234 n = " ".join(map(hex, nodes))
1228 zd = zlib.decompressobj()
1235 zd = zlib.decompressobj()
1229 f = self.do_cmd("changegroup", roots=n)
1236 f = self.do_cmd("changegroup", roots=n)
1230 bytes = 0
1237 bytes = 0
1231 while 1:
1238 while 1:
1232 d = f.read(4096)
1239 d = f.read(4096)
1233 bytes += len(d)
1240 bytes += len(d)
1234 if not d:
1241 if not d:
1235 yield zd.flush()
1242 yield zd.flush()
1236 break
1243 break
1237 yield zd.decompress(d)
1244 yield zd.decompress(d)
1238 self.ui.note("%d bytes of data transfered\n" % bytes)
1245 self.ui.note("%d bytes of data transfered\n" % bytes)
1239
1246
1240 def repository(ui, path=None, create=0):
1247 def repository(ui, path=None, create=0):
1241 if path and path[:7] == "http://":
1248 if path and path[:7] == "http://":
1242 return remoterepository(ui, path)
1249 return remoterepository(ui, path)
1243 if path and path[:5] == "hg://":
1250 if path and path[:5] == "hg://":
1244 return remoterepository(ui, path.replace("hg://", "http://"))
1251 return remoterepository(ui, path.replace("hg://", "http://"))
1245 if path and path[:11] == "old-http://":
1252 if path and path[:11] == "old-http://":
1246 return localrepository(ui, path.replace("old-http://", "http://"))
1253 return localrepository(ui, path.replace("old-http://", "http://"))
1247 else:
1254 else:
1248 return localrepository(ui, path, create)
1255 return localrepository(ui, path, create)
1249
1256
1250 class httprangereader:
1257 class httprangereader:
1251 def __init__(self, url):
1258 def __init__(self, url):
1252 self.url = url
1259 self.url = url
1253 self.pos = 0
1260 self.pos = 0
1254 def seek(self, pos):
1261 def seek(self, pos):
1255 self.pos = pos
1262 self.pos = pos
1256 def read(self, bytes=None):
1263 def read(self, bytes=None):
1257 opener = urllib2.build_opener(byterange.HTTPRangeHandler())
1264 opener = urllib2.build_opener(byterange.HTTPRangeHandler())
1258 urllib2.install_opener(opener)
1265 urllib2.install_opener(opener)
1259 req = urllib2.Request(self.url)
1266 req = urllib2.Request(self.url)
1260 end = ''
1267 end = ''
1261 if bytes: end = self.pos + bytes
1268 if bytes: end = self.pos + bytes
1262 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
1269 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
1263 f = urllib2.urlopen(req)
1270 f = urllib2.urlopen(req)
1264 return f.read()
1271 return f.read()
General Comments 0
You need to be logged in to leave comments. Login now