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