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