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