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