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