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