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