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