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