##// END OF EJS Templates
Move hex/bin bits to revlog...
mpm@selenic.com -
r37:a8811676 default
parent child Browse files
Show More
@@ -1,614 +1,613 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, sha, socket, os, time, base64, re, urllib2, binascii
8 import sys, struct, sha, socket, os, time, base64, re, urllib2
9 import urllib
9 import urllib
10 from mercurial import byterange
10 from mercurial import byterange
11 from mercurial.transaction import *
11 from mercurial.transaction import *
12 from mercurial.revlog import *
12 from mercurial.revlog import *
13
13
14 def hex(node): return binascii.hexlify(node)
15 def bin(node): return binascii.unhexlify(node)
16
17 class filelog(revlog):
14 class filelog(revlog):
18 def __init__(self, opener, path):
15 def __init__(self, opener, path):
19 s = self.encodepath(path)
16 s = self.encodepath(path)
20 revlog.__init__(self, opener, os.path.join("data", s + "i"),
17 revlog.__init__(self, opener, os.path.join("data", s + "i"),
21 os.path.join("data", s))
18 os.path.join("data", s))
22
19
23 def encodepath(self, path):
20 def encodepath(self, path):
24 s = sha.sha(path).digest()
21 s = sha.sha(path).digest()
25 s = base64.encodestring(s)[:-3]
22 s = base64.encodestring(s)[:-3]
26 s = re.sub("\+", "%", s)
23 s = re.sub("\+", "%", s)
27 s = re.sub("/", "_", s)
24 s = re.sub("/", "_", s)
28 return s
25 return s
29
26
30 def read(self, node):
27 def read(self, node):
31 return self.revision(node)
28 return self.revision(node)
32 def add(self, text, transaction, link, p1=None, p2=None):
29 def add(self, text, transaction, link, p1=None, p2=None):
33 return self.addrevision(text, transaction, link, p1, p2)
30 return self.addrevision(text, transaction, link, p1, p2)
34
31
35 def resolvedag(self, old, new, transaction, link):
32 def resolvedag(self, old, new, transaction, link):
36 """resolve unmerged heads in our DAG"""
33 """resolve unmerged heads in our DAG"""
37 if old == new: return None
34 if old == new: return None
38 a = self.ancestor(old, new)
35 a = self.ancestor(old, new)
39 if old == a: return new
36 if old == a: return new
40 return self.merge3(old, new, a, transaction, link)
37 return self.merge3(old, new, a, transaction, link)
41
38
42 def merge3(self, my, other, base, transaction, link):
39 def merge3(self, my, other, base, transaction, link):
43 """perform a 3-way merge and append the result"""
40 """perform a 3-way merge and append the result"""
44 def temp(prefix, node):
41 def temp(prefix, node):
45 (fd, name) = tempfile.mkstemp(prefix)
42 (fd, name) = tempfile.mkstemp(prefix)
46 f = os.fdopen(fd, "w")
43 f = os.fdopen(fd, "w")
47 f.write(self.revision(node))
44 f.write(self.revision(node))
48 f.close()
45 f.close()
49 return name
46 return name
50
47
51 a = temp("local", my)
48 a = temp("local", my)
52 b = temp("remote", other)
49 b = temp("remote", other)
53 c = temp("parent", base)
50 c = temp("parent", base)
54
51
55 cmd = os.environ["HGMERGE"]
52 cmd = os.environ["HGMERGE"]
56 r = os.system("%s %s %s %s" % (cmd, a, b, c))
53 r = os.system("%s %s %s %s" % (cmd, a, b, c))
57 if r:
54 if r:
58 raise "Merge failed, implement rollback!"
55 raise "Merge failed, implement rollback!"
59
56
60 t = open(a).read()
57 t = open(a).read()
61 os.unlink(a)
58 os.unlink(a)
62 os.unlink(b)
59 os.unlink(b)
63 os.unlink(c)
60 os.unlink(c)
64 return self.addrevision(t, transaction, link, my, other)
61 return self.addrevision(t, transaction, link, my, other)
65
62
66 def merge(self, other, transaction, linkseq, link):
63 def merge(self, other, transaction, linkseq, link):
67 """perform a merge and resolve resulting heads"""
64 """perform a merge and resolve resulting heads"""
68 (o, n) = self.mergedag(other, transaction, linkseq)
65 (o, n) = self.mergedag(other, transaction, linkseq)
69 return self.resolvedag(o, n, transaction, link)
66 return self.resolvedag(o, n, transaction, link)
70
67
71 class manifest(revlog):
68 class manifest(revlog):
72 def __init__(self, opener):
69 def __init__(self, opener):
73 self.mapcache = None
70 self.mapcache = None
74 self.listcache = None
71 self.listcache = None
75 self.addlist = None
72 self.addlist = None
76 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
73 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
77
74
78 def read(self, node):
75 def read(self, node):
79 if self.mapcache and self.mapcache[0] == node:
76 if self.mapcache and self.mapcache[0] == node:
80 return self.mapcache[1]
77 return self.mapcache[1]
81 text = self.revision(node)
78 text = self.revision(node)
82 map = {}
79 map = {}
83 self.listcache = (text, text.splitlines(1))
80 self.listcache = (text, text.splitlines(1))
84 for l in self.listcache[1]:
81 for l in self.listcache[1]:
85 (f, n) = l.split('\0')
82 (f, n) = l.split('\0')
86 map[f] = bin(n[:40])
83 map[f] = bin(n[:40])
87 self.mapcache = (node, map)
84 self.mapcache = (node, map)
88 return map
85 return map
89
86
90 def diff(self, a, b):
87 def diff(self, a, b):
91 # this is sneaky, as we're not actually using a and b
88 # this is sneaky, as we're not actually using a and b
92 if self.listcache and len(self.listcache[0]) == len(a):
89 if self.listcache and len(self.listcache[0]) == len(a):
93 return mdiff.diff(self.listcache[1], self.addlist, 1)
90 return mdiff.diff(self.listcache[1], self.addlist, 1)
94 else:
91 else:
95 return mdiff.diff(a, b)
92 return mdiff.diff(a, b)
96
93
97 def add(self, map, transaction, link, p1=None, p2=None):
94 def add(self, map, transaction, link, p1=None, p2=None):
98 files = map.keys()
95 files = map.keys()
99 files.sort()
96 files.sort()
100
97
101 self.addlist = ["%s\000%s\n" % (f, hex(map[f])) for f in files]
98 self.addlist = ["%s\000%s\n" % (f, hex(map[f])) for f in files]
102 text = "".join(self.addlist)
99 text = "".join(self.addlist)
103
100
104 n = self.addrevision(text, transaction, link, p1, p2)
101 n = self.addrevision(text, transaction, link, p1, p2)
105 self.mapcache = (n, map)
102 self.mapcache = (n, map)
106 self.listcache = (text, self.addlist)
103 self.listcache = (text, self.addlist)
107
104
108 return n
105 return n
109
106
110 class changelog(revlog):
107 class changelog(revlog):
111 def __init__(self, opener):
108 def __init__(self, opener):
112 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
109 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
113
110
114 def extract(self, text):
111 def extract(self, text):
112 if not text:
113 return (nullid, "", 0, [], "")
115 last = text.index("\n\n")
114 last = text.index("\n\n")
116 desc = text[last + 2:]
115 desc = text[last + 2:]
117 l = text[:last].splitlines()
116 l = text[:last].splitlines()
118 manifest = bin(l[0])
117 manifest = bin(l[0])
119 user = l[1]
118 user = l[1]
120 date = l[2]
119 date = l[2]
121 files = l[3:]
120 files = l[3:]
122 return (manifest, user, date, files, desc)
121 return (manifest, user, date, files, desc)
123
122
124 def read(self, node):
123 def read(self, node):
125 return self.extract(self.revision(node))
124 return self.extract(self.revision(node))
126
125
127 def add(self, manifest, list, desc, transaction, p1=None, p2=None):
126 def add(self, manifest, list, desc, transaction, p1=None, p2=None):
128 try: user = os.environ["HGUSER"]
127 try: user = os.environ["HGUSER"]
129 except: user = os.environ["LOGNAME"] + '@' + socket.getfqdn()
128 except: user = os.environ["LOGNAME"] + '@' + socket.getfqdn()
130 date = "%d %d" % (time.time(), time.timezone)
129 date = "%d %d" % (time.time(), time.timezone)
131 list.sort()
130 list.sort()
132 l = [hex(manifest), user, date] + list + ["", desc]
131 l = [hex(manifest), user, date] + list + ["", desc]
133 text = "\n".join(l)
132 text = "\n".join(l)
134 return self.addrevision(text, transaction, self.count(), p1, p2)
133 return self.addrevision(text, transaction, self.count(), p1, p2)
135
134
136 def merge3(self, my, other, base):
135 def merge3(self, my, other, base):
137 pass
136 pass
138
137
139 class dircache:
138 class dircache:
140 def __init__(self, opener, ui):
139 def __init__(self, opener, ui):
141 self.opener = opener
140 self.opener = opener
142 self.dirty = 0
141 self.dirty = 0
143 self.ui = ui
142 self.ui = ui
144 self.map = None
143 self.map = None
145 def __del__(self):
144 def __del__(self):
146 if self.dirty: self.write()
145 if self.dirty: self.write()
147 def __getitem__(self, key):
146 def __getitem__(self, key):
148 try:
147 try:
149 return self.map[key]
148 return self.map[key]
150 except TypeError:
149 except TypeError:
151 self.read()
150 self.read()
152 return self[key]
151 return self[key]
153
152
154 def read(self):
153 def read(self):
155 if self.map is not None: return self.map
154 if self.map is not None: return self.map
156
155
157 self.map = {}
156 self.map = {}
158 try:
157 try:
159 st = self.opener("dircache").read()
158 st = self.opener("dircache").read()
160 except: return
159 except: return
161
160
162 pos = 0
161 pos = 0
163 while pos < len(st):
162 while pos < len(st):
164 e = struct.unpack(">llll", st[pos:pos+16])
163 e = struct.unpack(">llll", st[pos:pos+16])
165 l = e[3]
164 l = e[3]
166 pos += 16
165 pos += 16
167 f = st[pos:pos + l]
166 f = st[pos:pos + l]
168 self.map[f] = e[:3]
167 self.map[f] = e[:3]
169 pos += l
168 pos += l
170
169
171 def update(self, files):
170 def update(self, files):
172 if not files: return
171 if not files: return
173 self.read()
172 self.read()
174 self.dirty = 1
173 self.dirty = 1
175 for f in files:
174 for f in files:
176 try:
175 try:
177 s = os.stat(f)
176 s = os.stat(f)
178 self.map[f] = (s.st_mode, s.st_size, s.st_mtime)
177 self.map[f] = (s.st_mode, s.st_size, s.st_mtime)
179 except IOError:
178 except IOError:
180 self.remove(f)
179 self.remove(f)
181
180
182 def taint(self, files):
181 def taint(self, files):
183 if not files: return
182 if not files: return
184 self.read()
183 self.read()
185 self.dirty = 1
184 self.dirty = 1
186 for f in files:
185 for f in files:
187 self.map[f] = (0, -1, 0)
186 self.map[f] = (0, -1, 0)
188
187
189 def remove(self, files):
188 def remove(self, files):
190 if not files: return
189 if not files: return
191 self.read()
190 self.read()
192 self.dirty = 1
191 self.dirty = 1
193 for f in files:
192 for f in files:
194 try:
193 try:
195 del self.map[f]
194 del self.map[f]
196 except KeyError:
195 except KeyError:
197 self.ui.warn("Not in dircache: %s\n" % f)
196 self.ui.warn("Not in dircache: %s\n" % f)
198 pass
197 pass
199
198
200 def clear(self):
199 def clear(self):
201 self.map = {}
200 self.map = {}
202 self.dirty = 1
201 self.dirty = 1
203
202
204 def write(self):
203 def write(self):
205 st = self.opener("dircache", "w")
204 st = self.opener("dircache", "w")
206 for f, e in self.map.items():
205 for f, e in self.map.items():
207 e = struct.pack(">llll", e[0], e[1], e[2], len(f))
206 e = struct.pack(">llll", e[0], e[1], e[2], len(f))
208 st.write(e + f)
207 st.write(e + f)
209 self.dirty = 0
208 self.dirty = 0
210
209
211 def copy(self):
210 def copy(self):
212 self.read()
211 self.read()
213 return self.map.copy()
212 return self.map.copy()
214
213
215 # used to avoid circular references so destructors work
214 # used to avoid circular references so destructors work
216 def opener(base):
215 def opener(base):
217 p = base
216 p = base
218 def o(path, mode="r"):
217 def o(path, mode="r"):
219 if p[:7] == "http://":
218 if p[:7] == "http://":
220 f = os.path.join(p, urllib.quote(path))
219 f = os.path.join(p, urllib.quote(path))
221 return httprangereader(f)
220 return httprangereader(f)
222
221
223 f = os.path.join(p, path)
222 f = os.path.join(p, path)
224
223
225 if mode != "r" and os.path.isfile(f):
224 if mode != "r" and os.path.isfile(f):
226 s = os.stat(f)
225 s = os.stat(f)
227 if s.st_nlink > 1:
226 if s.st_nlink > 1:
228 file(f + ".tmp", "w").write(file(f).read())
227 file(f + ".tmp", "w").write(file(f).read())
229 os.rename(f+".tmp", f)
228 os.rename(f+".tmp", f)
230
229
231 return file(f, mode)
230 return file(f, mode)
232
231
233 return o
232 return o
234
233
235 class repository:
234 class repository:
236 def __init__(self, ui, path=None, create=0):
235 def __init__(self, ui, path=None, create=0):
237 self.remote = 0
236 self.remote = 0
238 if path and path[:7] == "http://":
237 if path and path[:7] == "http://":
239 self.remote = 1
238 self.remote = 1
240 self.path = path
239 self.path = path
241 else:
240 else:
242 if not path:
241 if not path:
243 p = os.getcwd()
242 p = os.getcwd()
244 while not os.path.isdir(os.path.join(p, ".hg")):
243 while not os.path.isdir(os.path.join(p, ".hg")):
245 p = os.path.dirname(p)
244 p = os.path.dirname(p)
246 if p == "/": raise "No repo found"
245 if p == "/": raise "No repo found"
247 path = p
246 path = p
248 self.path = os.path.join(path, ".hg")
247 self.path = os.path.join(path, ".hg")
249
248
250 self.root = path
249 self.root = path
251 self.ui = ui
250 self.ui = ui
252
251
253 if create:
252 if create:
254 os.mkdir(self.path)
253 os.mkdir(self.path)
255 os.mkdir(self.join("data"))
254 os.mkdir(self.join("data"))
256
255
257 self.opener = opener(self.path)
256 self.opener = opener(self.path)
258 self.manifest = manifest(self.opener)
257 self.manifest = manifest(self.opener)
259 self.changelog = changelog(self.opener)
258 self.changelog = changelog(self.opener)
260 self.ignorelist = None
259 self.ignorelist = None
261
260
262 if not self.remote:
261 if not self.remote:
263 self.dircache = dircache(self.opener, ui)
262 self.dircache = dircache(self.opener, ui)
264 try:
263 try:
265 self.current = bin(self.opener("current").read())
264 self.current = bin(self.opener("current").read())
266 except IOError:
265 except IOError:
267 self.current = None
266 self.current = None
268
267
269 def setcurrent(self, node):
268 def setcurrent(self, node):
270 self.current = node
269 self.current = node
271 self.opener("current", "w").write(hex(node))
270 self.opener("current", "w").write(hex(node))
272
271
273 def ignore(self, f):
272 def ignore(self, f):
274 if self.ignorelist is None:
273 if self.ignorelist is None:
275 self.ignorelist = []
274 self.ignorelist = []
276 try:
275 try:
277 l = open(os.path.join(self.root, ".hgignore")).readlines()
276 l = open(os.path.join(self.root, ".hgignore")).readlines()
278 for pat in l:
277 for pat in l:
279 if pat != "\n":
278 if pat != "\n":
280 self.ignorelist.append(re.compile(pat[:-1]))
279 self.ignorelist.append(re.compile(pat[:-1]))
281 except IOError: pass
280 except IOError: pass
282 for pat in self.ignorelist:
281 for pat in self.ignorelist:
283 if pat.search(f): return True
282 if pat.search(f): return True
284 return False
283 return False
285
284
286 def join(self, f):
285 def join(self, f):
287 return os.path.join(self.path, f)
286 return os.path.join(self.path, f)
288
287
289 def file(self, f):
288 def file(self, f):
290 return filelog(self.opener, f)
289 return filelog(self.opener, f)
291
290
292 def transaction(self):
291 def transaction(self):
293 return transaction(self.opener, self.join("journal"))
292 return transaction(self.opener, self.join("journal"))
294
293
295 def merge(self, other):
294 def merge(self, other):
296 tr = self.transaction()
295 tr = self.transaction()
297 changed = {}
296 changed = {}
298 new = {}
297 new = {}
299 seqrev = self.changelog.count()
298 seqrev = self.changelog.count()
300 # some magic to allow fiddling in nested scope
299 # some magic to allow fiddling in nested scope
301 nextrev = [seqrev]
300 nextrev = [seqrev]
302
301
303 # helpers for back-linking file revisions to local changeset
302 # helpers for back-linking file revisions to local changeset
304 # revisions so we can immediately get to changeset from annotate
303 # revisions so we can immediately get to changeset from annotate
305 def accumulate(text):
304 def accumulate(text):
306 # track which files are added in which changeset and the
305 # track which files are added in which changeset and the
307 # corresponding _local_ changeset revision
306 # corresponding _local_ changeset revision
308 files = self.changelog.extract(text)[3]
307 files = self.changelog.extract(text)[3]
309 for f in files:
308 for f in files:
310 changed.setdefault(f, []).append(nextrev[0])
309 changed.setdefault(f, []).append(nextrev[0])
311 nextrev[0] += 1
310 nextrev[0] += 1
312
311
313 def seq(start):
312 def seq(start):
314 while 1:
313 while 1:
315 yield start
314 yield start
316 start += 1
315 start += 1
317
316
318 def lseq(l):
317 def lseq(l):
319 for r in l:
318 for r in l:
320 yield r
319 yield r
321
320
322 # begin the import/merge of changesets
321 # begin the import/merge of changesets
323 self.ui.status("merging new changesets\n")
322 self.ui.status("merging new changesets\n")
324 (co, cn) = self.changelog.mergedag(other.changelog, tr,
323 (co, cn) = self.changelog.mergedag(other.changelog, tr,
325 seq(seqrev), accumulate)
324 seq(seqrev), accumulate)
326 resolverev = self.changelog.count()
325 resolverev = self.changelog.count()
327
326
328 # is there anything to do?
327 # is there anything to do?
329 if co == cn:
328 if co == cn:
330 tr.close()
329 tr.close()
331 return
330 return
332
331
333 # do we need to resolve?
332 # do we need to resolve?
334 simple = (co == self.changelog.ancestor(co, cn))
333 simple = (co == self.changelog.ancestor(co, cn))
335
334
336 # merge all files changed by the changesets,
335 # merge all files changed by the changesets,
337 # keeping track of the new tips
336 # keeping track of the new tips
338 changelist = changed.keys()
337 changelist = changed.keys()
339 changelist.sort()
338 changelist.sort()
340 for f in changelist:
339 for f in changelist:
341 sys.stdout.write(".")
340 sys.stdout.write(".")
342 sys.stdout.flush()
341 sys.stdout.flush()
343 r = self.file(f)
342 r = self.file(f)
344 node = r.merge(other.file(f), tr, lseq(changed[f]), resolverev)
343 node = r.merge(other.file(f), tr, lseq(changed[f]), resolverev)
345 if node:
344 if node:
346 new[f] = node
345 new[f] = node
347 sys.stdout.write("\n")
346 sys.stdout.write("\n")
348
347
349 # begin the merge of the manifest
348 # begin the merge of the manifest
350 self.ui.status("merging manifests\n")
349 self.ui.status("merging manifests\n")
351 (mm, mo) = self.manifest.mergedag(other.manifest, tr, seq(seqrev))
350 (mm, mo) = self.manifest.mergedag(other.manifest, tr, seq(seqrev))
352
351
353 # For simple merges, we don't need to resolve manifests or changesets
352 # For simple merges, we don't need to resolve manifests or changesets
354 if simple:
353 if simple:
355 tr.close()
354 tr.close()
356 return
355 return
357
356
358 ma = self.manifest.ancestor(mm, mo)
357 ma = self.manifest.ancestor(mm, mo)
359
358
360 # resolve the manifest to point to all the merged files
359 # resolve the manifest to point to all the merged files
361 self.ui.status("resolving manifests\n")
360 self.ui.status("resolving manifests\n")
362 mmap = self.manifest.read(mm) # mine
361 mmap = self.manifest.read(mm) # mine
363 omap = self.manifest.read(mo) # other
362 omap = self.manifest.read(mo) # other
364 amap = self.manifest.read(ma) # ancestor
363 amap = self.manifest.read(ma) # ancestor
365 nmap = {}
364 nmap = {}
366
365
367 for f, mid in mmap.iteritems():
366 for f, mid in mmap.iteritems():
368 if f in omap:
367 if f in omap:
369 if mid != omap[f]:
368 if mid != omap[f]:
370 nmap[f] = new.get(f, mid) # use merged version
369 nmap[f] = new.get(f, mid) # use merged version
371 else:
370 else:
372 nmap[f] = new.get(f, mid) # they're the same
371 nmap[f] = new.get(f, mid) # they're the same
373 del omap[f]
372 del omap[f]
374 elif f in amap:
373 elif f in amap:
375 if mid != amap[f]:
374 if mid != amap[f]:
376 pass # we should prompt here
375 pass # we should prompt here
377 else:
376 else:
378 pass # other deleted it
377 pass # other deleted it
379 else:
378 else:
380 nmap[f] = new.get(f, mid) # we created it
379 nmap[f] = new.get(f, mid) # we created it
381
380
382 del mmap
381 del mmap
383
382
384 for f, oid in omap.iteritems():
383 for f, oid in omap.iteritems():
385 if f in amap:
384 if f in amap:
386 if oid != amap[f]:
385 if oid != amap[f]:
387 pass # this is the nasty case, we should prompt
386 pass # this is the nasty case, we should prompt
388 else:
387 else:
389 pass # probably safe
388 pass # probably safe
390 else:
389 else:
391 nmap[f] = new.get(f, oid) # remote created it
390 nmap[f] = new.get(f, oid) # remote created it
392
391
393 del omap
392 del omap
394 del amap
393 del amap
395
394
396 node = self.manifest.add(nmap, tr, resolverev, mm, mo)
395 node = self.manifest.add(nmap, tr, resolverev, mm, mo)
397
396
398 # Now all files and manifests are merged, we add the changed files
397 # Now all files and manifests are merged, we add the changed files
399 # and manifest id to the changelog
398 # and manifest id to the changelog
400 self.ui.status("committing merge changeset\n")
399 self.ui.status("committing merge changeset\n")
401 new = new.keys()
400 new = new.keys()
402 new.sort()
401 new.sort()
403 if co == cn: cn = -1
402 if co == cn: cn = -1
404
403
405 edittext = "\n"+"".join(["HG: changed %s\n" % f for f in new])
404 edittext = "\n"+"".join(["HG: changed %s\n" % f for f in new])
406 edittext = self.ui.edit(edittext)
405 edittext = self.ui.edit(edittext)
407 n = self.changelog.add(node, new, edittext, tr, co, cn)
406 n = self.changelog.add(node, new, edittext, tr, co, cn)
408
407
409 tr.close()
408 tr.close()
410
409
411 def commit(self, parent, update = None, text = ""):
410 def commit(self, parent, update = None, text = ""):
412 tr = self.transaction()
411 tr = self.transaction()
413
412
414 try:
413 try:
415 remove = [ l[:-1] for l in self.opener("to-remove") ]
414 remove = [ l[:-1] for l in self.opener("to-remove") ]
416 os.unlink(self.join("to-remove"))
415 os.unlink(self.join("to-remove"))
417
416
418 except IOError:
417 except IOError:
419 remove = []
418 remove = []
420
419
421 if update == None:
420 if update == None:
422 update = self.diffdir(self.root, parent)[0]
421 update = self.diffdir(self.root, parent)[0]
423
422
424 # check in files
423 # check in files
425 new = {}
424 new = {}
426 linkrev = self.changelog.count()
425 linkrev = self.changelog.count()
427 for f in update:
426 for f in update:
428 try:
427 try:
429 t = file(f).read()
428 t = file(f).read()
430 except IOError:
429 except IOError:
431 remove.append(f)
430 remove.append(f)
432 continue
431 continue
433 r = self.file(f)
432 r = self.file(f)
434 new[f] = r.add(t, tr, linkrev)
433 new[f] = r.add(t, tr, linkrev)
435
434
436 # update manifest
435 # update manifest
437 mmap = self.manifest.read(self.manifest.tip())
436 mmap = self.manifest.read(self.manifest.tip())
438 mmap.update(new)
437 mmap.update(new)
439 for f in remove:
438 for f in remove:
440 del mmap[f]
439 del mmap[f]
441 mnode = self.manifest.add(mmap, tr, linkrev)
440 mnode = self.manifest.add(mmap, tr, linkrev)
442
441
443 # add changeset
442 # add changeset
444 new = new.keys()
443 new = new.keys()
445 new.sort()
444 new.sort()
446
445
447 edittext = text + "\n"+"".join(["HG: changed %s\n" % f for f in new])
446 edittext = text + "\n"+"".join(["HG: changed %s\n" % f for f in new])
448 edittext += "".join(["HG: removed %s\n" % f for f in remove])
447 edittext += "".join(["HG: removed %s\n" % f for f in remove])
449 edittext = self.ui.edit(edittext)
448 edittext = self.ui.edit(edittext)
450
449
451 n = self.changelog.add(mnode, new, edittext, tr)
450 n = self.changelog.add(mnode, new, edittext, tr)
452 tr.close()
451 tr.close()
453
452
454 self.setcurrent(n)
453 self.setcurrent(n)
455 self.dircache.update(new)
454 self.dircache.update(new)
456 self.dircache.remove(remove)
455 self.dircache.remove(remove)
457
456
458 def checkdir(self, path):
457 def checkdir(self, path):
459 d = os.path.dirname(path)
458 d = os.path.dirname(path)
460 if not d: return
459 if not d: return
461 if not os.path.isdir(d):
460 if not os.path.isdir(d):
462 self.checkdir(d)
461 self.checkdir(d)
463 os.mkdir(d)
462 os.mkdir(d)
464
463
465 def checkout(self, node):
464 def checkout(self, node):
466 # checkout is really dumb at the moment
465 # checkout is really dumb at the moment
467 # it ought to basically merge
466 # it ought to basically merge
468 change = self.changelog.read(node)
467 change = self.changelog.read(node)
469 mmap = self.manifest.read(change[0])
468 mmap = self.manifest.read(change[0])
470
469
471 l = mmap.keys()
470 l = mmap.keys()
472 l.sort()
471 l.sort()
473 stats = []
472 stats = []
474 for f in l:
473 for f in l:
475 r = self.file(f)
474 r = self.file(f)
476 t = r.revision(mmap[f])
475 t = r.revision(mmap[f])
477 try:
476 try:
478 file(f, "w").write(t)
477 file(f, "w").write(t)
479 except:
478 except:
480 self.checkdir(f)
479 self.checkdir(f)
481 file(f, "w").write(t)
480 file(f, "w").write(t)
482
481
483 self.setcurrent(node)
482 self.setcurrent(node)
484 self.dircache.clear()
483 self.dircache.clear()
485 self.dircache.update(l)
484 self.dircache.update(l)
486
485
487 def diffdir(self, path, changeset):
486 def diffdir(self, path, changeset):
488 changed = []
487 changed = []
489 mf = {}
488 mf = {}
490 added = []
489 added = []
491
490
492 if changeset:
491 if changeset:
493 change = self.changelog.read(changeset)
492 change = self.changelog.read(changeset)
494 mf = self.manifest.read(change[0])
493 mf = self.manifest.read(change[0])
495
494
496 if changeset == self.current:
495 if changeset == self.current:
497 dc = self.dircache.copy()
496 dc = self.dircache.copy()
498 else:
497 else:
499 dc = dict.fromkeys(mf)
498 dc = dict.fromkeys(mf)
500
499
501 def fcmp(fn):
500 def fcmp(fn):
502 t1 = file(fn).read()
501 t1 = file(fn).read()
503 t2 = self.file(fn).revision(mf[fn])
502 t2 = self.file(fn).revision(mf[fn])
504 return cmp(t1, t2)
503 return cmp(t1, t2)
505
504
506 for dir, subdirs, files in os.walk(self.root):
505 for dir, subdirs, files in os.walk(self.root):
507 d = dir[len(self.root)+1:]
506 d = dir[len(self.root)+1:]
508 if ".hg" in subdirs: subdirs.remove(".hg")
507 if ".hg" in subdirs: subdirs.remove(".hg")
509
508
510 for f in files:
509 for f in files:
511 fn = os.path.join(d, f)
510 fn = os.path.join(d, f)
512 try: s = os.stat(fn)
511 try: s = os.stat(fn)
513 except: continue
512 except: continue
514 if fn in dc:
513 if fn in dc:
515 c = dc[fn]
514 c = dc[fn]
516 del dc[fn]
515 del dc[fn]
517 if not c:
516 if not c:
518 if fcmp(fn):
517 if fcmp(fn):
519 changed.append(fn)
518 changed.append(fn)
520 elif c[1] != s.st_size:
519 elif c[1] != s.st_size:
521 changed.append(fn)
520 changed.append(fn)
522 elif c[0] != s.st_mode or c[2] != s.st_mtime:
521 elif c[0] != s.st_mode or c[2] != s.st_mtime:
523 if fcmp(fn):
522 if fcmp(fn):
524 changed.append(fn)
523 changed.append(fn)
525 else:
524 else:
526 if self.ignore(fn): continue
525 if self.ignore(fn): continue
527 added.append(fn)
526 added.append(fn)
528
527
529 deleted = dc.keys()
528 deleted = dc.keys()
530 deleted.sort()
529 deleted.sort()
531
530
532 return (changed, added, deleted)
531 return (changed, added, deleted)
533
532
534 def diffrevs(self, node1, node2):
533 def diffrevs(self, node1, node2):
535 changed, added = [], []
534 changed, added = [], []
536
535
537 change = self.changelog.read(node1)
536 change = self.changelog.read(node1)
538 mf1 = self.manifest.read(change[0])
537 mf1 = self.manifest.read(change[0])
539 change = self.changelog.read(node2)
538 change = self.changelog.read(node2)
540 mf2 = self.manifest.read(change[0])
539 mf2 = self.manifest.read(change[0])
541
540
542 for fn in mf2:
541 for fn in mf2:
543 if mf1.has_key(fn):
542 if mf1.has_key(fn):
544 if mf1[fn] != mf2[fn]:
543 if mf1[fn] != mf2[fn]:
545 changed.append(fn)
544 changed.append(fn)
546 del mf1[fn]
545 del mf1[fn]
547 else:
546 else:
548 added.append(fn)
547 added.append(fn)
549
548
550 deleted = mf1.keys()
549 deleted = mf1.keys()
551 deleted.sort()
550 deleted.sort()
552
551
553 return (changed, added, deleted)
552 return (changed, added, deleted)
554
553
555 def add(self, list):
554 def add(self, list):
556 self.dircache.taint(list)
555 self.dircache.taint(list)
557
556
558 def remove(self, list):
557 def remove(self, list):
559 dl = self.opener("to-remove", "a")
558 dl = self.opener("to-remove", "a")
560 for f in list:
559 for f in list:
561 dl.write(f + "\n")
560 dl.write(f + "\n")
562
561
563 class ui:
562 class ui:
564 def __init__(self, verbose=False, debug=False):
563 def __init__(self, verbose=False, debug=False):
565 self.verbose = verbose
564 self.verbose = verbose
566 def write(self, *args):
565 def write(self, *args):
567 for a in args:
566 for a in args:
568 sys.stdout.write(str(a))
567 sys.stdout.write(str(a))
569 def prompt(self, msg, pat):
568 def prompt(self, msg, pat):
570 while 1:
569 while 1:
571 sys.stdout.write(msg)
570 sys.stdout.write(msg)
572 r = sys.stdin.readline()[:-1]
571 r = sys.stdin.readline()[:-1]
573 if re.match(pat, r):
572 if re.match(pat, r):
574 return r
573 return r
575 def status(self, *msg):
574 def status(self, *msg):
576 self.write(*msg)
575 self.write(*msg)
577 def warn(self, msg):
576 def warn(self, msg):
578 self.write(*msg)
577 self.write(*msg)
579 def note(self, msg):
578 def note(self, msg):
580 if self.verbose: self.write(*msg)
579 if self.verbose: self.write(*msg)
581 def debug(self, msg):
580 def debug(self, msg):
582 if self.debug: self.write(*msg)
581 if self.debug: self.write(*msg)
583 def edit(self, text):
582 def edit(self, text):
584 (fd, name) = tempfile.mkstemp("hg")
583 (fd, name) = tempfile.mkstemp("hg")
585 f = os.fdopen(fd, "w")
584 f = os.fdopen(fd, "w")
586 f.write(text)
585 f.write(text)
587 f.close()
586 f.close()
588
587
589 editor = os.environ.get("EDITOR", "vi")
588 editor = os.environ.get("EDITOR", "vi")
590 r = os.system("%s %s" % (editor, name))
589 r = os.system("%s %s" % (editor, name))
591 if r:
590 if r:
592 raise "Edit failed!"
591 raise "Edit failed!"
593
592
594 t = open(name).read()
593 t = open(name).read()
595 t = re.sub("(?m)^HG:.*\n", "", t)
594 t = re.sub("(?m)^HG:.*\n", "", t)
596
595
597 return t
596 return t
598
597
599
598
600 class httprangereader:
599 class httprangereader:
601 def __init__(self, url):
600 def __init__(self, url):
602 self.url = url
601 self.url = url
603 self.pos = 0
602 self.pos = 0
604 def seek(self, pos):
603 def seek(self, pos):
605 self.pos = pos
604 self.pos = pos
606 def read(self, bytes=None):
605 def read(self, bytes=None):
607 opener = urllib2.build_opener(byterange.HTTPRangeHandler())
606 opener = urllib2.build_opener(byterange.HTTPRangeHandler())
608 urllib2.install_opener(opener)
607 urllib2.install_opener(opener)
609 req = urllib2.Request(self.url)
608 req = urllib2.Request(self.url)
610 end = ''
609 end = ''
611 if bytes: end = self.pos + bytes
610 if bytes: end = self.pos + bytes
612 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
611 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
613 f = urllib2.urlopen(req)
612 f = urllib2.urlopen(req)
614 return f.read()
613 return f.read()
General Comments 0
You need to be logged in to leave comments. Login now