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