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