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