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