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