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