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