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