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