##// END OF EJS Templates
Fix verify error reporting bug...
mpm@selenic.com -
r721:fed842bb default
parent child Browse files
Show More
@@ -1,1888 +1,1888 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 demandload(globals(), "bisect select")
14 demandload(globals(), "bisect select")
15
15
16 class filelog(revlog):
16 class filelog(revlog):
17 def __init__(self, opener, path):
17 def __init__(self, opener, path):
18 revlog.__init__(self, opener,
18 revlog.__init__(self, opener,
19 os.path.join("data", path + ".i"),
19 os.path.join("data", path + ".i"),
20 os.path.join("data", path + ".d"))
20 os.path.join("data", path + ".d"))
21
21
22 def read(self, node):
22 def read(self, node):
23 t = self.revision(node)
23 t = self.revision(node)
24 if not t.startswith('\1\n'):
24 if not t.startswith('\1\n'):
25 return t
25 return t
26 s = t.find('\1\n', 2)
26 s = t.find('\1\n', 2)
27 return t[s+2:]
27 return t[s+2:]
28
28
29 def readmeta(self, node):
29 def readmeta(self, node):
30 t = self.revision(node)
30 t = self.revision(node)
31 if not t.startswith('\1\n'):
31 if not t.startswith('\1\n'):
32 return t
32 return t
33 s = t.find('\1\n', 2)
33 s = t.find('\1\n', 2)
34 mt = t[2:s]
34 mt = t[2:s]
35 for l in mt.splitlines():
35 for l in mt.splitlines():
36 k, v = l.split(": ", 1)
36 k, v = l.split(": ", 1)
37 m[k] = v
37 m[k] = v
38 return m
38 return m
39
39
40 def add(self, text, meta, transaction, link, p1=None, p2=None):
40 def add(self, text, meta, transaction, link, p1=None, p2=None):
41 if meta or text.startswith('\1\n'):
41 if meta or text.startswith('\1\n'):
42 mt = ""
42 mt = ""
43 if meta:
43 if meta:
44 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
44 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
45 text = "\1\n" + "".join(mt) + "\1\n" + text
45 text = "\1\n" + "".join(mt) + "\1\n" + text
46 return self.addrevision(text, transaction, link, p1, p2)
46 return self.addrevision(text, transaction, link, p1, p2)
47
47
48 def annotate(self, node):
48 def annotate(self, node):
49
49
50 def decorate(text, rev):
50 def decorate(text, rev):
51 return ([rev] * len(text.splitlines()), text)
51 return ([rev] * len(text.splitlines()), text)
52
52
53 def pair(parent, child):
53 def pair(parent, child):
54 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
54 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
55 child[0][b1:b2] = parent[0][a1:a2]
55 child[0][b1:b2] = parent[0][a1:a2]
56 return child
56 return child
57
57
58 # find all ancestors
58 # find all ancestors
59 needed = {node:1}
59 needed = {node:1}
60 visit = [node]
60 visit = [node]
61 while visit:
61 while visit:
62 n = visit.pop(0)
62 n = visit.pop(0)
63 for p in self.parents(n):
63 for p in self.parents(n):
64 if p not in needed:
64 if p not in needed:
65 needed[p] = 1
65 needed[p] = 1
66 visit.append(p)
66 visit.append(p)
67 else:
67 else:
68 # count how many times we'll use this
68 # count how many times we'll use this
69 needed[p] += 1
69 needed[p] += 1
70
70
71 # sort by revision which is a topological order
71 # sort by revision which is a topological order
72 visit = [ (self.rev(n), n) for n in needed.keys() ]
72 visit = [ (self.rev(n), n) for n in needed.keys() ]
73 visit.sort()
73 visit.sort()
74 hist = {}
74 hist = {}
75
75
76 for r,n in visit:
76 for r,n in visit:
77 curr = decorate(self.read(n), self.linkrev(n))
77 curr = decorate(self.read(n), self.linkrev(n))
78 for p in self.parents(n):
78 for p in self.parents(n):
79 if p != nullid:
79 if p != nullid:
80 curr = pair(hist[p], curr)
80 curr = pair(hist[p], curr)
81 # trim the history of unneeded revs
81 # trim the history of unneeded revs
82 needed[p] -= 1
82 needed[p] -= 1
83 if not needed[p]:
83 if not needed[p]:
84 del hist[p]
84 del hist[p]
85 hist[n] = curr
85 hist[n] = curr
86
86
87 return zip(hist[n][0], hist[n][1].splitlines(1))
87 return zip(hist[n][0], hist[n][1].splitlines(1))
88
88
89 class manifest(revlog):
89 class manifest(revlog):
90 def __init__(self, opener):
90 def __init__(self, opener):
91 self.mapcache = None
91 self.mapcache = None
92 self.listcache = None
92 self.listcache = None
93 self.addlist = None
93 self.addlist = None
94 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
94 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
95
95
96 def read(self, node):
96 def read(self, node):
97 if node == nullid: return {} # don't upset local cache
97 if node == nullid: return {} # don't upset local cache
98 if self.mapcache and self.mapcache[0] == node:
98 if self.mapcache and self.mapcache[0] == node:
99 return self.mapcache[1]
99 return self.mapcache[1]
100 text = self.revision(node)
100 text = self.revision(node)
101 map = {}
101 map = {}
102 flag = {}
102 flag = {}
103 self.listcache = (text, text.splitlines(1))
103 self.listcache = (text, text.splitlines(1))
104 for l in self.listcache[1]:
104 for l in self.listcache[1]:
105 (f, n) = l.split('\0')
105 (f, n) = l.split('\0')
106 map[f] = bin(n[:40])
106 map[f] = bin(n[:40])
107 flag[f] = (n[40:-1] == "x")
107 flag[f] = (n[40:-1] == "x")
108 self.mapcache = (node, map, flag)
108 self.mapcache = (node, map, flag)
109 return map
109 return map
110
110
111 def readflags(self, node):
111 def readflags(self, node):
112 if node == nullid: return {} # don't upset local cache
112 if node == nullid: return {} # don't upset local cache
113 if not self.mapcache or self.mapcache[0] != node:
113 if not self.mapcache or self.mapcache[0] != node:
114 self.read(node)
114 self.read(node)
115 return self.mapcache[2]
115 return self.mapcache[2]
116
116
117 def diff(self, a, b):
117 def diff(self, a, b):
118 # this is sneaky, as we're not actually using a and b
118 # this is sneaky, as we're not actually using a and b
119 if self.listcache and self.addlist and self.listcache[0] == a:
119 if self.listcache and self.addlist and self.listcache[0] == a:
120 d = mdiff.diff(self.listcache[1], self.addlist, 1)
120 d = mdiff.diff(self.listcache[1], self.addlist, 1)
121 if mdiff.patch(a, d) != b:
121 if mdiff.patch(a, d) != b:
122 sys.stderr.write("*** sortdiff failed, falling back ***\n")
122 sys.stderr.write("*** sortdiff failed, falling back ***\n")
123 return mdiff.textdiff(a, b)
123 return mdiff.textdiff(a, b)
124 return d
124 return d
125 else:
125 else:
126 return mdiff.textdiff(a, b)
126 return mdiff.textdiff(a, b)
127
127
128 def add(self, map, flags, transaction, link, p1=None, p2=None,changed=None):
128 def add(self, map, flags, transaction, link, p1=None, p2=None,changed=None):
129 # directly generate the mdiff delta from the data collected during
129 # directly generate the mdiff delta from the data collected during
130 # the bisect loop below
130 # the bisect loop below
131 def gendelta(delta):
131 def gendelta(delta):
132 i = 0
132 i = 0
133 result = []
133 result = []
134 while i < len(delta):
134 while i < len(delta):
135 start = delta[i][2]
135 start = delta[i][2]
136 end = delta[i][3]
136 end = delta[i][3]
137 l = delta[i][4]
137 l = delta[i][4]
138 if l == None:
138 if l == None:
139 l = ""
139 l = ""
140 while i < len(delta) - 1 and start <= delta[i+1][2] and end >= delta[i+1][2]:
140 while i < len(delta) - 1 and start <= delta[i+1][2] and end >= delta[i+1][2]:
141 if delta[i+1][3] > end:
141 if delta[i+1][3] > end:
142 end = delta[i+1][3]
142 end = delta[i+1][3]
143 if delta[i+1][4]:
143 if delta[i+1][4]:
144 l += delta[i+1][4]
144 l += delta[i+1][4]
145 i += 1
145 i += 1
146 result.append(struct.pack(">lll", start, end, len(l)) + l)
146 result.append(struct.pack(">lll", start, end, len(l)) + l)
147 i += 1
147 i += 1
148 return result
148 return result
149
149
150 # apply the changes collected during the bisect loop to our addlist
150 # apply the changes collected during the bisect loop to our addlist
151 def addlistdelta(addlist, delta):
151 def addlistdelta(addlist, delta):
152 # apply the deltas to the addlist. start from the bottom up
152 # apply the deltas to the addlist. start from the bottom up
153 # so changes to the offsets don't mess things up.
153 # so changes to the offsets don't mess things up.
154 i = len(delta)
154 i = len(delta)
155 while i > 0:
155 while i > 0:
156 i -= 1
156 i -= 1
157 start = delta[i][0]
157 start = delta[i][0]
158 end = delta[i][1]
158 end = delta[i][1]
159 if delta[i][4]:
159 if delta[i][4]:
160 addlist[start:end] = [delta[i][4]]
160 addlist[start:end] = [delta[i][4]]
161 else:
161 else:
162 del addlist[start:end]
162 del addlist[start:end]
163 return addlist
163 return addlist
164
164
165 # calculate the byte offset of the start of each line in the
165 # calculate the byte offset of the start of each line in the
166 # manifest
166 # manifest
167 def calcoffsets(addlist):
167 def calcoffsets(addlist):
168 offsets = [0] * (len(addlist) + 1)
168 offsets = [0] * (len(addlist) + 1)
169 offset = 0
169 offset = 0
170 i = 0
170 i = 0
171 while i < len(addlist):
171 while i < len(addlist):
172 offsets[i] = offset
172 offsets[i] = offset
173 offset += len(addlist[i])
173 offset += len(addlist[i])
174 i += 1
174 i += 1
175 offsets[i] = offset
175 offsets[i] = offset
176 return offsets
176 return offsets
177
177
178 # if we're using the listcache, make sure it is valid and
178 # if we're using the listcache, make sure it is valid and
179 # parented by the same node we're diffing against
179 # parented by the same node we're diffing against
180 if not changed or not self.listcache or not p1 or self.mapcache[0] != p1:
180 if not changed or not self.listcache or not p1 or self.mapcache[0] != p1:
181 files = map.keys()
181 files = map.keys()
182 files.sort()
182 files.sort()
183
183
184 self.addlist = ["%s\000%s%s\n" %
184 self.addlist = ["%s\000%s%s\n" %
185 (f, hex(map[f]), flags[f] and "x" or '')
185 (f, hex(map[f]), flags[f] and "x" or '')
186 for f in files]
186 for f in files]
187 cachedelta = None
187 cachedelta = None
188 else:
188 else:
189 addlist = self.listcache[1]
189 addlist = self.listcache[1]
190
190
191 # find the starting offset for each line in the add list
191 # find the starting offset for each line in the add list
192 offsets = calcoffsets(addlist)
192 offsets = calcoffsets(addlist)
193
193
194 # combine the changed lists into one list for sorting
194 # combine the changed lists into one list for sorting
195 work = [[x, 0] for x in changed[0]]
195 work = [[x, 0] for x in changed[0]]
196 work[len(work):] = [[x, 1] for x in changed[1]]
196 work[len(work):] = [[x, 1] for x in changed[1]]
197 work.sort()
197 work.sort()
198
198
199 delta = []
199 delta = []
200 bs = 0
200 bs = 0
201
201
202 for w in work:
202 for w in work:
203 f = w[0]
203 f = w[0]
204 # bs will either be the index of the item or the insertion point
204 # bs will either be the index of the item or the insertion point
205 bs = bisect.bisect(addlist, f, bs)
205 bs = bisect.bisect(addlist, f, bs)
206 if bs < len(addlist):
206 if bs < len(addlist):
207 fn = addlist[bs][:addlist[bs].index('\0')]
207 fn = addlist[bs][:addlist[bs].index('\0')]
208 else:
208 else:
209 fn = None
209 fn = None
210 if w[1] == 0:
210 if w[1] == 0:
211 l = "%s\000%s%s\n" % (f, hex(map[f]), flags[f] and "x" or '')
211 l = "%s\000%s%s\n" % (f, hex(map[f]), flags[f] and "x" or '')
212 else:
212 else:
213 l = None
213 l = None
214 start = bs
214 start = bs
215 if fn != f:
215 if fn != f:
216 # item not found, insert a new one
216 # item not found, insert a new one
217 end = bs
217 end = bs
218 if w[1] == 1:
218 if w[1] == 1:
219 sys.stderr.write("failed to remove %s from manifest\n"
219 sys.stderr.write("failed to remove %s from manifest\n"
220 % f)
220 % f)
221 sys.exit(1)
221 sys.exit(1)
222 else:
222 else:
223 # item is found, replace/delete the existing line
223 # item is found, replace/delete the existing line
224 end = bs + 1
224 end = bs + 1
225 delta.append([start, end, offsets[start], offsets[end], l])
225 delta.append([start, end, offsets[start], offsets[end], l])
226
226
227 self.addlist = addlistdelta(addlist, delta)
227 self.addlist = addlistdelta(addlist, delta)
228 if self.mapcache[0] == self.tip():
228 if self.mapcache[0] == self.tip():
229 cachedelta = "".join(gendelta(delta))
229 cachedelta = "".join(gendelta(delta))
230 else:
230 else:
231 cachedelta = None
231 cachedelta = None
232
232
233 text = "".join(self.addlist)
233 text = "".join(self.addlist)
234 if cachedelta and mdiff.patch(self.listcache[0], cachedelta) != text:
234 if cachedelta and mdiff.patch(self.listcache[0], cachedelta) != text:
235 sys.stderr.write("manifest delta failure\n")
235 sys.stderr.write("manifest delta failure\n")
236 sys.exit(1)
236 sys.exit(1)
237 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
237 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
238 self.mapcache = (n, map, flags)
238 self.mapcache = (n, map, flags)
239 self.listcache = (text, self.addlist)
239 self.listcache = (text, self.addlist)
240 self.addlist = None
240 self.addlist = None
241
241
242 return n
242 return n
243
243
244 class changelog(revlog):
244 class changelog(revlog):
245 def __init__(self, opener):
245 def __init__(self, opener):
246 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
246 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
247
247
248 def extract(self, text):
248 def extract(self, text):
249 if not text:
249 if not text:
250 return (nullid, "", "0", [], "")
250 return (nullid, "", "0", [], "")
251 last = text.index("\n\n")
251 last = text.index("\n\n")
252 desc = text[last + 2:]
252 desc = text[last + 2:]
253 l = text[:last].splitlines()
253 l = text[:last].splitlines()
254 manifest = bin(l[0])
254 manifest = bin(l[0])
255 user = l[1]
255 user = l[1]
256 date = l[2]
256 date = l[2]
257 files = l[3:]
257 files = l[3:]
258 return (manifest, user, date, files, desc)
258 return (manifest, user, date, files, desc)
259
259
260 def read(self, node):
260 def read(self, node):
261 return self.extract(self.revision(node))
261 return self.extract(self.revision(node))
262
262
263 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
263 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
264 user=None, date=None):
264 user=None, date=None):
265 date = date or "%d %d" % (time.time(), time.timezone)
265 date = date or "%d %d" % (time.time(), time.timezone)
266 list.sort()
266 list.sort()
267 l = [hex(manifest), user, date] + list + ["", desc]
267 l = [hex(manifest), user, date] + list + ["", desc]
268 text = "\n".join(l)
268 text = "\n".join(l)
269 return self.addrevision(text, transaction, self.count(), p1, p2)
269 return self.addrevision(text, transaction, self.count(), p1, p2)
270
270
271 class dirstate:
271 class dirstate:
272 def __init__(self, opener, ui, root):
272 def __init__(self, opener, ui, root):
273 self.opener = opener
273 self.opener = opener
274 self.root = root
274 self.root = root
275 self.dirty = 0
275 self.dirty = 0
276 self.ui = ui
276 self.ui = ui
277 self.map = None
277 self.map = None
278 self.pl = None
278 self.pl = None
279 self.copies = {}
279 self.copies = {}
280
280
281 def __del__(self):
281 def __del__(self):
282 if self.dirty:
282 if self.dirty:
283 self.write()
283 self.write()
284
284
285 def __getitem__(self, key):
285 def __getitem__(self, key):
286 try:
286 try:
287 return self.map[key]
287 return self.map[key]
288 except TypeError:
288 except TypeError:
289 self.read()
289 self.read()
290 return self[key]
290 return self[key]
291
291
292 def __contains__(self, key):
292 def __contains__(self, key):
293 if not self.map: self.read()
293 if not self.map: self.read()
294 return key in self.map
294 return key in self.map
295
295
296 def parents(self):
296 def parents(self):
297 if not self.pl:
297 if not self.pl:
298 self.read()
298 self.read()
299 return self.pl
299 return self.pl
300
300
301 def setparents(self, p1, p2 = nullid):
301 def setparents(self, p1, p2 = nullid):
302 self.dirty = 1
302 self.dirty = 1
303 self.pl = p1, p2
303 self.pl = p1, p2
304
304
305 def state(self, key):
305 def state(self, key):
306 try:
306 try:
307 return self[key][0]
307 return self[key][0]
308 except KeyError:
308 except KeyError:
309 return "?"
309 return "?"
310
310
311 def read(self):
311 def read(self):
312 if self.map is not None: return self.map
312 if self.map is not None: return self.map
313
313
314 self.map = {}
314 self.map = {}
315 self.pl = [nullid, nullid]
315 self.pl = [nullid, nullid]
316 try:
316 try:
317 st = self.opener("dirstate").read()
317 st = self.opener("dirstate").read()
318 if not st: return
318 if not st: return
319 except: return
319 except: return
320
320
321 self.pl = [st[:20], st[20: 40]]
321 self.pl = [st[:20], st[20: 40]]
322
322
323 pos = 40
323 pos = 40
324 while pos < len(st):
324 while pos < len(st):
325 e = struct.unpack(">cllll", st[pos:pos+17])
325 e = struct.unpack(">cllll", st[pos:pos+17])
326 l = e[4]
326 l = e[4]
327 pos += 17
327 pos += 17
328 f = st[pos:pos + l]
328 f = st[pos:pos + l]
329 if '\0' in f:
329 if '\0' in f:
330 f, c = f.split('\0')
330 f, c = f.split('\0')
331 self.copies[f] = c
331 self.copies[f] = c
332 self.map[f] = e[:4]
332 self.map[f] = e[:4]
333 pos += l
333 pos += l
334
334
335 def copy(self, source, dest):
335 def copy(self, source, dest):
336 self.read()
336 self.read()
337 self.dirty = 1
337 self.dirty = 1
338 self.copies[dest] = source
338 self.copies[dest] = source
339
339
340 def copied(self, file):
340 def copied(self, file):
341 return self.copies.get(file, None)
341 return self.copies.get(file, None)
342
342
343 def update(self, files, state):
343 def update(self, files, state):
344 ''' current states:
344 ''' current states:
345 n normal
345 n normal
346 m needs merging
346 m needs merging
347 r marked for removal
347 r marked for removal
348 a marked for addition'''
348 a marked for addition'''
349
349
350 if not files: return
350 if not files: return
351 self.read()
351 self.read()
352 self.dirty = 1
352 self.dirty = 1
353 for f in files:
353 for f in files:
354 if state == "r":
354 if state == "r":
355 self.map[f] = ('r', 0, 0, 0)
355 self.map[f] = ('r', 0, 0, 0)
356 else:
356 else:
357 s = os.stat(os.path.join(self.root, f))
357 s = os.stat(os.path.join(self.root, f))
358 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
358 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
359
359
360 def forget(self, files):
360 def forget(self, files):
361 if not files: return
361 if not files: return
362 self.read()
362 self.read()
363 self.dirty = 1
363 self.dirty = 1
364 for f in files:
364 for f in files:
365 try:
365 try:
366 del self.map[f]
366 del self.map[f]
367 except KeyError:
367 except KeyError:
368 self.ui.warn("not in dirstate: %s!\n" % f)
368 self.ui.warn("not in dirstate: %s!\n" % f)
369 pass
369 pass
370
370
371 def clear(self):
371 def clear(self):
372 self.map = {}
372 self.map = {}
373 self.dirty = 1
373 self.dirty = 1
374
374
375 def write(self):
375 def write(self):
376 st = self.opener("dirstate", "w")
376 st = self.opener("dirstate", "w")
377 st.write("".join(self.pl))
377 st.write("".join(self.pl))
378 for f, e in self.map.items():
378 for f, e in self.map.items():
379 c = self.copied(f)
379 c = self.copied(f)
380 if c:
380 if c:
381 f = f + "\0" + c
381 f = f + "\0" + c
382 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
382 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
383 st.write(e + f)
383 st.write(e + f)
384 self.dirty = 0
384 self.dirty = 0
385
385
386 def changes(self, files, ignore):
386 def changes(self, files, ignore):
387 self.read()
387 self.read()
388 dc = self.map.copy()
388 dc = self.map.copy()
389 lookup, changed, added, unknown = [], [], [], []
389 lookup, changed, added, unknown = [], [], [], []
390
390
391 # compare all files by default
391 # compare all files by default
392 if not files: files = [self.root]
392 if not files: files = [self.root]
393
393
394 # recursive generator of all files listed
394 # recursive generator of all files listed
395 def walk(files):
395 def walk(files):
396 for f in util.unique(files):
396 for f in util.unique(files):
397 f = os.path.join(self.root, f)
397 f = os.path.join(self.root, f)
398 if os.path.isdir(f):
398 if os.path.isdir(f):
399 for dir, subdirs, fl in os.walk(f):
399 for dir, subdirs, fl in os.walk(f):
400 d = dir[len(self.root) + 1:]
400 d = dir[len(self.root) + 1:]
401 for sd in subdirs:
401 for sd in subdirs:
402 if ignore(os.path.join(d, sd +'/')):
402 if ignore(os.path.join(d, sd +'/')):
403 subdirs.remove(sd)
403 subdirs.remove(sd)
404 for fn in fl:
404 for fn in fl:
405 fn = util.pconvert(os.path.join(d, fn))
405 fn = util.pconvert(os.path.join(d, fn))
406 yield fn
406 yield fn
407 else:
407 else:
408 yield f[len(self.root) + 1:]
408 yield f[len(self.root) + 1:]
409
409
410 for k in dc.keys():
410 for k in dc.keys():
411 yield k
411 yield k
412
412
413 for fn in util.unique(walk(files)):
413 for fn in util.unique(walk(files)):
414 try: s = os.stat(os.path.join(self.root, fn))
414 try: s = os.stat(os.path.join(self.root, fn))
415 except: continue
415 except: continue
416
416
417 if fn in dc:
417 if fn in dc:
418 c = dc[fn]
418 c = dc[fn]
419 del dc[fn]
419 del dc[fn]
420
420
421 if c[0] == 'm':
421 if c[0] == 'm':
422 changed.append(fn)
422 changed.append(fn)
423 elif c[0] == 'a':
423 elif c[0] == 'a':
424 added.append(fn)
424 added.append(fn)
425 elif c[0] == 'r':
425 elif c[0] == 'r':
426 unknown.append(fn)
426 unknown.append(fn)
427 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
427 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
428 changed.append(fn)
428 changed.append(fn)
429 elif c[1] != s.st_mode or c[3] != s.st_mtime:
429 elif c[1] != s.st_mode or c[3] != s.st_mtime:
430 lookup.append(fn)
430 lookup.append(fn)
431 else:
431 else:
432 if not ignore(fn): unknown.append(fn)
432 if not ignore(fn): unknown.append(fn)
433
433
434 return (lookup, changed, added, dc.keys(), unknown)
434 return (lookup, changed, added, dc.keys(), unknown)
435
435
436 # used to avoid circular references so destructors work
436 # used to avoid circular references so destructors work
437 def opener(base):
437 def opener(base):
438 p = base
438 p = base
439 def o(path, mode="r"):
439 def o(path, mode="r"):
440 if p.startswith("http://"):
440 if p.startswith("http://"):
441 f = os.path.join(p, urllib.quote(path))
441 f = os.path.join(p, urllib.quote(path))
442 return httprangereader.httprangereader(f)
442 return httprangereader.httprangereader(f)
443
443
444 f = os.path.join(p, path)
444 f = os.path.join(p, path)
445
445
446 mode += "b" # for that other OS
446 mode += "b" # for that other OS
447
447
448 if mode[0] != "r":
448 if mode[0] != "r":
449 try:
449 try:
450 s = os.stat(f)
450 s = os.stat(f)
451 except OSError:
451 except OSError:
452 d = os.path.dirname(f)
452 d = os.path.dirname(f)
453 if not os.path.isdir(d):
453 if not os.path.isdir(d):
454 os.makedirs(d)
454 os.makedirs(d)
455 else:
455 else:
456 if s.st_nlink > 1:
456 if s.st_nlink > 1:
457 file(f + ".tmp", "wb").write(file(f, "rb").read())
457 file(f + ".tmp", "wb").write(file(f, "rb").read())
458 util.rename(f+".tmp", f)
458 util.rename(f+".tmp", f)
459
459
460 return file(f, mode)
460 return file(f, mode)
461
461
462 return o
462 return o
463
463
464 class RepoError(Exception): pass
464 class RepoError(Exception): pass
465
465
466 class localrepository:
466 class localrepository:
467 def __init__(self, ui, path=None, create=0):
467 def __init__(self, ui, path=None, create=0):
468 self.remote = 0
468 self.remote = 0
469 if path and path.startswith("http://"):
469 if path and path.startswith("http://"):
470 self.remote = 1
470 self.remote = 1
471 self.path = path
471 self.path = path
472 else:
472 else:
473 if not path:
473 if not path:
474 p = os.getcwd()
474 p = os.getcwd()
475 while not os.path.isdir(os.path.join(p, ".hg")):
475 while not os.path.isdir(os.path.join(p, ".hg")):
476 oldp = p
476 oldp = p
477 p = os.path.dirname(p)
477 p = os.path.dirname(p)
478 if p == oldp: raise RepoError("no repo found")
478 if p == oldp: raise RepoError("no repo found")
479 path = p
479 path = p
480 self.path = os.path.join(path, ".hg")
480 self.path = os.path.join(path, ".hg")
481
481
482 if not create and not os.path.isdir(self.path):
482 if not create and not os.path.isdir(self.path):
483 raise RepoError("repository %s not found" % self.path)
483 raise RepoError("repository %s not found" % self.path)
484
484
485 self.root = path
485 self.root = path
486 self.ui = ui
486 self.ui = ui
487
487
488 if create:
488 if create:
489 os.mkdir(self.path)
489 os.mkdir(self.path)
490 os.mkdir(self.join("data"))
490 os.mkdir(self.join("data"))
491
491
492 self.opener = opener(self.path)
492 self.opener = opener(self.path)
493 self.wopener = opener(self.root)
493 self.wopener = opener(self.root)
494 self.manifest = manifest(self.opener)
494 self.manifest = manifest(self.opener)
495 self.changelog = changelog(self.opener)
495 self.changelog = changelog(self.opener)
496 self.ignorefunc = None
496 self.ignorefunc = None
497 self.tagscache = None
497 self.tagscache = None
498 self.nodetagscache = None
498 self.nodetagscache = None
499
499
500 if not self.remote:
500 if not self.remote:
501 self.dirstate = dirstate(self.opener, ui, self.root)
501 self.dirstate = dirstate(self.opener, ui, self.root)
502 try:
502 try:
503 self.ui.readconfig(self.opener("hgrc"))
503 self.ui.readconfig(self.opener("hgrc"))
504 except IOError: pass
504 except IOError: pass
505
505
506 def ignore(self, f):
506 def ignore(self, f):
507 if not self.ignorefunc:
507 if not self.ignorefunc:
508 bigpat = ["^.hg/$"]
508 bigpat = ["^.hg/$"]
509 try:
509 try:
510 l = file(self.wjoin(".hgignore"))
510 l = file(self.wjoin(".hgignore"))
511 for pat in l:
511 for pat in l:
512 if pat != "\n":
512 if pat != "\n":
513 p = util.pconvert(pat[:-1])
513 p = util.pconvert(pat[:-1])
514 try:
514 try:
515 r = re.compile(p)
515 r = re.compile(p)
516 except:
516 except:
517 self.ui.warn("ignoring invalid ignore"
517 self.ui.warn("ignoring invalid ignore"
518 + " regular expression '%s'\n" % p)
518 + " regular expression '%s'\n" % p)
519 else:
519 else:
520 bigpat.append(util.pconvert(pat[:-1]))
520 bigpat.append(util.pconvert(pat[:-1]))
521 except IOError: pass
521 except IOError: pass
522
522
523 s = "(?:%s)" % (")|(?:".join(bigpat))
523 s = "(?:%s)" % (")|(?:".join(bigpat))
524 r = re.compile(s)
524 r = re.compile(s)
525 self.ignorefunc = r.search
525 self.ignorefunc = r.search
526
526
527 return self.ignorefunc(f)
527 return self.ignorefunc(f)
528
528
529 def hook(self, name, **args):
529 def hook(self, name, **args):
530 s = self.ui.config("hooks", name)
530 s = self.ui.config("hooks", name)
531 if s:
531 if s:
532 self.ui.note("running hook %s: %s\n" % (name, s))
532 self.ui.note("running hook %s: %s\n" % (name, s))
533 old = {}
533 old = {}
534 for k, v in args.items():
534 for k, v in args.items():
535 k = k.upper()
535 k = k.upper()
536 old[k] = os.environ.get(k, None)
536 old[k] = os.environ.get(k, None)
537 os.environ[k] = v
537 os.environ[k] = v
538
538
539 r = os.system(s)
539 r = os.system(s)
540
540
541 for k, v in old.items():
541 for k, v in old.items():
542 if v != None:
542 if v != None:
543 os.environ[k] = v
543 os.environ[k] = v
544 else:
544 else:
545 del os.environ[k]
545 del os.environ[k]
546
546
547 if r:
547 if r:
548 self.ui.warn("abort: %s hook failed with status %d!\n" %
548 self.ui.warn("abort: %s hook failed with status %d!\n" %
549 (name, r))
549 (name, r))
550 return False
550 return False
551 return True
551 return True
552
552
553 def tags(self):
553 def tags(self):
554 '''return a mapping of tag to node'''
554 '''return a mapping of tag to node'''
555 if not self.tagscache:
555 if not self.tagscache:
556 self.tagscache = {}
556 self.tagscache = {}
557 def addtag(self, k, n):
557 def addtag(self, k, n):
558 try:
558 try:
559 bin_n = bin(n)
559 bin_n = bin(n)
560 except TypeError:
560 except TypeError:
561 bin_n = ''
561 bin_n = ''
562 self.tagscache[k.strip()] = bin_n
562 self.tagscache[k.strip()] = bin_n
563
563
564 try:
564 try:
565 # read each head of the tags file, ending with the tip
565 # read each head of the tags file, ending with the tip
566 # and add each tag found to the map, with "newer" ones
566 # and add each tag found to the map, with "newer" ones
567 # taking precedence
567 # taking precedence
568 fl = self.file(".hgtags")
568 fl = self.file(".hgtags")
569 h = fl.heads()
569 h = fl.heads()
570 h.reverse()
570 h.reverse()
571 for r in h:
571 for r in h:
572 for l in fl.revision(r).splitlines():
572 for l in fl.revision(r).splitlines():
573 if l:
573 if l:
574 n, k = l.split(" ", 1)
574 n, k = l.split(" ", 1)
575 addtag(self, k, n)
575 addtag(self, k, n)
576 except KeyError:
576 except KeyError:
577 pass
577 pass
578
578
579 try:
579 try:
580 f = self.opener("localtags")
580 f = self.opener("localtags")
581 for l in f:
581 for l in f:
582 n, k = l.split(" ", 1)
582 n, k = l.split(" ", 1)
583 addtag(self, k, n)
583 addtag(self, k, n)
584 except IOError:
584 except IOError:
585 pass
585 pass
586
586
587 self.tagscache['tip'] = self.changelog.tip()
587 self.tagscache['tip'] = self.changelog.tip()
588
588
589 return self.tagscache
589 return self.tagscache
590
590
591 def tagslist(self):
591 def tagslist(self):
592 '''return a list of tags ordered by revision'''
592 '''return a list of tags ordered by revision'''
593 l = []
593 l = []
594 for t, n in self.tags().items():
594 for t, n in self.tags().items():
595 try:
595 try:
596 r = self.changelog.rev(n)
596 r = self.changelog.rev(n)
597 except:
597 except:
598 r = -2 # sort to the beginning of the list if unknown
598 r = -2 # sort to the beginning of the list if unknown
599 l.append((r,t,n))
599 l.append((r,t,n))
600 l.sort()
600 l.sort()
601 return [(t,n) for r,t,n in l]
601 return [(t,n) for r,t,n in l]
602
602
603 def nodetags(self, node):
603 def nodetags(self, node):
604 '''return the tags associated with a node'''
604 '''return the tags associated with a node'''
605 if not self.nodetagscache:
605 if not self.nodetagscache:
606 self.nodetagscache = {}
606 self.nodetagscache = {}
607 for t,n in self.tags().items():
607 for t,n in self.tags().items():
608 self.nodetagscache.setdefault(n,[]).append(t)
608 self.nodetagscache.setdefault(n,[]).append(t)
609 return self.nodetagscache.get(node, [])
609 return self.nodetagscache.get(node, [])
610
610
611 def lookup(self, key):
611 def lookup(self, key):
612 try:
612 try:
613 return self.tags()[key]
613 return self.tags()[key]
614 except KeyError:
614 except KeyError:
615 try:
615 try:
616 return self.changelog.lookup(key)
616 return self.changelog.lookup(key)
617 except:
617 except:
618 raise RepoError("unknown revision '%s'" % key)
618 raise RepoError("unknown revision '%s'" % key)
619
619
620 def dev(self):
620 def dev(self):
621 if self.remote: return -1
621 if self.remote: return -1
622 return os.stat(self.path).st_dev
622 return os.stat(self.path).st_dev
623
623
624 def join(self, f):
624 def join(self, f):
625 return os.path.join(self.path, f)
625 return os.path.join(self.path, f)
626
626
627 def wjoin(self, f):
627 def wjoin(self, f):
628 return os.path.join(self.root, f)
628 return os.path.join(self.root, f)
629
629
630 def file(self, f):
630 def file(self, f):
631 if f[0] == '/': f = f[1:]
631 if f[0] == '/': f = f[1:]
632 return filelog(self.opener, f)
632 return filelog(self.opener, f)
633
633
634 def getcwd(self):
634 def getcwd(self):
635 cwd = os.getcwd()
635 cwd = os.getcwd()
636 if cwd == self.root: return ''
636 if cwd == self.root: return ''
637 return cwd[len(self.root) + 1:]
637 return cwd[len(self.root) + 1:]
638
638
639 def wfile(self, f, mode='r'):
639 def wfile(self, f, mode='r'):
640 return self.wopener(f, mode)
640 return self.wopener(f, mode)
641
641
642 def transaction(self):
642 def transaction(self):
643 # save dirstate for undo
643 # save dirstate for undo
644 try:
644 try:
645 ds = self.opener("dirstate").read()
645 ds = self.opener("dirstate").read()
646 except IOError:
646 except IOError:
647 ds = ""
647 ds = ""
648 self.opener("undo.dirstate", "w").write(ds)
648 self.opener("undo.dirstate", "w").write(ds)
649
649
650 return transaction.transaction(self.ui.warn,
650 return transaction.transaction(self.ui.warn,
651 self.opener, self.join("journal"),
651 self.opener, self.join("journal"),
652 self.join("undo"))
652 self.join("undo"))
653
653
654 def recover(self):
654 def recover(self):
655 lock = self.lock()
655 lock = self.lock()
656 if os.path.exists(self.join("journal")):
656 if os.path.exists(self.join("journal")):
657 self.ui.status("rolling back interrupted transaction\n")
657 self.ui.status("rolling back interrupted transaction\n")
658 return transaction.rollback(self.opener, self.join("journal"))
658 return transaction.rollback(self.opener, self.join("journal"))
659 else:
659 else:
660 self.ui.warn("no interrupted transaction available\n")
660 self.ui.warn("no interrupted transaction available\n")
661
661
662 def undo(self):
662 def undo(self):
663 lock = self.lock()
663 lock = self.lock()
664 if os.path.exists(self.join("undo")):
664 if os.path.exists(self.join("undo")):
665 self.ui.status("rolling back last transaction\n")
665 self.ui.status("rolling back last transaction\n")
666 transaction.rollback(self.opener, self.join("undo"))
666 transaction.rollback(self.opener, self.join("undo"))
667 self.dirstate = None
667 self.dirstate = None
668 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
668 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
669 self.dirstate = dirstate(self.opener, self.ui, self.root)
669 self.dirstate = dirstate(self.opener, self.ui, self.root)
670 else:
670 else:
671 self.ui.warn("no undo information available\n")
671 self.ui.warn("no undo information available\n")
672
672
673 def lock(self, wait = 1):
673 def lock(self, wait = 1):
674 try:
674 try:
675 return lock.lock(self.join("lock"), 0)
675 return lock.lock(self.join("lock"), 0)
676 except lock.LockHeld, inst:
676 except lock.LockHeld, inst:
677 if wait:
677 if wait:
678 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
678 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
679 return lock.lock(self.join("lock"), wait)
679 return lock.lock(self.join("lock"), wait)
680 raise inst
680 raise inst
681
681
682 def rawcommit(self, files, text, user, date, p1=None, p2=None):
682 def rawcommit(self, files, text, user, date, p1=None, p2=None):
683 orig_parent = self.dirstate.parents()[0] or nullid
683 orig_parent = self.dirstate.parents()[0] or nullid
684 p1 = p1 or self.dirstate.parents()[0] or nullid
684 p1 = p1 or self.dirstate.parents()[0] or nullid
685 p2 = p2 or self.dirstate.parents()[1] or nullid
685 p2 = p2 or self.dirstate.parents()[1] or nullid
686 c1 = self.changelog.read(p1)
686 c1 = self.changelog.read(p1)
687 c2 = self.changelog.read(p2)
687 c2 = self.changelog.read(p2)
688 m1 = self.manifest.read(c1[0])
688 m1 = self.manifest.read(c1[0])
689 mf1 = self.manifest.readflags(c1[0])
689 mf1 = self.manifest.readflags(c1[0])
690 m2 = self.manifest.read(c2[0])
690 m2 = self.manifest.read(c2[0])
691
691
692 if orig_parent == p1:
692 if orig_parent == p1:
693 update_dirstate = 1
693 update_dirstate = 1
694 else:
694 else:
695 update_dirstate = 0
695 update_dirstate = 0
696
696
697 tr = self.transaction()
697 tr = self.transaction()
698 mm = m1.copy()
698 mm = m1.copy()
699 mfm = mf1.copy()
699 mfm = mf1.copy()
700 linkrev = self.changelog.count()
700 linkrev = self.changelog.count()
701 for f in files:
701 for f in files:
702 try:
702 try:
703 t = self.wfile(f).read()
703 t = self.wfile(f).read()
704 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
704 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
705 r = self.file(f)
705 r = self.file(f)
706 mfm[f] = tm
706 mfm[f] = tm
707 mm[f] = r.add(t, {}, tr, linkrev,
707 mm[f] = r.add(t, {}, tr, linkrev,
708 m1.get(f, nullid), m2.get(f, nullid))
708 m1.get(f, nullid), m2.get(f, nullid))
709 if update_dirstate:
709 if update_dirstate:
710 self.dirstate.update([f], "n")
710 self.dirstate.update([f], "n")
711 except IOError:
711 except IOError:
712 try:
712 try:
713 del mm[f]
713 del mm[f]
714 del mfm[f]
714 del mfm[f]
715 if update_dirstate:
715 if update_dirstate:
716 self.dirstate.forget([f])
716 self.dirstate.forget([f])
717 except:
717 except:
718 # deleted from p2?
718 # deleted from p2?
719 pass
719 pass
720
720
721 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
721 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
722 user = user or self.ui.username()
722 user = user or self.ui.username()
723 n = self.changelog.add(mnode, files, text, tr, p1, p2, user, date)
723 n = self.changelog.add(mnode, files, text, tr, p1, p2, user, date)
724 tr.close()
724 tr.close()
725 if update_dirstate:
725 if update_dirstate:
726 self.dirstate.setparents(n, nullid)
726 self.dirstate.setparents(n, nullid)
727
727
728 def commit(self, files = None, text = "", user = None, date = None):
728 def commit(self, files = None, text = "", user = None, date = None):
729 commit = []
729 commit = []
730 remove = []
730 remove = []
731 if files:
731 if files:
732 for f in files:
732 for f in files:
733 s = self.dirstate.state(f)
733 s = self.dirstate.state(f)
734 if s in 'nmai':
734 if s in 'nmai':
735 commit.append(f)
735 commit.append(f)
736 elif s == 'r':
736 elif s == 'r':
737 remove.append(f)
737 remove.append(f)
738 else:
738 else:
739 self.ui.warn("%s not tracked!\n" % f)
739 self.ui.warn("%s not tracked!\n" % f)
740 else:
740 else:
741 (c, a, d, u) = self.changes(None, None)
741 (c, a, d, u) = self.changes(None, None)
742 commit = c + a
742 commit = c + a
743 remove = d
743 remove = d
744
744
745 if not commit and not remove:
745 if not commit and not remove:
746 self.ui.status("nothing changed\n")
746 self.ui.status("nothing changed\n")
747 return
747 return
748
748
749 if not self.hook("precommit"):
749 if not self.hook("precommit"):
750 return 1
750 return 1
751
751
752 p1, p2 = self.dirstate.parents()
752 p1, p2 = self.dirstate.parents()
753 c1 = self.changelog.read(p1)
753 c1 = self.changelog.read(p1)
754 c2 = self.changelog.read(p2)
754 c2 = self.changelog.read(p2)
755 m1 = self.manifest.read(c1[0])
755 m1 = self.manifest.read(c1[0])
756 mf1 = self.manifest.readflags(c1[0])
756 mf1 = self.manifest.readflags(c1[0])
757 m2 = self.manifest.read(c2[0])
757 m2 = self.manifest.read(c2[0])
758 lock = self.lock()
758 lock = self.lock()
759 tr = self.transaction()
759 tr = self.transaction()
760
760
761 # check in files
761 # check in files
762 new = {}
762 new = {}
763 linkrev = self.changelog.count()
763 linkrev = self.changelog.count()
764 commit.sort()
764 commit.sort()
765 for f in commit:
765 for f in commit:
766 self.ui.note(f + "\n")
766 self.ui.note(f + "\n")
767 try:
767 try:
768 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
768 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
769 t = self.wfile(f).read()
769 t = self.wfile(f).read()
770 except IOError:
770 except IOError:
771 self.ui.warn("trouble committing %s!\n" % f)
771 self.ui.warn("trouble committing %s!\n" % f)
772 raise
772 raise
773
773
774 meta = {}
774 meta = {}
775 cp = self.dirstate.copied(f)
775 cp = self.dirstate.copied(f)
776 if cp:
776 if cp:
777 meta["copy"] = cp
777 meta["copy"] = cp
778 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
778 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
779 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"]))
779 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"]))
780
780
781 r = self.file(f)
781 r = self.file(f)
782 fp1 = m1.get(f, nullid)
782 fp1 = m1.get(f, nullid)
783 fp2 = m2.get(f, nullid)
783 fp2 = m2.get(f, nullid)
784 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
784 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
785
785
786 # update manifest
786 # update manifest
787 m1.update(new)
787 m1.update(new)
788 for f in remove:
788 for f in remove:
789 if f in m1:
789 if f in m1:
790 del m1[f]
790 del m1[f]
791 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0], (new,remove))
791 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0], (new,remove))
792
792
793 # add changeset
793 # add changeset
794 new = new.keys()
794 new = new.keys()
795 new.sort()
795 new.sort()
796
796
797 if not text:
797 if not text:
798 edittext = "\n" + "HG: manifest hash %s\n" % hex(mn)
798 edittext = "\n" + "HG: manifest hash %s\n" % hex(mn)
799 edittext += "".join(["HG: changed %s\n" % f for f in new])
799 edittext += "".join(["HG: changed %s\n" % f for f in new])
800 edittext += "".join(["HG: removed %s\n" % f for f in remove])
800 edittext += "".join(["HG: removed %s\n" % f for f in remove])
801 edittext = self.ui.edit(edittext)
801 edittext = self.ui.edit(edittext)
802 if not edittext.rstrip():
802 if not edittext.rstrip():
803 return 1
803 return 1
804 text = edittext
804 text = edittext
805
805
806 user = user or self.ui.username()
806 user = user or self.ui.username()
807 n = self.changelog.add(mn, new, text, tr, p1, p2, user, date)
807 n = self.changelog.add(mn, new, text, tr, p1, p2, user, date)
808
808
809 tr.close()
809 tr.close()
810
810
811 self.dirstate.setparents(n)
811 self.dirstate.setparents(n)
812 self.dirstate.update(new, "n")
812 self.dirstate.update(new, "n")
813 self.dirstate.forget(remove)
813 self.dirstate.forget(remove)
814
814
815 if not self.hook("commit", node=hex(n)):
815 if not self.hook("commit", node=hex(n)):
816 return 1
816 return 1
817
817
818 def changes(self, node1, node2, files=None):
818 def changes(self, node1, node2, files=None):
819 mf2, u = None, []
819 mf2, u = None, []
820
820
821 def fcmp(fn, mf):
821 def fcmp(fn, mf):
822 t1 = self.wfile(fn).read()
822 t1 = self.wfile(fn).read()
823 t2 = self.file(fn).revision(mf[fn])
823 t2 = self.file(fn).revision(mf[fn])
824 return cmp(t1, t2)
824 return cmp(t1, t2)
825
825
826 # are we comparing the working directory?
826 # are we comparing the working directory?
827 if not node2:
827 if not node2:
828 l, c, a, d, u = self.dirstate.changes(files, self.ignore)
828 l, c, a, d, u = self.dirstate.changes(files, self.ignore)
829
829
830 # are we comparing working dir against its parent?
830 # are we comparing working dir against its parent?
831 if not node1:
831 if not node1:
832 if l:
832 if l:
833 # do a full compare of any files that might have changed
833 # do a full compare of any files that might have changed
834 change = self.changelog.read(self.dirstate.parents()[0])
834 change = self.changelog.read(self.dirstate.parents()[0])
835 mf2 = self.manifest.read(change[0])
835 mf2 = self.manifest.read(change[0])
836 for f in l:
836 for f in l:
837 if fcmp(f, mf2):
837 if fcmp(f, mf2):
838 c.append(f)
838 c.append(f)
839
839
840 for l in c, a, d, u:
840 for l in c, a, d, u:
841 l.sort()
841 l.sort()
842
842
843 return (c, a, d, u)
843 return (c, a, d, u)
844
844
845 # are we comparing working dir against non-tip?
845 # are we comparing working dir against non-tip?
846 # generate a pseudo-manifest for the working dir
846 # generate a pseudo-manifest for the working dir
847 if not node2:
847 if not node2:
848 if not mf2:
848 if not mf2:
849 change = self.changelog.read(self.dirstate.parents()[0])
849 change = self.changelog.read(self.dirstate.parents()[0])
850 mf2 = self.manifest.read(change[0]).copy()
850 mf2 = self.manifest.read(change[0]).copy()
851 for f in a + c + l:
851 for f in a + c + l:
852 mf2[f] = ""
852 mf2[f] = ""
853 for f in d:
853 for f in d:
854 if f in mf2: del mf2[f]
854 if f in mf2: del mf2[f]
855 else:
855 else:
856 change = self.changelog.read(node2)
856 change = self.changelog.read(node2)
857 mf2 = self.manifest.read(change[0])
857 mf2 = self.manifest.read(change[0])
858
858
859 # flush lists from dirstate before comparing manifests
859 # flush lists from dirstate before comparing manifests
860 c, a = [], []
860 c, a = [], []
861
861
862 change = self.changelog.read(node1)
862 change = self.changelog.read(node1)
863 mf1 = self.manifest.read(change[0]).copy()
863 mf1 = self.manifest.read(change[0]).copy()
864
864
865 for fn in mf2:
865 for fn in mf2:
866 if mf1.has_key(fn):
866 if mf1.has_key(fn):
867 if mf1[fn] != mf2[fn]:
867 if mf1[fn] != mf2[fn]:
868 if mf2[fn] != "" or fcmp(fn, mf1):
868 if mf2[fn] != "" or fcmp(fn, mf1):
869 c.append(fn)
869 c.append(fn)
870 del mf1[fn]
870 del mf1[fn]
871 else:
871 else:
872 a.append(fn)
872 a.append(fn)
873
873
874 d = mf1.keys()
874 d = mf1.keys()
875
875
876 for l in c, a, d, u:
876 for l in c, a, d, u:
877 l.sort()
877 l.sort()
878
878
879 return (c, a, d, u)
879 return (c, a, d, u)
880
880
881 def add(self, list):
881 def add(self, list):
882 for f in list:
882 for f in list:
883 p = self.wjoin(f)
883 p = self.wjoin(f)
884 if not os.path.exists(p):
884 if not os.path.exists(p):
885 self.ui.warn("%s does not exist!\n" % f)
885 self.ui.warn("%s does not exist!\n" % f)
886 elif not os.path.isfile(p):
886 elif not os.path.isfile(p):
887 self.ui.warn("%s not added: mercurial only supports files currently\n" % f)
887 self.ui.warn("%s not added: mercurial only supports files currently\n" % f)
888 elif self.dirstate.state(f) == 'n':
888 elif self.dirstate.state(f) == 'n':
889 self.ui.warn("%s already tracked!\n" % f)
889 self.ui.warn("%s already tracked!\n" % f)
890 else:
890 else:
891 self.dirstate.update([f], "a")
891 self.dirstate.update([f], "a")
892
892
893 def forget(self, list):
893 def forget(self, list):
894 for f in list:
894 for f in list:
895 if self.dirstate.state(f) not in 'ai':
895 if self.dirstate.state(f) not in 'ai':
896 self.ui.warn("%s not added!\n" % f)
896 self.ui.warn("%s not added!\n" % f)
897 else:
897 else:
898 self.dirstate.forget([f])
898 self.dirstate.forget([f])
899
899
900 def remove(self, list):
900 def remove(self, list):
901 for f in list:
901 for f in list:
902 p = self.wjoin(f)
902 p = self.wjoin(f)
903 if os.path.exists(p):
903 if os.path.exists(p):
904 self.ui.warn("%s still exists!\n" % f)
904 self.ui.warn("%s still exists!\n" % f)
905 elif self.dirstate.state(f) == 'a':
905 elif self.dirstate.state(f) == 'a':
906 self.ui.warn("%s never committed!\n" % f)
906 self.ui.warn("%s never committed!\n" % f)
907 self.dirstate.forget([f])
907 self.dirstate.forget([f])
908 elif f not in self.dirstate:
908 elif f not in self.dirstate:
909 self.ui.warn("%s not tracked!\n" % f)
909 self.ui.warn("%s not tracked!\n" % f)
910 else:
910 else:
911 self.dirstate.update([f], "r")
911 self.dirstate.update([f], "r")
912
912
913 def copy(self, source, dest):
913 def copy(self, source, dest):
914 p = self.wjoin(dest)
914 p = self.wjoin(dest)
915 if not os.path.exists(dest):
915 if not os.path.exists(dest):
916 self.ui.warn("%s does not exist!\n" % dest)
916 self.ui.warn("%s does not exist!\n" % dest)
917 elif not os.path.isfile(dest):
917 elif not os.path.isfile(dest):
918 self.ui.warn("copy failed: %s is not a file\n" % dest)
918 self.ui.warn("copy failed: %s is not a file\n" % dest)
919 else:
919 else:
920 if self.dirstate.state(dest) == '?':
920 if self.dirstate.state(dest) == '?':
921 self.dirstate.update([dest], "a")
921 self.dirstate.update([dest], "a")
922 self.dirstate.copy(source, dest)
922 self.dirstate.copy(source, dest)
923
923
924 def heads(self):
924 def heads(self):
925 return self.changelog.heads()
925 return self.changelog.heads()
926
926
927 def branches(self, nodes):
927 def branches(self, nodes):
928 if not nodes: nodes = [self.changelog.tip()]
928 if not nodes: nodes = [self.changelog.tip()]
929 b = []
929 b = []
930 for n in nodes:
930 for n in nodes:
931 t = n
931 t = n
932 while n:
932 while n:
933 p = self.changelog.parents(n)
933 p = self.changelog.parents(n)
934 if p[1] != nullid or p[0] == nullid:
934 if p[1] != nullid or p[0] == nullid:
935 b.append((t, n, p[0], p[1]))
935 b.append((t, n, p[0], p[1]))
936 break
936 break
937 n = p[0]
937 n = p[0]
938 return b
938 return b
939
939
940 def between(self, pairs):
940 def between(self, pairs):
941 r = []
941 r = []
942
942
943 for top, bottom in pairs:
943 for top, bottom in pairs:
944 n, l, i = top, [], 0
944 n, l, i = top, [], 0
945 f = 1
945 f = 1
946
946
947 while n != bottom:
947 while n != bottom:
948 p = self.changelog.parents(n)[0]
948 p = self.changelog.parents(n)[0]
949 if i == f:
949 if i == f:
950 l.append(n)
950 l.append(n)
951 f = f * 2
951 f = f * 2
952 n = p
952 n = p
953 i += 1
953 i += 1
954
954
955 r.append(l)
955 r.append(l)
956
956
957 return r
957 return r
958
958
959 def newer(self, nodes):
959 def newer(self, nodes):
960 m = {}
960 m = {}
961 nl = []
961 nl = []
962 pm = {}
962 pm = {}
963 cl = self.changelog
963 cl = self.changelog
964 t = l = cl.count()
964 t = l = cl.count()
965
965
966 # find the lowest numbered node
966 # find the lowest numbered node
967 for n in nodes:
967 for n in nodes:
968 l = min(l, cl.rev(n))
968 l = min(l, cl.rev(n))
969 m[n] = 1
969 m[n] = 1
970
970
971 for i in xrange(l, t):
971 for i in xrange(l, t):
972 n = cl.node(i)
972 n = cl.node(i)
973 if n in m: # explicitly listed
973 if n in m: # explicitly listed
974 pm[n] = 1
974 pm[n] = 1
975 nl.append(n)
975 nl.append(n)
976 continue
976 continue
977 for p in cl.parents(n):
977 for p in cl.parents(n):
978 if p in pm: # parent listed
978 if p in pm: # parent listed
979 pm[n] = 1
979 pm[n] = 1
980 nl.append(n)
980 nl.append(n)
981 break
981 break
982
982
983 return nl
983 return nl
984
984
985 def findincoming(self, remote, base={}):
985 def findincoming(self, remote, base={}):
986 m = self.changelog.nodemap
986 m = self.changelog.nodemap
987 search = []
987 search = []
988 fetch = []
988 fetch = []
989 seen = {}
989 seen = {}
990 seenbranch = {}
990 seenbranch = {}
991
991
992 # assume we're closer to the tip than the root
992 # assume we're closer to the tip than the root
993 # and start by examining the heads
993 # and start by examining the heads
994 self.ui.status("searching for changes\n")
994 self.ui.status("searching for changes\n")
995 heads = remote.heads()
995 heads = remote.heads()
996 unknown = []
996 unknown = []
997 for h in heads:
997 for h in heads:
998 if h not in m:
998 if h not in m:
999 unknown.append(h)
999 unknown.append(h)
1000 else:
1000 else:
1001 base[h] = 1
1001 base[h] = 1
1002
1002
1003 if not unknown:
1003 if not unknown:
1004 return None
1004 return None
1005
1005
1006 rep = {}
1006 rep = {}
1007 reqcnt = 0
1007 reqcnt = 0
1008
1008
1009 # search through remote branches
1009 # search through remote branches
1010 # a 'branch' here is a linear segment of history, with four parts:
1010 # a 'branch' here is a linear segment of history, with four parts:
1011 # head, root, first parent, second parent
1011 # head, root, first parent, second parent
1012 # (a branch always has two parents (or none) by definition)
1012 # (a branch always has two parents (or none) by definition)
1013 unknown = remote.branches(unknown)
1013 unknown = remote.branches(unknown)
1014 while unknown:
1014 while unknown:
1015 r = []
1015 r = []
1016 while unknown:
1016 while unknown:
1017 n = unknown.pop(0)
1017 n = unknown.pop(0)
1018 if n[0] in seen:
1018 if n[0] in seen:
1019 continue
1019 continue
1020
1020
1021 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
1021 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
1022 if n[0] == nullid:
1022 if n[0] == nullid:
1023 break
1023 break
1024 if n in seenbranch:
1024 if n in seenbranch:
1025 self.ui.debug("branch already found\n")
1025 self.ui.debug("branch already found\n")
1026 continue
1026 continue
1027 if n[1] and n[1] in m: # do we know the base?
1027 if n[1] and n[1] in m: # do we know the base?
1028 self.ui.debug("found incomplete branch %s:%s\n"
1028 self.ui.debug("found incomplete branch %s:%s\n"
1029 % (short(n[0]), short(n[1])))
1029 % (short(n[0]), short(n[1])))
1030 search.append(n) # schedule branch range for scanning
1030 search.append(n) # schedule branch range for scanning
1031 seenbranch[n] = 1
1031 seenbranch[n] = 1
1032 else:
1032 else:
1033 if n[1] not in seen and n[1] not in fetch:
1033 if n[1] not in seen and n[1] not in fetch:
1034 if n[2] in m and n[3] in m:
1034 if n[2] in m and n[3] in m:
1035 self.ui.debug("found new changeset %s\n" %
1035 self.ui.debug("found new changeset %s\n" %
1036 short(n[1]))
1036 short(n[1]))
1037 fetch.append(n[1]) # earliest unknown
1037 fetch.append(n[1]) # earliest unknown
1038 base[n[2]] = 1 # latest known
1038 base[n[2]] = 1 # latest known
1039 continue
1039 continue
1040
1040
1041 for a in n[2:4]:
1041 for a in n[2:4]:
1042 if a not in rep:
1042 if a not in rep:
1043 r.append(a)
1043 r.append(a)
1044 rep[a] = 1
1044 rep[a] = 1
1045
1045
1046 seen[n[0]] = 1
1046 seen[n[0]] = 1
1047
1047
1048 if r:
1048 if r:
1049 reqcnt += 1
1049 reqcnt += 1
1050 self.ui.debug("request %d: %s\n" %
1050 self.ui.debug("request %d: %s\n" %
1051 (reqcnt, " ".join(map(short, r))))
1051 (reqcnt, " ".join(map(short, r))))
1052 for p in range(0, len(r), 10):
1052 for p in range(0, len(r), 10):
1053 for b in remote.branches(r[p:p+10]):
1053 for b in remote.branches(r[p:p+10]):
1054 self.ui.debug("received %s:%s\n" %
1054 self.ui.debug("received %s:%s\n" %
1055 (short(b[0]), short(b[1])))
1055 (short(b[0]), short(b[1])))
1056 if b[0] not in m and b[0] not in seen:
1056 if b[0] not in m and b[0] not in seen:
1057 unknown.append(b)
1057 unknown.append(b)
1058
1058
1059 # do binary search on the branches we found
1059 # do binary search on the branches we found
1060 while search:
1060 while search:
1061 n = search.pop(0)
1061 n = search.pop(0)
1062 reqcnt += 1
1062 reqcnt += 1
1063 l = remote.between([(n[0], n[1])])[0]
1063 l = remote.between([(n[0], n[1])])[0]
1064 l.append(n[1])
1064 l.append(n[1])
1065 p = n[0]
1065 p = n[0]
1066 f = 1
1066 f = 1
1067 for i in l:
1067 for i in l:
1068 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
1068 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
1069 if i in m:
1069 if i in m:
1070 if f <= 2:
1070 if f <= 2:
1071 self.ui.debug("found new branch changeset %s\n" %
1071 self.ui.debug("found new branch changeset %s\n" %
1072 short(p))
1072 short(p))
1073 fetch.append(p)
1073 fetch.append(p)
1074 base[i] = 1
1074 base[i] = 1
1075 else:
1075 else:
1076 self.ui.debug("narrowed branch search to %s:%s\n"
1076 self.ui.debug("narrowed branch search to %s:%s\n"
1077 % (short(p), short(i)))
1077 % (short(p), short(i)))
1078 search.append((p, i))
1078 search.append((p, i))
1079 break
1079 break
1080 p, f = i, f * 2
1080 p, f = i, f * 2
1081
1081
1082 # sanity check our fetch list
1082 # sanity check our fetch list
1083 for f in fetch:
1083 for f in fetch:
1084 if f in m:
1084 if f in m:
1085 raise RepoError("already have changeset " + short(f[:4]))
1085 raise RepoError("already have changeset " + short(f[:4]))
1086
1086
1087 if base.keys() == [nullid]:
1087 if base.keys() == [nullid]:
1088 self.ui.warn("warning: pulling from an unrelated repository!\n")
1088 self.ui.warn("warning: pulling from an unrelated repository!\n")
1089
1089
1090 self.ui.note("adding new changesets starting at " +
1090 self.ui.note("adding new changesets starting at " +
1091 " ".join([short(f) for f in fetch]) + "\n")
1091 " ".join([short(f) for f in fetch]) + "\n")
1092
1092
1093 self.ui.debug("%d total queries\n" % reqcnt)
1093 self.ui.debug("%d total queries\n" % reqcnt)
1094
1094
1095 return fetch
1095 return fetch
1096
1096
1097 def findoutgoing(self, remote):
1097 def findoutgoing(self, remote):
1098 base = {}
1098 base = {}
1099 self.findincoming(remote, base)
1099 self.findincoming(remote, base)
1100 remain = dict.fromkeys(self.changelog.nodemap)
1100 remain = dict.fromkeys(self.changelog.nodemap)
1101
1101
1102 # prune everything remote has from the tree
1102 # prune everything remote has from the tree
1103 del remain[nullid]
1103 del remain[nullid]
1104 remove = base.keys()
1104 remove = base.keys()
1105 while remove:
1105 while remove:
1106 n = remove.pop(0)
1106 n = remove.pop(0)
1107 if n in remain:
1107 if n in remain:
1108 del remain[n]
1108 del remain[n]
1109 for p in self.changelog.parents(n):
1109 for p in self.changelog.parents(n):
1110 remove.append(p)
1110 remove.append(p)
1111
1111
1112 # find every node whose parents have been pruned
1112 # find every node whose parents have been pruned
1113 subset = []
1113 subset = []
1114 for n in remain:
1114 for n in remain:
1115 p1, p2 = self.changelog.parents(n)
1115 p1, p2 = self.changelog.parents(n)
1116 if p1 not in remain and p2 not in remain:
1116 if p1 not in remain and p2 not in remain:
1117 subset.append(n)
1117 subset.append(n)
1118
1118
1119 # this is the set of all roots we have to push
1119 # this is the set of all roots we have to push
1120 return subset
1120 return subset
1121
1121
1122 def pull(self, remote):
1122 def pull(self, remote):
1123 lock = self.lock()
1123 lock = self.lock()
1124
1124
1125 # if we have an empty repo, fetch everything
1125 # if we have an empty repo, fetch everything
1126 if self.changelog.tip() == nullid:
1126 if self.changelog.tip() == nullid:
1127 self.ui.status("requesting all changes\n")
1127 self.ui.status("requesting all changes\n")
1128 fetch = [nullid]
1128 fetch = [nullid]
1129 else:
1129 else:
1130 fetch = self.findincoming(remote)
1130 fetch = self.findincoming(remote)
1131
1131
1132 if not fetch:
1132 if not fetch:
1133 self.ui.status("no changes found\n")
1133 self.ui.status("no changes found\n")
1134 return 1
1134 return 1
1135
1135
1136 cg = remote.changegroup(fetch)
1136 cg = remote.changegroup(fetch)
1137 return self.addchangegroup(cg)
1137 return self.addchangegroup(cg)
1138
1138
1139 def push(self, remote):
1139 def push(self, remote):
1140 lock = remote.lock()
1140 lock = remote.lock()
1141 update = self.findoutgoing(remote)
1141 update = self.findoutgoing(remote)
1142 if not update:
1142 if not update:
1143 self.ui.status("no changes found\n")
1143 self.ui.status("no changes found\n")
1144 return 1
1144 return 1
1145
1145
1146 cg = self.changegroup(update)
1146 cg = self.changegroup(update)
1147 return remote.addchangegroup(cg)
1147 return remote.addchangegroup(cg)
1148
1148
1149 def changegroup(self, basenodes):
1149 def changegroup(self, basenodes):
1150 class genread:
1150 class genread:
1151 def __init__(self, generator):
1151 def __init__(self, generator):
1152 self.g = generator
1152 self.g = generator
1153 self.buf = ""
1153 self.buf = ""
1154 def read(self, l):
1154 def read(self, l):
1155 while l > len(self.buf):
1155 while l > len(self.buf):
1156 try:
1156 try:
1157 self.buf += self.g.next()
1157 self.buf += self.g.next()
1158 except StopIteration:
1158 except StopIteration:
1159 break
1159 break
1160 d, self.buf = self.buf[:l], self.buf[l:]
1160 d, self.buf = self.buf[:l], self.buf[l:]
1161 return d
1161 return d
1162
1162
1163 def gengroup():
1163 def gengroup():
1164 nodes = self.newer(basenodes)
1164 nodes = self.newer(basenodes)
1165
1165
1166 # construct the link map
1166 # construct the link map
1167 linkmap = {}
1167 linkmap = {}
1168 for n in nodes:
1168 for n in nodes:
1169 linkmap[self.changelog.rev(n)] = n
1169 linkmap[self.changelog.rev(n)] = n
1170
1170
1171 # construct a list of all changed files
1171 # construct a list of all changed files
1172 changed = {}
1172 changed = {}
1173 for n in nodes:
1173 for n in nodes:
1174 c = self.changelog.read(n)
1174 c = self.changelog.read(n)
1175 for f in c[3]:
1175 for f in c[3]:
1176 changed[f] = 1
1176 changed[f] = 1
1177 changed = changed.keys()
1177 changed = changed.keys()
1178 changed.sort()
1178 changed.sort()
1179
1179
1180 # the changegroup is changesets + manifests + all file revs
1180 # the changegroup is changesets + manifests + all file revs
1181 revs = [ self.changelog.rev(n) for n in nodes ]
1181 revs = [ self.changelog.rev(n) for n in nodes ]
1182
1182
1183 for y in self.changelog.group(linkmap): yield y
1183 for y in self.changelog.group(linkmap): yield y
1184 for y in self.manifest.group(linkmap): yield y
1184 for y in self.manifest.group(linkmap): yield y
1185 for f in changed:
1185 for f in changed:
1186 yield struct.pack(">l", len(f) + 4) + f
1186 yield struct.pack(">l", len(f) + 4) + f
1187 g = self.file(f).group(linkmap)
1187 g = self.file(f).group(linkmap)
1188 for y in g:
1188 for y in g:
1189 yield y
1189 yield y
1190
1190
1191 yield struct.pack(">l", 0)
1191 yield struct.pack(">l", 0)
1192
1192
1193 return genread(gengroup())
1193 return genread(gengroup())
1194
1194
1195 def addchangegroup(self, source):
1195 def addchangegroup(self, source):
1196
1196
1197 def getchunk():
1197 def getchunk():
1198 d = source.read(4)
1198 d = source.read(4)
1199 if not d: return ""
1199 if not d: return ""
1200 l = struct.unpack(">l", d)[0]
1200 l = struct.unpack(">l", d)[0]
1201 if l <= 4: return ""
1201 if l <= 4: return ""
1202 return source.read(l - 4)
1202 return source.read(l - 4)
1203
1203
1204 def getgroup():
1204 def getgroup():
1205 while 1:
1205 while 1:
1206 c = getchunk()
1206 c = getchunk()
1207 if not c: break
1207 if not c: break
1208 yield c
1208 yield c
1209
1209
1210 def csmap(x):
1210 def csmap(x):
1211 self.ui.debug("add changeset %s\n" % short(x))
1211 self.ui.debug("add changeset %s\n" % short(x))
1212 return self.changelog.count()
1212 return self.changelog.count()
1213
1213
1214 def revmap(x):
1214 def revmap(x):
1215 return self.changelog.rev(x)
1215 return self.changelog.rev(x)
1216
1216
1217 if not source: return
1217 if not source: return
1218 changesets = files = revisions = 0
1218 changesets = files = revisions = 0
1219
1219
1220 tr = self.transaction()
1220 tr = self.transaction()
1221
1221
1222 # pull off the changeset group
1222 # pull off the changeset group
1223 self.ui.status("adding changesets\n")
1223 self.ui.status("adding changesets\n")
1224 co = self.changelog.tip()
1224 co = self.changelog.tip()
1225 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
1225 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
1226 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
1226 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
1227
1227
1228 # pull off the manifest group
1228 # pull off the manifest group
1229 self.ui.status("adding manifests\n")
1229 self.ui.status("adding manifests\n")
1230 mm = self.manifest.tip()
1230 mm = self.manifest.tip()
1231 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1231 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1232
1232
1233 # process the files
1233 # process the files
1234 self.ui.status("adding file revisions\n")
1234 self.ui.status("adding file revisions\n")
1235 while 1:
1235 while 1:
1236 f = getchunk()
1236 f = getchunk()
1237 if not f: break
1237 if not f: break
1238 self.ui.debug("adding %s revisions\n" % f)
1238 self.ui.debug("adding %s revisions\n" % f)
1239 fl = self.file(f)
1239 fl = self.file(f)
1240 o = fl.count()
1240 o = fl.count()
1241 n = fl.addgroup(getgroup(), revmap, tr)
1241 n = fl.addgroup(getgroup(), revmap, tr)
1242 revisions += fl.count() - o
1242 revisions += fl.count() - o
1243 files += 1
1243 files += 1
1244
1244
1245 self.ui.status(("modified %d files, added %d changesets" +
1245 self.ui.status(("modified %d files, added %d changesets" +
1246 " and %d new revisions\n")
1246 " and %d new revisions\n")
1247 % (files, changesets, revisions))
1247 % (files, changesets, revisions))
1248
1248
1249 tr.close()
1249 tr.close()
1250 return
1250 return
1251
1251
1252 def update(self, node, allow=False, force=False, choose=None,
1252 def update(self, node, allow=False, force=False, choose=None,
1253 moddirstate=True):
1253 moddirstate=True):
1254 pl = self.dirstate.parents()
1254 pl = self.dirstate.parents()
1255 if not force and pl[1] != nullid:
1255 if not force and pl[1] != nullid:
1256 self.ui.warn("aborting: outstanding uncommitted merges\n")
1256 self.ui.warn("aborting: outstanding uncommitted merges\n")
1257 return 1
1257 return 1
1258
1258
1259 p1, p2 = pl[0], node
1259 p1, p2 = pl[0], node
1260 pa = self.changelog.ancestor(p1, p2)
1260 pa = self.changelog.ancestor(p1, p2)
1261 m1n = self.changelog.read(p1)[0]
1261 m1n = self.changelog.read(p1)[0]
1262 m2n = self.changelog.read(p2)[0]
1262 m2n = self.changelog.read(p2)[0]
1263 man = self.manifest.ancestor(m1n, m2n)
1263 man = self.manifest.ancestor(m1n, m2n)
1264 m1 = self.manifest.read(m1n)
1264 m1 = self.manifest.read(m1n)
1265 mf1 = self.manifest.readflags(m1n)
1265 mf1 = self.manifest.readflags(m1n)
1266 m2 = self.manifest.read(m2n)
1266 m2 = self.manifest.read(m2n)
1267 mf2 = self.manifest.readflags(m2n)
1267 mf2 = self.manifest.readflags(m2n)
1268 ma = self.manifest.read(man)
1268 ma = self.manifest.read(man)
1269 mfa = self.manifest.readflags(man)
1269 mfa = self.manifest.readflags(man)
1270
1270
1271 (c, a, d, u) = self.changes(None, None)
1271 (c, a, d, u) = self.changes(None, None)
1272
1272
1273 # is this a jump, or a merge? i.e. is there a linear path
1273 # is this a jump, or a merge? i.e. is there a linear path
1274 # from p1 to p2?
1274 # from p1 to p2?
1275 linear_path = (pa == p1 or pa == p2)
1275 linear_path = (pa == p1 or pa == p2)
1276
1276
1277 # resolve the manifest to determine which files
1277 # resolve the manifest to determine which files
1278 # we care about merging
1278 # we care about merging
1279 self.ui.note("resolving manifests\n")
1279 self.ui.note("resolving manifests\n")
1280 self.ui.debug(" force %s allow %s moddirstate %s linear %s\n" %
1280 self.ui.debug(" force %s allow %s moddirstate %s linear %s\n" %
1281 (force, allow, moddirstate, linear_path))
1281 (force, allow, moddirstate, linear_path))
1282 self.ui.debug(" ancestor %s local %s remote %s\n" %
1282 self.ui.debug(" ancestor %s local %s remote %s\n" %
1283 (short(man), short(m1n), short(m2n)))
1283 (short(man), short(m1n), short(m2n)))
1284
1284
1285 merge = {}
1285 merge = {}
1286 get = {}
1286 get = {}
1287 remove = []
1287 remove = []
1288 mark = {}
1288 mark = {}
1289
1289
1290 # construct a working dir manifest
1290 # construct a working dir manifest
1291 mw = m1.copy()
1291 mw = m1.copy()
1292 mfw = mf1.copy()
1292 mfw = mf1.copy()
1293 umap = dict.fromkeys(u)
1293 umap = dict.fromkeys(u)
1294
1294
1295 for f in a + c + u:
1295 for f in a + c + u:
1296 mw[f] = ""
1296 mw[f] = ""
1297 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1297 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1298
1298
1299 for f in d:
1299 for f in d:
1300 if f in mw: del mw[f]
1300 if f in mw: del mw[f]
1301
1301
1302 # If we're jumping between revisions (as opposed to merging),
1302 # If we're jumping between revisions (as opposed to merging),
1303 # and if neither the working directory nor the target rev has
1303 # and if neither the working directory nor the target rev has
1304 # the file, then we need to remove it from the dirstate, to
1304 # the file, then we need to remove it from the dirstate, to
1305 # prevent the dirstate from listing the file when it is no
1305 # prevent the dirstate from listing the file when it is no
1306 # longer in the manifest.
1306 # longer in the manifest.
1307 if moddirstate and linear_path and f not in m2:
1307 if moddirstate and linear_path and f not in m2:
1308 self.dirstate.forget((f,))
1308 self.dirstate.forget((f,))
1309
1309
1310 # Compare manifests
1310 # Compare manifests
1311 for f, n in mw.iteritems():
1311 for f, n in mw.iteritems():
1312 if choose and not choose(f): continue
1312 if choose and not choose(f): continue
1313 if f in m2:
1313 if f in m2:
1314 s = 0
1314 s = 0
1315
1315
1316 # is the wfile new since m1, and match m2?
1316 # is the wfile new since m1, and match m2?
1317 if f not in m1:
1317 if f not in m1:
1318 t1 = self.wfile(f).read()
1318 t1 = self.wfile(f).read()
1319 t2 = self.file(f).revision(m2[f])
1319 t2 = self.file(f).revision(m2[f])
1320 if cmp(t1, t2) == 0:
1320 if cmp(t1, t2) == 0:
1321 mark[f] = 1
1321 mark[f] = 1
1322 n = m2[f]
1322 n = m2[f]
1323 del t1, t2
1323 del t1, t2
1324
1324
1325 # are files different?
1325 # are files different?
1326 if n != m2[f]:
1326 if n != m2[f]:
1327 a = ma.get(f, nullid)
1327 a = ma.get(f, nullid)
1328 # are both different from the ancestor?
1328 # are both different from the ancestor?
1329 if n != a and m2[f] != a:
1329 if n != a and m2[f] != a:
1330 self.ui.debug(" %s versions differ, resolve\n" % f)
1330 self.ui.debug(" %s versions differ, resolve\n" % f)
1331 # merge executable bits
1331 # merge executable bits
1332 # "if we changed or they changed, change in merge"
1332 # "if we changed or they changed, change in merge"
1333 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1333 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1334 mode = ((a^b) | (a^c)) ^ a
1334 mode = ((a^b) | (a^c)) ^ a
1335 merge[f] = (m1.get(f, nullid), m2[f], mode)
1335 merge[f] = (m1.get(f, nullid), m2[f], mode)
1336 s = 1
1336 s = 1
1337 # are we clobbering?
1337 # are we clobbering?
1338 # is remote's version newer?
1338 # is remote's version newer?
1339 # or are we going back in time?
1339 # or are we going back in time?
1340 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1340 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1341 self.ui.debug(" remote %s is newer, get\n" % f)
1341 self.ui.debug(" remote %s is newer, get\n" % f)
1342 get[f] = m2[f]
1342 get[f] = m2[f]
1343 s = 1
1343 s = 1
1344 else:
1344 else:
1345 mark[f] = 1
1345 mark[f] = 1
1346 elif f in umap:
1346 elif f in umap:
1347 # this unknown file is the same as the checkout
1347 # this unknown file is the same as the checkout
1348 get[f] = m2[f]
1348 get[f] = m2[f]
1349
1349
1350 if not s and mfw[f] != mf2[f]:
1350 if not s and mfw[f] != mf2[f]:
1351 if force:
1351 if force:
1352 self.ui.debug(" updating permissions for %s\n" % f)
1352 self.ui.debug(" updating permissions for %s\n" % f)
1353 util.set_exec(self.wjoin(f), mf2[f])
1353 util.set_exec(self.wjoin(f), mf2[f])
1354 else:
1354 else:
1355 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1355 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1356 mode = ((a^b) | (a^c)) ^ a
1356 mode = ((a^b) | (a^c)) ^ a
1357 if mode != b:
1357 if mode != b:
1358 self.ui.debug(" updating permissions for %s\n" % f)
1358 self.ui.debug(" updating permissions for %s\n" % f)
1359 util.set_exec(self.wjoin(f), mode)
1359 util.set_exec(self.wjoin(f), mode)
1360 mark[f] = 1
1360 mark[f] = 1
1361 del m2[f]
1361 del m2[f]
1362 elif f in ma:
1362 elif f in ma:
1363 if n != ma[f]:
1363 if n != ma[f]:
1364 r = "d"
1364 r = "d"
1365 if not force and (linear_path or allow):
1365 if not force and (linear_path or allow):
1366 r = self.ui.prompt(
1366 r = self.ui.prompt(
1367 (" local changed %s which remote deleted\n" % f) +
1367 (" local changed %s which remote deleted\n" % f) +
1368 "(k)eep or (d)elete?", "[kd]", "k")
1368 "(k)eep or (d)elete?", "[kd]", "k")
1369 if r == "d":
1369 if r == "d":
1370 remove.append(f)
1370 remove.append(f)
1371 else:
1371 else:
1372 self.ui.debug("other deleted %s\n" % f)
1372 self.ui.debug("other deleted %s\n" % f)
1373 remove.append(f) # other deleted it
1373 remove.append(f) # other deleted it
1374 else:
1374 else:
1375 if n == m1.get(f, nullid): # same as parent
1375 if n == m1.get(f, nullid): # same as parent
1376 if p2 == pa: # going backwards?
1376 if p2 == pa: # going backwards?
1377 self.ui.debug("remote deleted %s\n" % f)
1377 self.ui.debug("remote deleted %s\n" % f)
1378 remove.append(f)
1378 remove.append(f)
1379 else:
1379 else:
1380 self.ui.debug("local created %s, keeping\n" % f)
1380 self.ui.debug("local created %s, keeping\n" % f)
1381 else:
1381 else:
1382 self.ui.debug("working dir created %s, keeping\n" % f)
1382 self.ui.debug("working dir created %s, keeping\n" % f)
1383
1383
1384 for f, n in m2.iteritems():
1384 for f, n in m2.iteritems():
1385 if choose and not choose(f): continue
1385 if choose and not choose(f): continue
1386 if f[0] == "/": continue
1386 if f[0] == "/": continue
1387 if f in ma and n != ma[f]:
1387 if f in ma and n != ma[f]:
1388 r = "k"
1388 r = "k"
1389 if not force and (linear_path or allow):
1389 if not force and (linear_path or allow):
1390 r = self.ui.prompt(
1390 r = self.ui.prompt(
1391 ("remote changed %s which local deleted\n" % f) +
1391 ("remote changed %s which local deleted\n" % f) +
1392 "(k)eep or (d)elete?", "[kd]", "k")
1392 "(k)eep or (d)elete?", "[kd]", "k")
1393 if r == "k": get[f] = n
1393 if r == "k": get[f] = n
1394 elif f not in ma:
1394 elif f not in ma:
1395 self.ui.debug("remote created %s\n" % f)
1395 self.ui.debug("remote created %s\n" % f)
1396 get[f] = n
1396 get[f] = n
1397 else:
1397 else:
1398 if force or p2 == pa: # going backwards?
1398 if force or p2 == pa: # going backwards?
1399 self.ui.debug("local deleted %s, recreating\n" % f)
1399 self.ui.debug("local deleted %s, recreating\n" % f)
1400 get[f] = n
1400 get[f] = n
1401 else:
1401 else:
1402 self.ui.debug("local deleted %s\n" % f)
1402 self.ui.debug("local deleted %s\n" % f)
1403
1403
1404 del mw, m1, m2, ma
1404 del mw, m1, m2, ma
1405
1405
1406 if force:
1406 if force:
1407 for f in merge:
1407 for f in merge:
1408 get[f] = merge[f][1]
1408 get[f] = merge[f][1]
1409 merge = {}
1409 merge = {}
1410
1410
1411 if linear_path or force:
1411 if linear_path or force:
1412 # we don't need to do any magic, just jump to the new rev
1412 # we don't need to do any magic, just jump to the new rev
1413 mode = 'n'
1413 mode = 'n'
1414 p1, p2 = p2, nullid
1414 p1, p2 = p2, nullid
1415 else:
1415 else:
1416 if not allow:
1416 if not allow:
1417 self.ui.status("this update spans a branch" +
1417 self.ui.status("this update spans a branch" +
1418 " affecting the following files:\n")
1418 " affecting the following files:\n")
1419 fl = merge.keys() + get.keys()
1419 fl = merge.keys() + get.keys()
1420 fl.sort()
1420 fl.sort()
1421 for f in fl:
1421 for f in fl:
1422 cf = ""
1422 cf = ""
1423 if f in merge: cf = " (resolve)"
1423 if f in merge: cf = " (resolve)"
1424 self.ui.status(" %s%s\n" % (f, cf))
1424 self.ui.status(" %s%s\n" % (f, cf))
1425 self.ui.warn("aborting update spanning branches!\n")
1425 self.ui.warn("aborting update spanning branches!\n")
1426 self.ui.status("(use update -m to perform a branch merge)\n")
1426 self.ui.status("(use update -m to perform a branch merge)\n")
1427 return 1
1427 return 1
1428 # we have to remember what files we needed to get/change
1428 # we have to remember what files we needed to get/change
1429 # because any file that's different from either one of its
1429 # because any file that's different from either one of its
1430 # parents must be in the changeset
1430 # parents must be in the changeset
1431 mode = 'm'
1431 mode = 'm'
1432 if moddirstate:
1432 if moddirstate:
1433 self.dirstate.update(mark.keys(), "m")
1433 self.dirstate.update(mark.keys(), "m")
1434
1434
1435 if moddirstate:
1435 if moddirstate:
1436 self.dirstate.setparents(p1, p2)
1436 self.dirstate.setparents(p1, p2)
1437
1437
1438 # get the files we don't need to change
1438 # get the files we don't need to change
1439 files = get.keys()
1439 files = get.keys()
1440 files.sort()
1440 files.sort()
1441 for f in files:
1441 for f in files:
1442 if f[0] == "/": continue
1442 if f[0] == "/": continue
1443 self.ui.note("getting %s\n" % f)
1443 self.ui.note("getting %s\n" % f)
1444 t = self.file(f).read(get[f])
1444 t = self.file(f).read(get[f])
1445 try:
1445 try:
1446 self.wfile(f, "w").write(t)
1446 self.wfile(f, "w").write(t)
1447 except IOError:
1447 except IOError:
1448 os.makedirs(os.path.dirname(self.wjoin(f)))
1448 os.makedirs(os.path.dirname(self.wjoin(f)))
1449 self.wfile(f, "w").write(t)
1449 self.wfile(f, "w").write(t)
1450 util.set_exec(self.wjoin(f), mf2[f])
1450 util.set_exec(self.wjoin(f), mf2[f])
1451 if moddirstate:
1451 if moddirstate:
1452 self.dirstate.update([f], mode)
1452 self.dirstate.update([f], mode)
1453
1453
1454 # merge the tricky bits
1454 # merge the tricky bits
1455 files = merge.keys()
1455 files = merge.keys()
1456 files.sort()
1456 files.sort()
1457 for f in files:
1457 for f in files:
1458 self.ui.status("merging %s\n" % f)
1458 self.ui.status("merging %s\n" % f)
1459 m, o, flag = merge[f]
1459 m, o, flag = merge[f]
1460 self.merge3(f, m, o)
1460 self.merge3(f, m, o)
1461 util.set_exec(self.wjoin(f), flag)
1461 util.set_exec(self.wjoin(f), flag)
1462 if moddirstate:
1462 if moddirstate:
1463 self.dirstate.update([f], 'm')
1463 self.dirstate.update([f], 'm')
1464
1464
1465 remove.sort()
1465 remove.sort()
1466 for f in remove:
1466 for f in remove:
1467 self.ui.note("removing %s\n" % f)
1467 self.ui.note("removing %s\n" % f)
1468 try:
1468 try:
1469 os.unlink(f)
1469 os.unlink(f)
1470 except OSError, inst:
1470 except OSError, inst:
1471 self.ui.warn("update failed to remove %s: %s!\n" % (f, inst))
1471 self.ui.warn("update failed to remove %s: %s!\n" % (f, inst))
1472 # try removing directories that might now be empty
1472 # try removing directories that might now be empty
1473 try: os.removedirs(os.path.dirname(f))
1473 try: os.removedirs(os.path.dirname(f))
1474 except: pass
1474 except: pass
1475 if moddirstate:
1475 if moddirstate:
1476 if mode == 'n':
1476 if mode == 'n':
1477 self.dirstate.forget(remove)
1477 self.dirstate.forget(remove)
1478 else:
1478 else:
1479 self.dirstate.update(remove, 'r')
1479 self.dirstate.update(remove, 'r')
1480
1480
1481 def merge3(self, fn, my, other):
1481 def merge3(self, fn, my, other):
1482 """perform a 3-way merge in the working directory"""
1482 """perform a 3-way merge in the working directory"""
1483
1483
1484 def temp(prefix, node):
1484 def temp(prefix, node):
1485 pre = "%s~%s." % (os.path.basename(fn), prefix)
1485 pre = "%s~%s." % (os.path.basename(fn), prefix)
1486 (fd, name) = tempfile.mkstemp("", pre)
1486 (fd, name) = tempfile.mkstemp("", pre)
1487 f = os.fdopen(fd, "wb")
1487 f = os.fdopen(fd, "wb")
1488 f.write(fl.revision(node))
1488 f.write(fl.revision(node))
1489 f.close()
1489 f.close()
1490 return name
1490 return name
1491
1491
1492 fl = self.file(fn)
1492 fl = self.file(fn)
1493 base = fl.ancestor(my, other)
1493 base = fl.ancestor(my, other)
1494 a = self.wjoin(fn)
1494 a = self.wjoin(fn)
1495 b = temp("base", base)
1495 b = temp("base", base)
1496 c = temp("other", other)
1496 c = temp("other", other)
1497
1497
1498 self.ui.note("resolving %s\n" % fn)
1498 self.ui.note("resolving %s\n" % fn)
1499 self.ui.debug("file %s: other %s ancestor %s\n" %
1499 self.ui.debug("file %s: other %s ancestor %s\n" %
1500 (fn, short(other), short(base)))
1500 (fn, short(other), short(base)))
1501
1501
1502 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1502 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1503 or "hgmerge")
1503 or "hgmerge")
1504 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1504 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1505 if r:
1505 if r:
1506 self.ui.warn("merging %s failed!\n" % fn)
1506 self.ui.warn("merging %s failed!\n" % fn)
1507
1507
1508 os.unlink(b)
1508 os.unlink(b)
1509 os.unlink(c)
1509 os.unlink(c)
1510
1510
1511 def verify(self):
1511 def verify(self):
1512 filelinkrevs = {}
1512 filelinkrevs = {}
1513 filenodes = {}
1513 filenodes = {}
1514 changesets = revisions = files = 0
1514 changesets = revisions = files = 0
1515 errors = 0
1515 errors = 0
1516
1516
1517 seen = {}
1517 seen = {}
1518 self.ui.status("checking changesets\n")
1518 self.ui.status("checking changesets\n")
1519 for i in range(self.changelog.count()):
1519 for i in range(self.changelog.count()):
1520 changesets += 1
1520 changesets += 1
1521 n = self.changelog.node(i)
1521 n = self.changelog.node(i)
1522 if n in seen:
1522 if n in seen:
1523 self.ui.warn("duplicate changeset at revision %d\n" % i)
1523 self.ui.warn("duplicate changeset at revision %d\n" % i)
1524 errors += 1
1524 errors += 1
1525 seen[n] = 1
1525 seen[n] = 1
1526
1526
1527 for p in self.changelog.parents(n):
1527 for p in self.changelog.parents(n):
1528 if p not in self.changelog.nodemap:
1528 if p not in self.changelog.nodemap:
1529 self.ui.warn("changeset %s has unknown parent %s\n" %
1529 self.ui.warn("changeset %s has unknown parent %s\n" %
1530 (short(n), short(p)))
1530 (short(n), short(p)))
1531 errors += 1
1531 errors += 1
1532 try:
1532 try:
1533 changes = self.changelog.read(n)
1533 changes = self.changelog.read(n)
1534 except Exception, inst:
1534 except Exception, inst:
1535 self.ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
1535 self.ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
1536 errors += 1
1536 errors += 1
1537
1537
1538 for f in changes[3]:
1538 for f in changes[3]:
1539 filelinkrevs.setdefault(f, []).append(i)
1539 filelinkrevs.setdefault(f, []).append(i)
1540
1540
1541 seen = {}
1541 seen = {}
1542 self.ui.status("checking manifests\n")
1542 self.ui.status("checking manifests\n")
1543 for i in range(self.manifest.count()):
1543 for i in range(self.manifest.count()):
1544 n = self.manifest.node(i)
1544 n = self.manifest.node(i)
1545 if n in seen:
1545 if n in seen:
1546 self.ui.warn("duplicate manifest at revision %d\n" % i)
1546 self.ui.warn("duplicate manifest at revision %d\n" % i)
1547 errors += 1
1547 errors += 1
1548 seen[n] = 1
1548 seen[n] = 1
1549
1549
1550 for p in self.manifest.parents(n):
1550 for p in self.manifest.parents(n):
1551 if p not in self.manifest.nodemap:
1551 if p not in self.manifest.nodemap:
1552 self.ui.warn("manifest %s has unknown parent %s\n" %
1552 self.ui.warn("manifest %s has unknown parent %s\n" %
1553 (short(n), short(p)))
1553 (short(n), short(p)))
1554 errors += 1
1554 errors += 1
1555
1555
1556 try:
1556 try:
1557 delta = mdiff.patchtext(self.manifest.delta(n))
1557 delta = mdiff.patchtext(self.manifest.delta(n))
1558 except KeyboardInterrupt:
1558 except KeyboardInterrupt:
1559 self.ui.warn("aborted")
1559 self.ui.warn("aborted")
1560 sys.exit(0)
1560 sys.exit(0)
1561 except Exception, inst:
1561 except Exception, inst:
1562 self.ui.warn("unpacking manifest %s: %s\n"
1562 self.ui.warn("unpacking manifest %s: %s\n"
1563 % (short(n), inst))
1563 % (short(n), inst))
1564 errors += 1
1564 errors += 1
1565
1565
1566 ff = [ l.split('\0') for l in delta.splitlines() ]
1566 ff = [ l.split('\0') for l in delta.splitlines() ]
1567 for f, fn in ff:
1567 for f, fn in ff:
1568 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1568 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1569
1569
1570 self.ui.status("crosschecking files in changesets and manifests\n")
1570 self.ui.status("crosschecking files in changesets and manifests\n")
1571 for f in filenodes:
1571 for f in filenodes:
1572 if f not in filelinkrevs:
1572 if f not in filelinkrevs:
1573 self.ui.warn("file %s in manifest but not in changesets\n" % f)
1573 self.ui.warn("file %s in manifest but not in changesets\n" % f)
1574 errors += 1
1574 errors += 1
1575
1575
1576 for f in filelinkrevs:
1576 for f in filelinkrevs:
1577 if f not in filenodes:
1577 if f not in filenodes:
1578 self.ui.warn("file %s in changeset but not in manifest\n" % f)
1578 self.ui.warn("file %s in changeset but not in manifest\n" % f)
1579 errors += 1
1579 errors += 1
1580
1580
1581 self.ui.status("checking files\n")
1581 self.ui.status("checking files\n")
1582 ff = filenodes.keys()
1582 ff = filenodes.keys()
1583 ff.sort()
1583 ff.sort()
1584 for f in ff:
1584 for f in ff:
1585 if f == "/dev/null": continue
1585 if f == "/dev/null": continue
1586 files += 1
1586 files += 1
1587 fl = self.file(f)
1587 fl = self.file(f)
1588 nodes = { nullid: 1 }
1588 nodes = { nullid: 1 }
1589 seen = {}
1589 seen = {}
1590 for i in range(fl.count()):
1590 for i in range(fl.count()):
1591 revisions += 1
1591 revisions += 1
1592 n = fl.node(i)
1592 n = fl.node(i)
1593
1593
1594 if n in seen:
1594 if n in seen:
1595 self.ui.warn("%s: duplicate revision %d\n" % (f, i))
1595 self.ui.warn("%s: duplicate revision %d\n" % (f, i))
1596 errors += 1
1596 errors += 1
1597
1597
1598 if n not in filenodes[f]:
1598 if n not in filenodes[f]:
1599 self.ui.warn("%s: %d:%s not in manifests\n"
1599 self.ui.warn("%s: %d:%s not in manifests\n"
1600 % (f, i, short(n)))
1600 % (f, i, short(n)))
1601 errors += 1
1601 errors += 1
1602 else:
1602 else:
1603 del filenodes[f][n]
1603 del filenodes[f][n]
1604
1604
1605 flr = fl.linkrev(n)
1605 flr = fl.linkrev(n)
1606 if flr not in filelinkrevs[f]:
1606 if flr not in filelinkrevs[f]:
1607 self.ui.warn("%s:%s points to unexpected changeset %d\n"
1607 self.ui.warn("%s:%s points to unexpected changeset %d\n"
1608 % (f, short(n), fl.linkrev(n)))
1608 % (f, short(n), fl.linkrev(n)))
1609 errors += 1
1609 errors += 1
1610 else:
1610 else:
1611 filelinkrevs[f].remove(flr)
1611 filelinkrevs[f].remove(flr)
1612
1612
1613 # verify contents
1613 # verify contents
1614 try:
1614 try:
1615 t = fl.read(n)
1615 t = fl.read(n)
1616 except Exception, inst:
1616 except Exception, inst:
1617 self.ui.warn("unpacking file %s %s: %s\n"
1617 self.ui.warn("unpacking file %s %s: %s\n"
1618 % (f, short(n), inst))
1618 % (f, short(n), inst))
1619 errors += 1
1619 errors += 1
1620
1620
1621 # verify parents
1621 # verify parents
1622 (p1, p2) = fl.parents(n)
1622 (p1, p2) = fl.parents(n)
1623 if p1 not in nodes:
1623 if p1 not in nodes:
1624 self.ui.warn("file %s:%s unknown parent 1 %s" %
1624 self.ui.warn("file %s:%s unknown parent 1 %s" %
1625 (f, short(n), short(p1)))
1625 (f, short(n), short(p1)))
1626 errors += 1
1626 errors += 1
1627 if p2 not in nodes:
1627 if p2 not in nodes:
1628 self.ui.warn("file %s:%s unknown parent 2 %s" %
1628 self.ui.warn("file %s:%s unknown parent 2 %s" %
1629 (f, short(n), short(p1)))
1629 (f, short(n), short(p1)))
1630 errors += 1
1630 errors += 1
1631 nodes[n] = 1
1631 nodes[n] = 1
1632
1632
1633 # cross-check
1633 # cross-check
1634 for node in filenodes[f]:
1634 for node in filenodes[f]:
1635 self.ui.warn("node %s in manifests not in %s\n"
1635 self.ui.warn("node %s in manifests not in %s\n"
1636 % (hex(n), f))
1636 % (hex(node), f))
1637 errors += 1
1637 errors += 1
1638
1638
1639 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1639 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1640 (files, changesets, revisions))
1640 (files, changesets, revisions))
1641
1641
1642 if errors:
1642 if errors:
1643 self.ui.warn("%d integrity errors encountered!\n" % errors)
1643 self.ui.warn("%d integrity errors encountered!\n" % errors)
1644 return 1
1644 return 1
1645
1645
1646 class httprepository:
1646 class httprepository:
1647 def __init__(self, ui, path):
1647 def __init__(self, ui, path):
1648 self.url = path
1648 self.url = path
1649 self.ui = ui
1649 self.ui = ui
1650 no_list = [ "localhost", "127.0.0.1" ]
1650 no_list = [ "localhost", "127.0.0.1" ]
1651 host = ui.config("http_proxy", "host")
1651 host = ui.config("http_proxy", "host")
1652 if host is None:
1652 if host is None:
1653 host = os.environ.get("http_proxy")
1653 host = os.environ.get("http_proxy")
1654 if host and host.startswith('http://'):
1654 if host and host.startswith('http://'):
1655 host = host[7:]
1655 host = host[7:]
1656 user = ui.config("http_proxy", "user")
1656 user = ui.config("http_proxy", "user")
1657 passwd = ui.config("http_proxy", "passwd")
1657 passwd = ui.config("http_proxy", "passwd")
1658 no = ui.config("http_proxy", "no")
1658 no = ui.config("http_proxy", "no")
1659 if no is None:
1659 if no is None:
1660 no = os.environ.get("no_proxy")
1660 no = os.environ.get("no_proxy")
1661 if no:
1661 if no:
1662 no_list = no_list + no.split(",")
1662 no_list = no_list + no.split(",")
1663
1663
1664 no_proxy = 0
1664 no_proxy = 0
1665 for h in no_list:
1665 for h in no_list:
1666 if (path.startswith("http://" + h + "/") or
1666 if (path.startswith("http://" + h + "/") or
1667 path.startswith("http://" + h + ":") or
1667 path.startswith("http://" + h + ":") or
1668 path == "http://" + h):
1668 path == "http://" + h):
1669 no_proxy = 1
1669 no_proxy = 1
1670
1670
1671 # Note: urllib2 takes proxy values from the environment and those will
1671 # Note: urllib2 takes proxy values from the environment and those will
1672 # take precedence
1672 # take precedence
1673 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
1673 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
1674 if os.environ.has_key(env):
1674 if os.environ.has_key(env):
1675 del os.environ[env]
1675 del os.environ[env]
1676
1676
1677 proxy_handler = urllib2.BaseHandler()
1677 proxy_handler = urllib2.BaseHandler()
1678 if host and not no_proxy:
1678 if host and not no_proxy:
1679 proxy_handler = urllib2.ProxyHandler({"http" : "http://" + host})
1679 proxy_handler = urllib2.ProxyHandler({"http" : "http://" + host})
1680
1680
1681 authinfo = None
1681 authinfo = None
1682 if user and passwd:
1682 if user and passwd:
1683 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1683 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1684 passmgr.add_password(None, host, user, passwd)
1684 passmgr.add_password(None, host, user, passwd)
1685 authinfo = urllib2.ProxyBasicAuthHandler(passmgr)
1685 authinfo = urllib2.ProxyBasicAuthHandler(passmgr)
1686
1686
1687 opener = urllib2.build_opener(proxy_handler, authinfo)
1687 opener = urllib2.build_opener(proxy_handler, authinfo)
1688 urllib2.install_opener(opener)
1688 urllib2.install_opener(opener)
1689
1689
1690 def dev(self):
1690 def dev(self):
1691 return -1
1691 return -1
1692
1692
1693 def do_cmd(self, cmd, **args):
1693 def do_cmd(self, cmd, **args):
1694 self.ui.debug("sending %s command\n" % cmd)
1694 self.ui.debug("sending %s command\n" % cmd)
1695 q = {"cmd": cmd}
1695 q = {"cmd": cmd}
1696 q.update(args)
1696 q.update(args)
1697 qs = urllib.urlencode(q)
1697 qs = urllib.urlencode(q)
1698 cu = "%s?%s" % (self.url, qs)
1698 cu = "%s?%s" % (self.url, qs)
1699 return urllib2.urlopen(cu)
1699 return urllib2.urlopen(cu)
1700
1700
1701 def heads(self):
1701 def heads(self):
1702 d = self.do_cmd("heads").read()
1702 d = self.do_cmd("heads").read()
1703 try:
1703 try:
1704 return map(bin, d[:-1].split(" "))
1704 return map(bin, d[:-1].split(" "))
1705 except:
1705 except:
1706 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1706 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1707 raise
1707 raise
1708
1708
1709 def branches(self, nodes):
1709 def branches(self, nodes):
1710 n = " ".join(map(hex, nodes))
1710 n = " ".join(map(hex, nodes))
1711 d = self.do_cmd("branches", nodes=n).read()
1711 d = self.do_cmd("branches", nodes=n).read()
1712 try:
1712 try:
1713 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1713 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1714 return br
1714 return br
1715 except:
1715 except:
1716 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1716 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1717 raise
1717 raise
1718
1718
1719 def between(self, pairs):
1719 def between(self, pairs):
1720 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1720 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1721 d = self.do_cmd("between", pairs=n).read()
1721 d = self.do_cmd("between", pairs=n).read()
1722 try:
1722 try:
1723 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1723 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1724 return p
1724 return p
1725 except:
1725 except:
1726 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1726 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1727 raise
1727 raise
1728
1728
1729 def changegroup(self, nodes):
1729 def changegroup(self, nodes):
1730 n = " ".join(map(hex, nodes))
1730 n = " ".join(map(hex, nodes))
1731 f = self.do_cmd("changegroup", roots=n)
1731 f = self.do_cmd("changegroup", roots=n)
1732 bytes = 0
1732 bytes = 0
1733
1733
1734 class zread:
1734 class zread:
1735 def __init__(self, f):
1735 def __init__(self, f):
1736 self.zd = zlib.decompressobj()
1736 self.zd = zlib.decompressobj()
1737 self.f = f
1737 self.f = f
1738 self.buf = ""
1738 self.buf = ""
1739 def read(self, l):
1739 def read(self, l):
1740 while l > len(self.buf):
1740 while l > len(self.buf):
1741 r = f.read(4096)
1741 r = f.read(4096)
1742 if r:
1742 if r:
1743 self.buf += self.zd.decompress(r)
1743 self.buf += self.zd.decompress(r)
1744 else:
1744 else:
1745 self.buf += self.zd.flush()
1745 self.buf += self.zd.flush()
1746 break
1746 break
1747 d, self.buf = self.buf[:l], self.buf[l:]
1747 d, self.buf = self.buf[:l], self.buf[l:]
1748 return d
1748 return d
1749
1749
1750 return zread(f)
1750 return zread(f)
1751
1751
1752 class remotelock:
1752 class remotelock:
1753 def __init__(self, repo):
1753 def __init__(self, repo):
1754 self.repo = repo
1754 self.repo = repo
1755 def release(self):
1755 def release(self):
1756 self.repo.unlock()
1756 self.repo.unlock()
1757 self.repo = None
1757 self.repo = None
1758 def __del__(self):
1758 def __del__(self):
1759 if self.repo:
1759 if self.repo:
1760 self.release()
1760 self.release()
1761
1761
1762 class sshrepository:
1762 class sshrepository:
1763 def __init__(self, ui, path):
1763 def __init__(self, ui, path):
1764 self.url = path
1764 self.url = path
1765 self.ui = ui
1765 self.ui = ui
1766
1766
1767 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', path)
1767 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', path)
1768 if not m:
1768 if not m:
1769 raise RepoError("couldn't parse destination %s\n" % path)
1769 raise RepoError("couldn't parse destination %s\n" % path)
1770
1770
1771 self.user = m.group(2)
1771 self.user = m.group(2)
1772 self.host = m.group(3)
1772 self.host = m.group(3)
1773 self.port = m.group(5)
1773 self.port = m.group(5)
1774 self.path = m.group(7)
1774 self.path = m.group(7)
1775
1775
1776 args = self.user and ("%s@%s" % (self.user, self.host)) or self.host
1776 args = self.user and ("%s@%s" % (self.user, self.host)) or self.host
1777 args = self.port and ("%s -p %s") % (args, self.port) or args
1777 args = self.port and ("%s -p %s") % (args, self.port) or args
1778 path = self.path or ""
1778 path = self.path or ""
1779
1779
1780 cmd = "ssh %s 'hg -R %s serve --stdio'"
1780 cmd = "ssh %s 'hg -R %s serve --stdio'"
1781 cmd = cmd % (args, path)
1781 cmd = cmd % (args, path)
1782
1782
1783 self.pipeo, self.pipei, self.pipee = os.popen3(cmd)
1783 self.pipeo, self.pipei, self.pipee = os.popen3(cmd)
1784
1784
1785 def readerr(self):
1785 def readerr(self):
1786 while 1:
1786 while 1:
1787 r,w,x = select.select([self.pipee], [], [], 0)
1787 r,w,x = select.select([self.pipee], [], [], 0)
1788 if not r: break
1788 if not r: break
1789 l = self.pipee.readline()
1789 l = self.pipee.readline()
1790 if not l: break
1790 if not l: break
1791 self.ui.status("remote: ", l)
1791 self.ui.status("remote: ", l)
1792
1792
1793 def __del__(self):
1793 def __del__(self):
1794 self.pipeo.close()
1794 self.pipeo.close()
1795 self.pipei.close()
1795 self.pipei.close()
1796 for l in self.pipee:
1796 for l in self.pipee:
1797 self.ui.status("remote: ", l)
1797 self.ui.status("remote: ", l)
1798 self.pipee.close()
1798 self.pipee.close()
1799
1799
1800 def dev(self):
1800 def dev(self):
1801 return -1
1801 return -1
1802
1802
1803 def do_cmd(self, cmd, **args):
1803 def do_cmd(self, cmd, **args):
1804 self.ui.debug("sending %s command\n" % cmd)
1804 self.ui.debug("sending %s command\n" % cmd)
1805 self.pipeo.write("%s\n" % cmd)
1805 self.pipeo.write("%s\n" % cmd)
1806 for k, v in args.items():
1806 for k, v in args.items():
1807 self.pipeo.write("%s %d\n" % (k, len(v)))
1807 self.pipeo.write("%s %d\n" % (k, len(v)))
1808 self.pipeo.write(v)
1808 self.pipeo.write(v)
1809 self.pipeo.flush()
1809 self.pipeo.flush()
1810
1810
1811 return self.pipei
1811 return self.pipei
1812
1812
1813 def call(self, cmd, **args):
1813 def call(self, cmd, **args):
1814 r = self.do_cmd(cmd, **args)
1814 r = self.do_cmd(cmd, **args)
1815 l = r.readline()
1815 l = r.readline()
1816 self.readerr()
1816 self.readerr()
1817 try:
1817 try:
1818 l = int(l)
1818 l = int(l)
1819 except:
1819 except:
1820 raise RepoError("unexpected response '%s'" % l)
1820 raise RepoError("unexpected response '%s'" % l)
1821 return r.read(l)
1821 return r.read(l)
1822
1822
1823 def lock(self):
1823 def lock(self):
1824 self.call("lock")
1824 self.call("lock")
1825 return remotelock(self)
1825 return remotelock(self)
1826
1826
1827 def unlock(self):
1827 def unlock(self):
1828 self.call("unlock")
1828 self.call("unlock")
1829
1829
1830 def heads(self):
1830 def heads(self):
1831 d = self.call("heads")
1831 d = self.call("heads")
1832 try:
1832 try:
1833 return map(bin, d[:-1].split(" "))
1833 return map(bin, d[:-1].split(" "))
1834 except:
1834 except:
1835 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1835 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1836
1836
1837 def branches(self, nodes):
1837 def branches(self, nodes):
1838 n = " ".join(map(hex, nodes))
1838 n = " ".join(map(hex, nodes))
1839 d = self.call("branches", nodes=n)
1839 d = self.call("branches", nodes=n)
1840 try:
1840 try:
1841 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1841 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1842 return br
1842 return br
1843 except:
1843 except:
1844 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1844 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1845
1845
1846 def between(self, pairs):
1846 def between(self, pairs):
1847 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1847 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1848 d = self.call("between", pairs=n)
1848 d = self.call("between", pairs=n)
1849 try:
1849 try:
1850 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1850 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1851 return p
1851 return p
1852 except:
1852 except:
1853 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1853 raise RepoError("unexpected response '%s'" % (d[:400] + "..."))
1854
1854
1855 def changegroup(self, nodes):
1855 def changegroup(self, nodes):
1856 n = " ".join(map(hex, nodes))
1856 n = " ".join(map(hex, nodes))
1857 f = self.do_cmd("changegroup", roots=n)
1857 f = self.do_cmd("changegroup", roots=n)
1858 return self.pipei
1858 return self.pipei
1859
1859
1860 def addchangegroup(self, cg):
1860 def addchangegroup(self, cg):
1861 d = self.call("addchangegroup")
1861 d = self.call("addchangegroup")
1862 if d:
1862 if d:
1863 raise RepoError("push refused: %s", d)
1863 raise RepoError("push refused: %s", d)
1864
1864
1865 while 1:
1865 while 1:
1866 d = cg.read(4096)
1866 d = cg.read(4096)
1867 if not d: break
1867 if not d: break
1868 self.pipeo.write(d)
1868 self.pipeo.write(d)
1869 self.readerr()
1869 self.readerr()
1870
1870
1871 self.pipeo.flush()
1871 self.pipeo.flush()
1872
1872
1873 self.readerr()
1873 self.readerr()
1874 l = int(self.pipei.readline())
1874 l = int(self.pipei.readline())
1875 return self.pipei.read(l) != ""
1875 return self.pipei.read(l) != ""
1876
1876
1877 def repository(ui, path=None, create=0):
1877 def repository(ui, path=None, create=0):
1878 if path:
1878 if path:
1879 if path.startswith("http://"):
1879 if path.startswith("http://"):
1880 return httprepository(ui, path)
1880 return httprepository(ui, path)
1881 if path.startswith("hg://"):
1881 if path.startswith("hg://"):
1882 return httprepository(ui, path.replace("hg://", "http://"))
1882 return httprepository(ui, path.replace("hg://", "http://"))
1883 if path.startswith("old-http://"):
1883 if path.startswith("old-http://"):
1884 return localrepository(ui, path.replace("old-http://", "http://"))
1884 return localrepository(ui, path.replace("old-http://", "http://"))
1885 if path.startswith("ssh://"):
1885 if path.startswith("ssh://"):
1886 return sshrepository(ui, path)
1886 return sshrepository(ui, path)
1887
1887
1888 return localrepository(ui, path, create)
1888 return localrepository(ui, path, create)
General Comments 0
You need to be logged in to leave comments. Login now