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