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