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