##// END OF EJS Templates
hg verify: add some bin to hex conversions
Matt Mackall -
r1384:d729850d default
parent child Browse files
Show More
@@ -1,1445 +1,1446 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class 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 struct, os, util
8 import struct, os, util
9 import filelog, manifest, changelog, dirstate, repo
9 import filelog, manifest, changelog, dirstate, repo
10 from node import *
10 from node import *
11 from demandload import *
11 from demandload import *
12 demandload(globals(), "re lock transaction tempfile stat mdiff errno")
12 demandload(globals(), "re lock transaction tempfile stat mdiff errno")
13
13
14 class localrepository:
14 class localrepository:
15 def __init__(self, ui, path=None, create=0):
15 def __init__(self, ui, path=None, create=0):
16 if not path:
16 if not path:
17 p = os.getcwd()
17 p = os.getcwd()
18 while not os.path.isdir(os.path.join(p, ".hg")):
18 while not os.path.isdir(os.path.join(p, ".hg")):
19 oldp = p
19 oldp = p
20 p = os.path.dirname(p)
20 p = os.path.dirname(p)
21 if p == oldp: raise repo.RepoError("no repo found")
21 if p == oldp: raise repo.RepoError("no repo found")
22 path = p
22 path = p
23 self.path = os.path.join(path, ".hg")
23 self.path = os.path.join(path, ".hg")
24
24
25 if not create and not os.path.isdir(self.path):
25 if not create and not os.path.isdir(self.path):
26 raise repo.RepoError("repository %s not found" % self.path)
26 raise repo.RepoError("repository %s not found" % self.path)
27
27
28 self.root = os.path.abspath(path)
28 self.root = os.path.abspath(path)
29 self.ui = ui
29 self.ui = ui
30 self.opener = util.opener(self.path)
30 self.opener = util.opener(self.path)
31 self.wopener = util.opener(self.root)
31 self.wopener = util.opener(self.root)
32 self.manifest = manifest.manifest(self.opener)
32 self.manifest = manifest.manifest(self.opener)
33 self.changelog = changelog.changelog(self.opener)
33 self.changelog = changelog.changelog(self.opener)
34 self.tagscache = None
34 self.tagscache = None
35 self.nodetagscache = None
35 self.nodetagscache = None
36 self.encodepats = None
36 self.encodepats = None
37 self.decodepats = None
37 self.decodepats = None
38
38
39 if create:
39 if create:
40 os.mkdir(self.path)
40 os.mkdir(self.path)
41 os.mkdir(self.join("data"))
41 os.mkdir(self.join("data"))
42
42
43 self.dirstate = dirstate.dirstate(self.opener, ui, self.root)
43 self.dirstate = dirstate.dirstate(self.opener, ui, self.root)
44 try:
44 try:
45 self.ui.readconfig(self.opener("hgrc"))
45 self.ui.readconfig(self.opener("hgrc"))
46 except IOError: pass
46 except IOError: pass
47
47
48 def hook(self, name, **args):
48 def hook(self, name, **args):
49 s = self.ui.config("hooks", name)
49 s = self.ui.config("hooks", name)
50 if s:
50 if s:
51 self.ui.note("running hook %s: %s\n" % (name, s))
51 self.ui.note("running hook %s: %s\n" % (name, s))
52 old = {}
52 old = {}
53 for k, v in args.items():
53 for k, v in args.items():
54 k = k.upper()
54 k = k.upper()
55 old[k] = os.environ.get(k, None)
55 old[k] = os.environ.get(k, None)
56 os.environ[k] = v
56 os.environ[k] = v
57
57
58 # Hooks run in the repository root
58 # Hooks run in the repository root
59 olddir = os.getcwd()
59 olddir = os.getcwd()
60 os.chdir(self.root)
60 os.chdir(self.root)
61 r = os.system(s)
61 r = os.system(s)
62 os.chdir(olddir)
62 os.chdir(olddir)
63
63
64 for k, v in old.items():
64 for k, v in old.items():
65 if v != None:
65 if v != None:
66 os.environ[k] = v
66 os.environ[k] = v
67 else:
67 else:
68 del os.environ[k]
68 del os.environ[k]
69
69
70 if r:
70 if r:
71 self.ui.warn("abort: %s hook failed with status %d!\n" %
71 self.ui.warn("abort: %s hook failed with status %d!\n" %
72 (name, r))
72 (name, r))
73 return False
73 return False
74 return True
74 return True
75
75
76 def tags(self):
76 def tags(self):
77 '''return a mapping of tag to node'''
77 '''return a mapping of tag to node'''
78 if not self.tagscache:
78 if not self.tagscache:
79 self.tagscache = {}
79 self.tagscache = {}
80 def addtag(self, k, n):
80 def addtag(self, k, n):
81 try:
81 try:
82 bin_n = bin(n)
82 bin_n = bin(n)
83 except TypeError:
83 except TypeError:
84 bin_n = ''
84 bin_n = ''
85 self.tagscache[k.strip()] = bin_n
85 self.tagscache[k.strip()] = bin_n
86
86
87 try:
87 try:
88 # read each head of the tags file, ending with the tip
88 # read each head of the tags file, ending with the tip
89 # and add each tag found to the map, with "newer" ones
89 # and add each tag found to the map, with "newer" ones
90 # taking precedence
90 # taking precedence
91 fl = self.file(".hgtags")
91 fl = self.file(".hgtags")
92 h = fl.heads()
92 h = fl.heads()
93 h.reverse()
93 h.reverse()
94 for r in h:
94 for r in h:
95 for l in fl.read(r).splitlines():
95 for l in fl.read(r).splitlines():
96 if l:
96 if l:
97 n, k = l.split(" ", 1)
97 n, k = l.split(" ", 1)
98 addtag(self, k, n)
98 addtag(self, k, n)
99 except KeyError:
99 except KeyError:
100 pass
100 pass
101
101
102 try:
102 try:
103 f = self.opener("localtags")
103 f = self.opener("localtags")
104 for l in f:
104 for l in f:
105 n, k = l.split(" ", 1)
105 n, k = l.split(" ", 1)
106 addtag(self, k, n)
106 addtag(self, k, n)
107 except IOError:
107 except IOError:
108 pass
108 pass
109
109
110 self.tagscache['tip'] = self.changelog.tip()
110 self.tagscache['tip'] = self.changelog.tip()
111
111
112 return self.tagscache
112 return self.tagscache
113
113
114 def tagslist(self):
114 def tagslist(self):
115 '''return a list of tags ordered by revision'''
115 '''return a list of tags ordered by revision'''
116 l = []
116 l = []
117 for t, n in self.tags().items():
117 for t, n in self.tags().items():
118 try:
118 try:
119 r = self.changelog.rev(n)
119 r = self.changelog.rev(n)
120 except:
120 except:
121 r = -2 # sort to the beginning of the list if unknown
121 r = -2 # sort to the beginning of the list if unknown
122 l.append((r,t,n))
122 l.append((r,t,n))
123 l.sort()
123 l.sort()
124 return [(t,n) for r,t,n in l]
124 return [(t,n) for r,t,n in l]
125
125
126 def nodetags(self, node):
126 def nodetags(self, node):
127 '''return the tags associated with a node'''
127 '''return the tags associated with a node'''
128 if not self.nodetagscache:
128 if not self.nodetagscache:
129 self.nodetagscache = {}
129 self.nodetagscache = {}
130 for t,n in self.tags().items():
130 for t,n in self.tags().items():
131 self.nodetagscache.setdefault(n,[]).append(t)
131 self.nodetagscache.setdefault(n,[]).append(t)
132 return self.nodetagscache.get(node, [])
132 return self.nodetagscache.get(node, [])
133
133
134 def lookup(self, key):
134 def lookup(self, key):
135 try:
135 try:
136 return self.tags()[key]
136 return self.tags()[key]
137 except KeyError:
137 except KeyError:
138 try:
138 try:
139 return self.changelog.lookup(key)
139 return self.changelog.lookup(key)
140 except:
140 except:
141 raise repo.RepoError("unknown revision '%s'" % key)
141 raise repo.RepoError("unknown revision '%s'" % key)
142
142
143 def dev(self):
143 def dev(self):
144 return os.stat(self.path).st_dev
144 return os.stat(self.path).st_dev
145
145
146 def local(self):
146 def local(self):
147 return True
147 return True
148
148
149 def join(self, f):
149 def join(self, f):
150 return os.path.join(self.path, f)
150 return os.path.join(self.path, f)
151
151
152 def wjoin(self, f):
152 def wjoin(self, f):
153 return os.path.join(self.root, f)
153 return os.path.join(self.root, f)
154
154
155 def file(self, f):
155 def file(self, f):
156 if f[0] == '/': f = f[1:]
156 if f[0] == '/': f = f[1:]
157 return filelog.filelog(self.opener, f)
157 return filelog.filelog(self.opener, f)
158
158
159 def getcwd(self):
159 def getcwd(self):
160 return self.dirstate.getcwd()
160 return self.dirstate.getcwd()
161
161
162 def wfile(self, f, mode='r'):
162 def wfile(self, f, mode='r'):
163 return self.wopener(f, mode)
163 return self.wopener(f, mode)
164
164
165 def wread(self, filename):
165 def wread(self, filename):
166 if self.encodepats == None:
166 if self.encodepats == None:
167 l = []
167 l = []
168 for pat, cmd in self.ui.configitems("encode"):
168 for pat, cmd in self.ui.configitems("encode"):
169 mf = util.matcher("", "/", [pat], [], [])[1]
169 mf = util.matcher("", "/", [pat], [], [])[1]
170 l.append((mf, cmd))
170 l.append((mf, cmd))
171 self.encodepats = l
171 self.encodepats = l
172
172
173 data = self.wopener(filename, 'r').read()
173 data = self.wopener(filename, 'r').read()
174
174
175 for mf, cmd in self.encodepats:
175 for mf, cmd in self.encodepats:
176 if mf(filename):
176 if mf(filename):
177 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
177 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
178 data = util.filter(data, cmd)
178 data = util.filter(data, cmd)
179 break
179 break
180
180
181 return data
181 return data
182
182
183 def wwrite(self, filename, data, fd=None):
183 def wwrite(self, filename, data, fd=None):
184 if self.decodepats == None:
184 if self.decodepats == None:
185 l = []
185 l = []
186 for pat, cmd in self.ui.configitems("decode"):
186 for pat, cmd in self.ui.configitems("decode"):
187 mf = util.matcher("", "/", [pat], [], [])[1]
187 mf = util.matcher("", "/", [pat], [], [])[1]
188 l.append((mf, cmd))
188 l.append((mf, cmd))
189 self.decodepats = l
189 self.decodepats = l
190
190
191 for mf, cmd in self.decodepats:
191 for mf, cmd in self.decodepats:
192 if mf(filename):
192 if mf(filename):
193 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
193 self.ui.debug("filtering %s through %s\n" % (filename, cmd))
194 data = util.filter(data, cmd)
194 data = util.filter(data, cmd)
195 break
195 break
196
196
197 if fd:
197 if fd:
198 return fd.write(data)
198 return fd.write(data)
199 return self.wopener(filename, 'w').write(data)
199 return self.wopener(filename, 'w').write(data)
200
200
201 def transaction(self):
201 def transaction(self):
202 # save dirstate for undo
202 # save dirstate for undo
203 try:
203 try:
204 ds = self.opener("dirstate").read()
204 ds = self.opener("dirstate").read()
205 except IOError:
205 except IOError:
206 ds = ""
206 ds = ""
207 self.opener("journal.dirstate", "w").write(ds)
207 self.opener("journal.dirstate", "w").write(ds)
208
208
209 def after():
209 def after():
210 util.rename(self.join("journal"), self.join("undo"))
210 util.rename(self.join("journal"), self.join("undo"))
211 util.rename(self.join("journal.dirstate"),
211 util.rename(self.join("journal.dirstate"),
212 self.join("undo.dirstate"))
212 self.join("undo.dirstate"))
213
213
214 return transaction.transaction(self.ui.warn, self.opener,
214 return transaction.transaction(self.ui.warn, self.opener,
215 self.join("journal"), after)
215 self.join("journal"), after)
216
216
217 def recover(self):
217 def recover(self):
218 lock = self.lock()
218 lock = self.lock()
219 if os.path.exists(self.join("journal")):
219 if os.path.exists(self.join("journal")):
220 self.ui.status("rolling back interrupted transaction\n")
220 self.ui.status("rolling back interrupted transaction\n")
221 return transaction.rollback(self.opener, self.join("journal"))
221 return transaction.rollback(self.opener, self.join("journal"))
222 else:
222 else:
223 self.ui.warn("no interrupted transaction available\n")
223 self.ui.warn("no interrupted transaction available\n")
224
224
225 def undo(self):
225 def undo(self):
226 lock = self.lock()
226 lock = self.lock()
227 if os.path.exists(self.join("undo")):
227 if os.path.exists(self.join("undo")):
228 self.ui.status("rolling back last transaction\n")
228 self.ui.status("rolling back last transaction\n")
229 transaction.rollback(self.opener, self.join("undo"))
229 transaction.rollback(self.opener, self.join("undo"))
230 self.dirstate = None
230 self.dirstate = None
231 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
231 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
232 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
232 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
233 else:
233 else:
234 self.ui.warn("no undo information available\n")
234 self.ui.warn("no undo information available\n")
235
235
236 def lock(self, wait=1):
236 def lock(self, wait=1):
237 try:
237 try:
238 return lock.lock(self.join("lock"), 0)
238 return lock.lock(self.join("lock"), 0)
239 except lock.LockHeld, inst:
239 except lock.LockHeld, inst:
240 if wait:
240 if wait:
241 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
241 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
242 return lock.lock(self.join("lock"), wait)
242 return lock.lock(self.join("lock"), wait)
243 raise inst
243 raise inst
244
244
245 def rawcommit(self, files, text, user, date, p1=None, p2=None):
245 def rawcommit(self, files, text, user, date, p1=None, p2=None):
246 orig_parent = self.dirstate.parents()[0] or nullid
246 orig_parent = self.dirstate.parents()[0] or nullid
247 p1 = p1 or self.dirstate.parents()[0] or nullid
247 p1 = p1 or self.dirstate.parents()[0] or nullid
248 p2 = p2 or self.dirstate.parents()[1] or nullid
248 p2 = p2 or self.dirstate.parents()[1] or nullid
249 c1 = self.changelog.read(p1)
249 c1 = self.changelog.read(p1)
250 c2 = self.changelog.read(p2)
250 c2 = self.changelog.read(p2)
251 m1 = self.manifest.read(c1[0])
251 m1 = self.manifest.read(c1[0])
252 mf1 = self.manifest.readflags(c1[0])
252 mf1 = self.manifest.readflags(c1[0])
253 m2 = self.manifest.read(c2[0])
253 m2 = self.manifest.read(c2[0])
254 changed = []
254 changed = []
255
255
256 if orig_parent == p1:
256 if orig_parent == p1:
257 update_dirstate = 1
257 update_dirstate = 1
258 else:
258 else:
259 update_dirstate = 0
259 update_dirstate = 0
260
260
261 tr = self.transaction()
261 tr = self.transaction()
262 mm = m1.copy()
262 mm = m1.copy()
263 mfm = mf1.copy()
263 mfm = mf1.copy()
264 linkrev = self.changelog.count()
264 linkrev = self.changelog.count()
265 for f in files:
265 for f in files:
266 try:
266 try:
267 t = self.wread(f)
267 t = self.wread(f)
268 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
268 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
269 r = self.file(f)
269 r = self.file(f)
270 mfm[f] = tm
270 mfm[f] = tm
271
271
272 fp1 = m1.get(f, nullid)
272 fp1 = m1.get(f, nullid)
273 fp2 = m2.get(f, nullid)
273 fp2 = m2.get(f, nullid)
274
274
275 # is the same revision on two branches of a merge?
275 # is the same revision on two branches of a merge?
276 if fp2 == fp1:
276 if fp2 == fp1:
277 fp2 = nullid
277 fp2 = nullid
278
278
279 if fp2 != nullid:
279 if fp2 != nullid:
280 # is one parent an ancestor of the other?
280 # is one parent an ancestor of the other?
281 fpa = r.ancestor(fp1, fp2)
281 fpa = r.ancestor(fp1, fp2)
282 if fpa == fp1:
282 if fpa == fp1:
283 fp1, fp2 = fp2, nullid
283 fp1, fp2 = fp2, nullid
284 elif fpa == fp2:
284 elif fpa == fp2:
285 fp2 = nullid
285 fp2 = nullid
286
286
287 # is the file unmodified from the parent?
287 # is the file unmodified from the parent?
288 if t == r.read(fp1):
288 if t == r.read(fp1):
289 # record the proper existing parent in manifest
289 # record the proper existing parent in manifest
290 # no need to add a revision
290 # no need to add a revision
291 mm[f] = fp1
291 mm[f] = fp1
292 continue
292 continue
293
293
294 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
294 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
295 changed.append(f)
295 changed.append(f)
296 if update_dirstate:
296 if update_dirstate:
297 self.dirstate.update([f], "n")
297 self.dirstate.update([f], "n")
298 except IOError:
298 except IOError:
299 try:
299 try:
300 del mm[f]
300 del mm[f]
301 del mfm[f]
301 del mfm[f]
302 if update_dirstate:
302 if update_dirstate:
303 self.dirstate.forget([f])
303 self.dirstate.forget([f])
304 except:
304 except:
305 # deleted from p2?
305 # deleted from p2?
306 pass
306 pass
307
307
308 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
308 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
309 user = user or self.ui.username()
309 user = user or self.ui.username()
310 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
310 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
311 tr.close()
311 tr.close()
312 if update_dirstate:
312 if update_dirstate:
313 self.dirstate.setparents(n, nullid)
313 self.dirstate.setparents(n, nullid)
314
314
315 def commit(self, files = None, text = "", user = None, date = None,
315 def commit(self, files = None, text = "", user = None, date = None,
316 match = util.always, force=False):
316 match = util.always, force=False):
317 commit = []
317 commit = []
318 remove = []
318 remove = []
319 changed = []
319 changed = []
320
320
321 if files:
321 if files:
322 for f in files:
322 for f in files:
323 s = self.dirstate.state(f)
323 s = self.dirstate.state(f)
324 if s in 'nmai':
324 if s in 'nmai':
325 commit.append(f)
325 commit.append(f)
326 elif s == 'r':
326 elif s == 'r':
327 remove.append(f)
327 remove.append(f)
328 else:
328 else:
329 self.ui.warn("%s not tracked!\n" % f)
329 self.ui.warn("%s not tracked!\n" % f)
330 else:
330 else:
331 (c, a, d, u) = self.changes(match=match)
331 (c, a, d, u) = self.changes(match=match)
332 commit = c + a
332 commit = c + a
333 remove = d
333 remove = d
334
334
335 p1, p2 = self.dirstate.parents()
335 p1, p2 = self.dirstate.parents()
336 c1 = self.changelog.read(p1)
336 c1 = self.changelog.read(p1)
337 c2 = self.changelog.read(p2)
337 c2 = self.changelog.read(p2)
338 m1 = self.manifest.read(c1[0])
338 m1 = self.manifest.read(c1[0])
339 mf1 = self.manifest.readflags(c1[0])
339 mf1 = self.manifest.readflags(c1[0])
340 m2 = self.manifest.read(c2[0])
340 m2 = self.manifest.read(c2[0])
341
341
342 if not commit and not remove and not force and p2 == nullid:
342 if not commit and not remove and not force and p2 == nullid:
343 self.ui.status("nothing changed\n")
343 self.ui.status("nothing changed\n")
344 return None
344 return None
345
345
346 if not self.hook("precommit"):
346 if not self.hook("precommit"):
347 return None
347 return None
348
348
349 lock = self.lock()
349 lock = self.lock()
350 tr = self.transaction()
350 tr = self.transaction()
351
351
352 # check in files
352 # check in files
353 new = {}
353 new = {}
354 linkrev = self.changelog.count()
354 linkrev = self.changelog.count()
355 commit.sort()
355 commit.sort()
356 for f in commit:
356 for f in commit:
357 self.ui.note(f + "\n")
357 self.ui.note(f + "\n")
358 try:
358 try:
359 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
359 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
360 t = self.wread(f)
360 t = self.wread(f)
361 except IOError:
361 except IOError:
362 self.ui.warn("trouble committing %s!\n" % f)
362 self.ui.warn("trouble committing %s!\n" % f)
363 raise
363 raise
364
364
365 r = self.file(f)
365 r = self.file(f)
366
366
367 meta = {}
367 meta = {}
368 cp = self.dirstate.copied(f)
368 cp = self.dirstate.copied(f)
369 if cp:
369 if cp:
370 meta["copy"] = cp
370 meta["copy"] = cp
371 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
371 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
372 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"]))
372 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"]))
373 fp1, fp2 = nullid, nullid
373 fp1, fp2 = nullid, nullid
374 else:
374 else:
375 fp1 = m1.get(f, nullid)
375 fp1 = m1.get(f, nullid)
376 fp2 = m2.get(f, nullid)
376 fp2 = m2.get(f, nullid)
377
377
378 # is the same revision on two branches of a merge?
378 # is the same revision on two branches of a merge?
379 if fp2 == fp1:
379 if fp2 == fp1:
380 fp2 = nullid
380 fp2 = nullid
381
381
382 if fp2 != nullid:
382 if fp2 != nullid:
383 # is one parent an ancestor of the other?
383 # is one parent an ancestor of the other?
384 fpa = r.ancestor(fp1, fp2)
384 fpa = r.ancestor(fp1, fp2)
385 if fpa == fp1:
385 if fpa == fp1:
386 fp1, fp2 = fp2, nullid
386 fp1, fp2 = fp2, nullid
387 elif fpa == fp2:
387 elif fpa == fp2:
388 fp2 = nullid
388 fp2 = nullid
389
389
390 # is the file unmodified from the parent?
390 # is the file unmodified from the parent?
391 if not meta and t == r.read(fp1):
391 if not meta and t == r.read(fp1):
392 # record the proper existing parent in manifest
392 # record the proper existing parent in manifest
393 # no need to add a revision
393 # no need to add a revision
394 new[f] = fp1
394 new[f] = fp1
395 continue
395 continue
396
396
397 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
397 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
398 # remember what we've added so that we can later calculate
398 # remember what we've added so that we can later calculate
399 # the files to pull from a set of changesets
399 # the files to pull from a set of changesets
400 changed.append(f)
400 changed.append(f)
401
401
402 # update manifest
402 # update manifest
403 m1.update(new)
403 m1.update(new)
404 for f in remove:
404 for f in remove:
405 if f in m1:
405 if f in m1:
406 del m1[f]
406 del m1[f]
407 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
407 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
408 (new, remove))
408 (new, remove))
409
409
410 # add changeset
410 # add changeset
411 new = new.keys()
411 new = new.keys()
412 new.sort()
412 new.sort()
413
413
414 if not text:
414 if not text:
415 edittext = ""
415 edittext = ""
416 if p2 != nullid:
416 if p2 != nullid:
417 edittext += "HG: branch merge\n"
417 edittext += "HG: branch merge\n"
418 edittext += "\n" + "HG: manifest hash %s\n" % hex(mn)
418 edittext += "\n" + "HG: manifest hash %s\n" % hex(mn)
419 edittext += "".join(["HG: changed %s\n" % f for f in changed])
419 edittext += "".join(["HG: changed %s\n" % f for f in changed])
420 edittext += "".join(["HG: removed %s\n" % f for f in remove])
420 edittext += "".join(["HG: removed %s\n" % f for f in remove])
421 if not changed and not remove:
421 if not changed and not remove:
422 edittext += "HG: no files changed\n"
422 edittext += "HG: no files changed\n"
423 edittext = self.ui.edit(edittext)
423 edittext = self.ui.edit(edittext)
424 if not edittext.rstrip():
424 if not edittext.rstrip():
425 return None
425 return None
426 text = edittext
426 text = edittext
427
427
428 user = user or self.ui.username()
428 user = user or self.ui.username()
429 n = self.changelog.add(mn, changed, text, tr, p1, p2, user, date)
429 n = self.changelog.add(mn, changed, text, tr, p1, p2, user, date)
430 tr.close()
430 tr.close()
431
431
432 self.dirstate.setparents(n)
432 self.dirstate.setparents(n)
433 self.dirstate.update(new, "n")
433 self.dirstate.update(new, "n")
434 self.dirstate.forget(remove)
434 self.dirstate.forget(remove)
435
435
436 if not self.hook("commit", node=hex(n)):
436 if not self.hook("commit", node=hex(n)):
437 return None
437 return None
438 return n
438 return n
439
439
440 def walk(self, node=None, files=[], match=util.always):
440 def walk(self, node=None, files=[], match=util.always):
441 if node:
441 if node:
442 for fn in self.manifest.read(self.changelog.read(node)[0]):
442 for fn in self.manifest.read(self.changelog.read(node)[0]):
443 if match(fn): yield 'm', fn
443 if match(fn): yield 'm', fn
444 else:
444 else:
445 for src, fn in self.dirstate.walk(files, match):
445 for src, fn in self.dirstate.walk(files, match):
446 yield src, fn
446 yield src, fn
447
447
448 def changes(self, node1 = None, node2 = None, files = [],
448 def changes(self, node1 = None, node2 = None, files = [],
449 match = util.always):
449 match = util.always):
450 mf2, u = None, []
450 mf2, u = None, []
451
451
452 def fcmp(fn, mf):
452 def fcmp(fn, mf):
453 t1 = self.wread(fn)
453 t1 = self.wread(fn)
454 t2 = self.file(fn).read(mf.get(fn, nullid))
454 t2 = self.file(fn).read(mf.get(fn, nullid))
455 return cmp(t1, t2)
455 return cmp(t1, t2)
456
456
457 def mfmatches(node):
457 def mfmatches(node):
458 mf = dict(self.manifest.read(node))
458 mf = dict(self.manifest.read(node))
459 for fn in mf.keys():
459 for fn in mf.keys():
460 if not match(fn):
460 if not match(fn):
461 del mf[fn]
461 del mf[fn]
462 return mf
462 return mf
463
463
464 # are we comparing the working directory?
464 # are we comparing the working directory?
465 if not node2:
465 if not node2:
466 l, c, a, d, u = self.dirstate.changes(files, match)
466 l, c, a, d, u = self.dirstate.changes(files, match)
467
467
468 # are we comparing working dir against its parent?
468 # are we comparing working dir against its parent?
469 if not node1:
469 if not node1:
470 if l:
470 if l:
471 # do a full compare of any files that might have changed
471 # do a full compare of any files that might have changed
472 change = self.changelog.read(self.dirstate.parents()[0])
472 change = self.changelog.read(self.dirstate.parents()[0])
473 mf2 = mfmatches(change[0])
473 mf2 = mfmatches(change[0])
474 for f in l:
474 for f in l:
475 if fcmp(f, mf2):
475 if fcmp(f, mf2):
476 c.append(f)
476 c.append(f)
477
477
478 for l in c, a, d, u:
478 for l in c, a, d, u:
479 l.sort()
479 l.sort()
480
480
481 return (c, a, d, u)
481 return (c, a, d, u)
482
482
483 # are we comparing working dir against non-tip?
483 # are we comparing working dir against non-tip?
484 # generate a pseudo-manifest for the working dir
484 # generate a pseudo-manifest for the working dir
485 if not node2:
485 if not node2:
486 if not mf2:
486 if not mf2:
487 change = self.changelog.read(self.dirstate.parents()[0])
487 change = self.changelog.read(self.dirstate.parents()[0])
488 mf2 = mfmatches(change[0])
488 mf2 = mfmatches(change[0])
489 for f in a + c + l:
489 for f in a + c + l:
490 mf2[f] = ""
490 mf2[f] = ""
491 for f in d:
491 for f in d:
492 if f in mf2: del mf2[f]
492 if f in mf2: del mf2[f]
493 else:
493 else:
494 change = self.changelog.read(node2)
494 change = self.changelog.read(node2)
495 mf2 = mfmatches(change[0])
495 mf2 = mfmatches(change[0])
496
496
497 # flush lists from dirstate before comparing manifests
497 # flush lists from dirstate before comparing manifests
498 c, a = [], []
498 c, a = [], []
499
499
500 change = self.changelog.read(node1)
500 change = self.changelog.read(node1)
501 mf1 = mfmatches(change[0])
501 mf1 = mfmatches(change[0])
502
502
503 for fn in mf2:
503 for fn in mf2:
504 if mf1.has_key(fn):
504 if mf1.has_key(fn):
505 if mf1[fn] != mf2[fn]:
505 if mf1[fn] != mf2[fn]:
506 if mf2[fn] != "" or fcmp(fn, mf1):
506 if mf2[fn] != "" or fcmp(fn, mf1):
507 c.append(fn)
507 c.append(fn)
508 del mf1[fn]
508 del mf1[fn]
509 else:
509 else:
510 a.append(fn)
510 a.append(fn)
511
511
512 d = mf1.keys()
512 d = mf1.keys()
513
513
514 for l in c, a, d, u:
514 for l in c, a, d, u:
515 l.sort()
515 l.sort()
516
516
517 return (c, a, d, u)
517 return (c, a, d, u)
518
518
519 def add(self, list):
519 def add(self, list):
520 for f in list:
520 for f in list:
521 p = self.wjoin(f)
521 p = self.wjoin(f)
522 if not os.path.exists(p):
522 if not os.path.exists(p):
523 self.ui.warn("%s does not exist!\n" % f)
523 self.ui.warn("%s does not exist!\n" % f)
524 elif not os.path.isfile(p):
524 elif not os.path.isfile(p):
525 self.ui.warn("%s not added: only files supported currently\n" % f)
525 self.ui.warn("%s not added: only files supported currently\n" % f)
526 elif self.dirstate.state(f) in 'an':
526 elif self.dirstate.state(f) in 'an':
527 self.ui.warn("%s already tracked!\n" % f)
527 self.ui.warn("%s already tracked!\n" % f)
528 else:
528 else:
529 self.dirstate.update([f], "a")
529 self.dirstate.update([f], "a")
530
530
531 def forget(self, list):
531 def forget(self, list):
532 for f in list:
532 for f in list:
533 if self.dirstate.state(f) not in 'ai':
533 if self.dirstate.state(f) not in 'ai':
534 self.ui.warn("%s not added!\n" % f)
534 self.ui.warn("%s not added!\n" % f)
535 else:
535 else:
536 self.dirstate.forget([f])
536 self.dirstate.forget([f])
537
537
538 def remove(self, list):
538 def remove(self, list):
539 for f in list:
539 for f in list:
540 p = self.wjoin(f)
540 p = self.wjoin(f)
541 if os.path.exists(p):
541 if os.path.exists(p):
542 self.ui.warn("%s still exists!\n" % f)
542 self.ui.warn("%s still exists!\n" % f)
543 elif self.dirstate.state(f) == 'a':
543 elif self.dirstate.state(f) == 'a':
544 self.ui.warn("%s never committed!\n" % f)
544 self.ui.warn("%s never committed!\n" % f)
545 self.dirstate.forget([f])
545 self.dirstate.forget([f])
546 elif f not in self.dirstate:
546 elif f not in self.dirstate:
547 self.ui.warn("%s not tracked!\n" % f)
547 self.ui.warn("%s not tracked!\n" % f)
548 else:
548 else:
549 self.dirstate.update([f], "r")
549 self.dirstate.update([f], "r")
550
550
551 def copy(self, source, dest):
551 def copy(self, source, dest):
552 p = self.wjoin(dest)
552 p = self.wjoin(dest)
553 if not os.path.exists(p):
553 if not os.path.exists(p):
554 self.ui.warn("%s does not exist!\n" % dest)
554 self.ui.warn("%s does not exist!\n" % dest)
555 elif not os.path.isfile(p):
555 elif not os.path.isfile(p):
556 self.ui.warn("copy failed: %s is not a file\n" % dest)
556 self.ui.warn("copy failed: %s is not a file\n" % dest)
557 else:
557 else:
558 if self.dirstate.state(dest) == '?':
558 if self.dirstate.state(dest) == '?':
559 self.dirstate.update([dest], "a")
559 self.dirstate.update([dest], "a")
560 self.dirstate.copy(source, dest)
560 self.dirstate.copy(source, dest)
561
561
562 def heads(self):
562 def heads(self):
563 return self.changelog.heads()
563 return self.changelog.heads()
564
564
565 # branchlookup returns a dict giving a list of branches for
565 # branchlookup returns a dict giving a list of branches for
566 # each head. A branch is defined as the tag of a node or
566 # each head. A branch is defined as the tag of a node or
567 # the branch of the node's parents. If a node has multiple
567 # the branch of the node's parents. If a node has multiple
568 # branch tags, tags are eliminated if they are visible from other
568 # branch tags, tags are eliminated if they are visible from other
569 # branch tags.
569 # branch tags.
570 #
570 #
571 # So, for this graph: a->b->c->d->e
571 # So, for this graph: a->b->c->d->e
572 # \ /
572 # \ /
573 # aa -----/
573 # aa -----/
574 # a has tag 2.6.12
574 # a has tag 2.6.12
575 # d has tag 2.6.13
575 # d has tag 2.6.13
576 # e would have branch tags for 2.6.12 and 2.6.13. Because the node
576 # e would have branch tags for 2.6.12 and 2.6.13. Because the node
577 # for 2.6.12 can be reached from the node 2.6.13, that is eliminated
577 # for 2.6.12 can be reached from the node 2.6.13, that is eliminated
578 # from the list.
578 # from the list.
579 #
579 #
580 # It is possible that more than one head will have the same branch tag.
580 # It is possible that more than one head will have the same branch tag.
581 # callers need to check the result for multiple heads under the same
581 # callers need to check the result for multiple heads under the same
582 # branch tag if that is a problem for them (ie checkout of a specific
582 # branch tag if that is a problem for them (ie checkout of a specific
583 # branch).
583 # branch).
584 #
584 #
585 # passing in a specific branch will limit the depth of the search
585 # passing in a specific branch will limit the depth of the search
586 # through the parents. It won't limit the branches returned in the
586 # through the parents. It won't limit the branches returned in the
587 # result though.
587 # result though.
588 def branchlookup(self, heads=None, branch=None):
588 def branchlookup(self, heads=None, branch=None):
589 if not heads:
589 if not heads:
590 heads = self.heads()
590 heads = self.heads()
591 headt = [ h for h in heads ]
591 headt = [ h for h in heads ]
592 chlog = self.changelog
592 chlog = self.changelog
593 branches = {}
593 branches = {}
594 merges = []
594 merges = []
595 seenmerge = {}
595 seenmerge = {}
596
596
597 # traverse the tree once for each head, recording in the branches
597 # traverse the tree once for each head, recording in the branches
598 # dict which tags are visible from this head. The branches
598 # dict which tags are visible from this head. The branches
599 # dict also records which tags are visible from each tag
599 # dict also records which tags are visible from each tag
600 # while we traverse.
600 # while we traverse.
601 while headt or merges:
601 while headt or merges:
602 if merges:
602 if merges:
603 n, found = merges.pop()
603 n, found = merges.pop()
604 visit = [n]
604 visit = [n]
605 else:
605 else:
606 h = headt.pop()
606 h = headt.pop()
607 visit = [h]
607 visit = [h]
608 found = [h]
608 found = [h]
609 seen = {}
609 seen = {}
610 while visit:
610 while visit:
611 n = visit.pop()
611 n = visit.pop()
612 if n in seen:
612 if n in seen:
613 continue
613 continue
614 pp = chlog.parents(n)
614 pp = chlog.parents(n)
615 tags = self.nodetags(n)
615 tags = self.nodetags(n)
616 if tags:
616 if tags:
617 for x in tags:
617 for x in tags:
618 if x == 'tip':
618 if x == 'tip':
619 continue
619 continue
620 for f in found:
620 for f in found:
621 branches.setdefault(f, {})[n] = 1
621 branches.setdefault(f, {})[n] = 1
622 branches.setdefault(n, {})[n] = 1
622 branches.setdefault(n, {})[n] = 1
623 break
623 break
624 if n not in found:
624 if n not in found:
625 found.append(n)
625 found.append(n)
626 if branch in tags:
626 if branch in tags:
627 continue
627 continue
628 seen[n] = 1
628 seen[n] = 1
629 if pp[1] != nullid and n not in seenmerge:
629 if pp[1] != nullid and n not in seenmerge:
630 merges.append((pp[1], [x for x in found]))
630 merges.append((pp[1], [x for x in found]))
631 seenmerge[n] = 1
631 seenmerge[n] = 1
632 if pp[0] != nullid:
632 if pp[0] != nullid:
633 visit.append(pp[0])
633 visit.append(pp[0])
634 # traverse the branches dict, eliminating branch tags from each
634 # traverse the branches dict, eliminating branch tags from each
635 # head that are visible from another branch tag for that head.
635 # head that are visible from another branch tag for that head.
636 out = {}
636 out = {}
637 viscache = {}
637 viscache = {}
638 for h in heads:
638 for h in heads:
639 def visible(node):
639 def visible(node):
640 if node in viscache:
640 if node in viscache:
641 return viscache[node]
641 return viscache[node]
642 ret = {}
642 ret = {}
643 visit = [node]
643 visit = [node]
644 while visit:
644 while visit:
645 x = visit.pop()
645 x = visit.pop()
646 if x in viscache:
646 if x in viscache:
647 ret.update(viscache[x])
647 ret.update(viscache[x])
648 elif x not in ret:
648 elif x not in ret:
649 ret[x] = 1
649 ret[x] = 1
650 if x in branches:
650 if x in branches:
651 visit[len(visit):] = branches[x].keys()
651 visit[len(visit):] = branches[x].keys()
652 viscache[node] = ret
652 viscache[node] = ret
653 return ret
653 return ret
654 if h not in branches:
654 if h not in branches:
655 continue
655 continue
656 # O(n^2), but somewhat limited. This only searches the
656 # O(n^2), but somewhat limited. This only searches the
657 # tags visible from a specific head, not all the tags in the
657 # tags visible from a specific head, not all the tags in the
658 # whole repo.
658 # whole repo.
659 for b in branches[h]:
659 for b in branches[h]:
660 vis = False
660 vis = False
661 for bb in branches[h].keys():
661 for bb in branches[h].keys():
662 if b != bb:
662 if b != bb:
663 if b in visible(bb):
663 if b in visible(bb):
664 vis = True
664 vis = True
665 break
665 break
666 if not vis:
666 if not vis:
667 l = out.setdefault(h, [])
667 l = out.setdefault(h, [])
668 l[len(l):] = self.nodetags(b)
668 l[len(l):] = self.nodetags(b)
669 return out
669 return out
670
670
671 def branches(self, nodes):
671 def branches(self, nodes):
672 if not nodes: nodes = [self.changelog.tip()]
672 if not nodes: nodes = [self.changelog.tip()]
673 b = []
673 b = []
674 for n in nodes:
674 for n in nodes:
675 t = n
675 t = n
676 while n:
676 while n:
677 p = self.changelog.parents(n)
677 p = self.changelog.parents(n)
678 if p[1] != nullid or p[0] == nullid:
678 if p[1] != nullid or p[0] == nullid:
679 b.append((t, n, p[0], p[1]))
679 b.append((t, n, p[0], p[1]))
680 break
680 break
681 n = p[0]
681 n = p[0]
682 return b
682 return b
683
683
684 def between(self, pairs):
684 def between(self, pairs):
685 r = []
685 r = []
686
686
687 for top, bottom in pairs:
687 for top, bottom in pairs:
688 n, l, i = top, [], 0
688 n, l, i = top, [], 0
689 f = 1
689 f = 1
690
690
691 while n != bottom:
691 while n != bottom:
692 p = self.changelog.parents(n)[0]
692 p = self.changelog.parents(n)[0]
693 if i == f:
693 if i == f:
694 l.append(n)
694 l.append(n)
695 f = f * 2
695 f = f * 2
696 n = p
696 n = p
697 i += 1
697 i += 1
698
698
699 r.append(l)
699 r.append(l)
700
700
701 return r
701 return r
702
702
703 def newer(self, nodes):
703 def newer(self, nodes):
704 m = {}
704 m = {}
705 nl = []
705 nl = []
706 pm = {}
706 pm = {}
707 cl = self.changelog
707 cl = self.changelog
708 t = l = cl.count()
708 t = l = cl.count()
709
709
710 # find the lowest numbered node
710 # find the lowest numbered node
711 for n in nodes:
711 for n in nodes:
712 l = min(l, cl.rev(n))
712 l = min(l, cl.rev(n))
713 m[n] = 1
713 m[n] = 1
714
714
715 for i in xrange(l, t):
715 for i in xrange(l, t):
716 n = cl.node(i)
716 n = cl.node(i)
717 if n in m: # explicitly listed
717 if n in m: # explicitly listed
718 pm[n] = 1
718 pm[n] = 1
719 nl.append(n)
719 nl.append(n)
720 continue
720 continue
721 for p in cl.parents(n):
721 for p in cl.parents(n):
722 if p in pm: # parent listed
722 if p in pm: # parent listed
723 pm[n] = 1
723 pm[n] = 1
724 nl.append(n)
724 nl.append(n)
725 break
725 break
726
726
727 return nl
727 return nl
728
728
729 def findincoming(self, remote, base=None, heads=None):
729 def findincoming(self, remote, base=None, heads=None):
730 m = self.changelog.nodemap
730 m = self.changelog.nodemap
731 search = []
731 search = []
732 fetch = {}
732 fetch = {}
733 seen = {}
733 seen = {}
734 seenbranch = {}
734 seenbranch = {}
735 if base == None:
735 if base == None:
736 base = {}
736 base = {}
737
737
738 # assume we're closer to the tip than the root
738 # assume we're closer to the tip than the root
739 # and start by examining the heads
739 # and start by examining the heads
740 self.ui.status("searching for changes\n")
740 self.ui.status("searching for changes\n")
741
741
742 if not heads:
742 if not heads:
743 heads = remote.heads()
743 heads = remote.heads()
744
744
745 unknown = []
745 unknown = []
746 for h in heads:
746 for h in heads:
747 if h not in m:
747 if h not in m:
748 unknown.append(h)
748 unknown.append(h)
749 else:
749 else:
750 base[h] = 1
750 base[h] = 1
751
751
752 if not unknown:
752 if not unknown:
753 return None
753 return None
754
754
755 rep = {}
755 rep = {}
756 reqcnt = 0
756 reqcnt = 0
757
757
758 # search through remote branches
758 # search through remote branches
759 # a 'branch' here is a linear segment of history, with four parts:
759 # a 'branch' here is a linear segment of history, with four parts:
760 # head, root, first parent, second parent
760 # head, root, first parent, second parent
761 # (a branch always has two parents (or none) by definition)
761 # (a branch always has two parents (or none) by definition)
762 unknown = remote.branches(unknown)
762 unknown = remote.branches(unknown)
763 while unknown:
763 while unknown:
764 r = []
764 r = []
765 while unknown:
765 while unknown:
766 n = unknown.pop(0)
766 n = unknown.pop(0)
767 if n[0] in seen:
767 if n[0] in seen:
768 continue
768 continue
769
769
770 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
770 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
771 if n[0] == nullid:
771 if n[0] == nullid:
772 break
772 break
773 if n in seenbranch:
773 if n in seenbranch:
774 self.ui.debug("branch already found\n")
774 self.ui.debug("branch already found\n")
775 continue
775 continue
776 if n[1] and n[1] in m: # do we know the base?
776 if n[1] and n[1] in m: # do we know the base?
777 self.ui.debug("found incomplete branch %s:%s\n"
777 self.ui.debug("found incomplete branch %s:%s\n"
778 % (short(n[0]), short(n[1])))
778 % (short(n[0]), short(n[1])))
779 search.append(n) # schedule branch range for scanning
779 search.append(n) # schedule branch range for scanning
780 seenbranch[n] = 1
780 seenbranch[n] = 1
781 else:
781 else:
782 if n[1] not in seen and n[1] not in fetch:
782 if n[1] not in seen and n[1] not in fetch:
783 if n[2] in m and n[3] in m:
783 if n[2] in m and n[3] in m:
784 self.ui.debug("found new changeset %s\n" %
784 self.ui.debug("found new changeset %s\n" %
785 short(n[1]))
785 short(n[1]))
786 fetch[n[1]] = 1 # earliest unknown
786 fetch[n[1]] = 1 # earliest unknown
787 base[n[2]] = 1 # latest known
787 base[n[2]] = 1 # latest known
788 continue
788 continue
789
789
790 for a in n[2:4]:
790 for a in n[2:4]:
791 if a not in rep:
791 if a not in rep:
792 r.append(a)
792 r.append(a)
793 rep[a] = 1
793 rep[a] = 1
794
794
795 seen[n[0]] = 1
795 seen[n[0]] = 1
796
796
797 if r:
797 if r:
798 reqcnt += 1
798 reqcnt += 1
799 self.ui.debug("request %d: %s\n" %
799 self.ui.debug("request %d: %s\n" %
800 (reqcnt, " ".join(map(short, r))))
800 (reqcnt, " ".join(map(short, r))))
801 for p in range(0, len(r), 10):
801 for p in range(0, len(r), 10):
802 for b in remote.branches(r[p:p+10]):
802 for b in remote.branches(r[p:p+10]):
803 self.ui.debug("received %s:%s\n" %
803 self.ui.debug("received %s:%s\n" %
804 (short(b[0]), short(b[1])))
804 (short(b[0]), short(b[1])))
805 if b[0] in m:
805 if b[0] in m:
806 self.ui.debug("found base node %s\n" % short(b[0]))
806 self.ui.debug("found base node %s\n" % short(b[0]))
807 base[b[0]] = 1
807 base[b[0]] = 1
808 elif b[0] not in seen:
808 elif b[0] not in seen:
809 unknown.append(b)
809 unknown.append(b)
810
810
811 # do binary search on the branches we found
811 # do binary search on the branches we found
812 while search:
812 while search:
813 n = search.pop(0)
813 n = search.pop(0)
814 reqcnt += 1
814 reqcnt += 1
815 l = remote.between([(n[0], n[1])])[0]
815 l = remote.between([(n[0], n[1])])[0]
816 l.append(n[1])
816 l.append(n[1])
817 p = n[0]
817 p = n[0]
818 f = 1
818 f = 1
819 for i in l:
819 for i in l:
820 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
820 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
821 if i in m:
821 if i in m:
822 if f <= 2:
822 if f <= 2:
823 self.ui.debug("found new branch changeset %s\n" %
823 self.ui.debug("found new branch changeset %s\n" %
824 short(p))
824 short(p))
825 fetch[p] = 1
825 fetch[p] = 1
826 base[i] = 1
826 base[i] = 1
827 else:
827 else:
828 self.ui.debug("narrowed branch search to %s:%s\n"
828 self.ui.debug("narrowed branch search to %s:%s\n"
829 % (short(p), short(i)))
829 % (short(p), short(i)))
830 search.append((p, i))
830 search.append((p, i))
831 break
831 break
832 p, f = i, f * 2
832 p, f = i, f * 2
833
833
834 # sanity check our fetch list
834 # sanity check our fetch list
835 for f in fetch.keys():
835 for f in fetch.keys():
836 if f in m:
836 if f in m:
837 raise repo.RepoError("already have changeset " + short(f[:4]))
837 raise repo.RepoError("already have changeset " + short(f[:4]))
838
838
839 if base.keys() == [nullid]:
839 if base.keys() == [nullid]:
840 self.ui.warn("warning: pulling from an unrelated repository!\n")
840 self.ui.warn("warning: pulling from an unrelated repository!\n")
841
841
842 self.ui.note("found new changesets starting at " +
842 self.ui.note("found new changesets starting at " +
843 " ".join([short(f) for f in fetch]) + "\n")
843 " ".join([short(f) for f in fetch]) + "\n")
844
844
845 self.ui.debug("%d total queries\n" % reqcnt)
845 self.ui.debug("%d total queries\n" % reqcnt)
846
846
847 return fetch.keys()
847 return fetch.keys()
848
848
849 def findoutgoing(self, remote, base=None, heads=None):
849 def findoutgoing(self, remote, base=None, heads=None):
850 if base == None:
850 if base == None:
851 base = {}
851 base = {}
852 self.findincoming(remote, base, heads)
852 self.findincoming(remote, base, heads)
853
853
854 self.ui.debug("common changesets up to "
854 self.ui.debug("common changesets up to "
855 + " ".join(map(short, base.keys())) + "\n")
855 + " ".join(map(short, base.keys())) + "\n")
856
856
857 remain = dict.fromkeys(self.changelog.nodemap)
857 remain = dict.fromkeys(self.changelog.nodemap)
858
858
859 # prune everything remote has from the tree
859 # prune everything remote has from the tree
860 del remain[nullid]
860 del remain[nullid]
861 remove = base.keys()
861 remove = base.keys()
862 while remove:
862 while remove:
863 n = remove.pop(0)
863 n = remove.pop(0)
864 if n in remain:
864 if n in remain:
865 del remain[n]
865 del remain[n]
866 for p in self.changelog.parents(n):
866 for p in self.changelog.parents(n):
867 remove.append(p)
867 remove.append(p)
868
868
869 # find every node whose parents have been pruned
869 # find every node whose parents have been pruned
870 subset = []
870 subset = []
871 for n in remain:
871 for n in remain:
872 p1, p2 = self.changelog.parents(n)
872 p1, p2 = self.changelog.parents(n)
873 if p1 not in remain and p2 not in remain:
873 if p1 not in remain and p2 not in remain:
874 subset.append(n)
874 subset.append(n)
875
875
876 # this is the set of all roots we have to push
876 # this is the set of all roots we have to push
877 return subset
877 return subset
878
878
879 def pull(self, remote):
879 def pull(self, remote):
880 lock = self.lock()
880 lock = self.lock()
881
881
882 # if we have an empty repo, fetch everything
882 # if we have an empty repo, fetch everything
883 if self.changelog.tip() == nullid:
883 if self.changelog.tip() == nullid:
884 self.ui.status("requesting all changes\n")
884 self.ui.status("requesting all changes\n")
885 fetch = [nullid]
885 fetch = [nullid]
886 else:
886 else:
887 fetch = self.findincoming(remote)
887 fetch = self.findincoming(remote)
888
888
889 if not fetch:
889 if not fetch:
890 self.ui.status("no changes found\n")
890 self.ui.status("no changes found\n")
891 return 1
891 return 1
892
892
893 cg = remote.changegroup(fetch)
893 cg = remote.changegroup(fetch)
894 return self.addchangegroup(cg)
894 return self.addchangegroup(cg)
895
895
896 def push(self, remote, force=False):
896 def push(self, remote, force=False):
897 lock = remote.lock()
897 lock = remote.lock()
898
898
899 base = {}
899 base = {}
900 heads = remote.heads()
900 heads = remote.heads()
901 inc = self.findincoming(remote, base, heads)
901 inc = self.findincoming(remote, base, heads)
902 if not force and inc:
902 if not force and inc:
903 self.ui.warn("abort: unsynced remote changes!\n")
903 self.ui.warn("abort: unsynced remote changes!\n")
904 self.ui.status("(did you forget to sync? use push -f to force)\n")
904 self.ui.status("(did you forget to sync? use push -f to force)\n")
905 return 1
905 return 1
906
906
907 update = self.findoutgoing(remote, base)
907 update = self.findoutgoing(remote, base)
908 if not update:
908 if not update:
909 self.ui.status("no changes found\n")
909 self.ui.status("no changes found\n")
910 return 1
910 return 1
911 elif not force:
911 elif not force:
912 if len(heads) < len(self.changelog.heads()):
912 if len(heads) < len(self.changelog.heads()):
913 self.ui.warn("abort: push creates new remote branches!\n")
913 self.ui.warn("abort: push creates new remote branches!\n")
914 self.ui.status("(did you forget to merge?" +
914 self.ui.status("(did you forget to merge?" +
915 " use push -f to force)\n")
915 " use push -f to force)\n")
916 return 1
916 return 1
917
917
918 cg = self.changegroup(update)
918 cg = self.changegroup(update)
919 return remote.addchangegroup(cg)
919 return remote.addchangegroup(cg)
920
920
921 def changegroup(self, basenodes):
921 def changegroup(self, basenodes):
922 genread = util.chunkbuffer
922 genread = util.chunkbuffer
923
923
924 def gengroup():
924 def gengroup():
925 nodes = self.newer(basenodes)
925 nodes = self.newer(basenodes)
926
926
927 # construct the link map
927 # construct the link map
928 linkmap = {}
928 linkmap = {}
929 for n in nodes:
929 for n in nodes:
930 linkmap[self.changelog.rev(n)] = n
930 linkmap[self.changelog.rev(n)] = n
931
931
932 # construct a list of all changed files
932 # construct a list of all changed files
933 changed = {}
933 changed = {}
934 for n in nodes:
934 for n in nodes:
935 c = self.changelog.read(n)
935 c = self.changelog.read(n)
936 for f in c[3]:
936 for f in c[3]:
937 changed[f] = 1
937 changed[f] = 1
938 changed = changed.keys()
938 changed = changed.keys()
939 changed.sort()
939 changed.sort()
940
940
941 # the changegroup is changesets + manifests + all file revs
941 # the changegroup is changesets + manifests + all file revs
942 revs = [ self.changelog.rev(n) for n in nodes ]
942 revs = [ self.changelog.rev(n) for n in nodes ]
943
943
944 for y in self.changelog.group(linkmap): yield y
944 for y in self.changelog.group(linkmap): yield y
945 for y in self.manifest.group(linkmap): yield y
945 for y in self.manifest.group(linkmap): yield y
946 for f in changed:
946 for f in changed:
947 yield struct.pack(">l", len(f) + 4) + f
947 yield struct.pack(">l", len(f) + 4) + f
948 g = self.file(f).group(linkmap)
948 g = self.file(f).group(linkmap)
949 for y in g:
949 for y in g:
950 yield y
950 yield y
951
951
952 yield struct.pack(">l", 0)
952 yield struct.pack(">l", 0)
953
953
954 return genread(gengroup())
954 return genread(gengroup())
955
955
956 def addchangegroup(self, source):
956 def addchangegroup(self, source):
957
957
958 def getchunk():
958 def getchunk():
959 d = source.read(4)
959 d = source.read(4)
960 if not d: return ""
960 if not d: return ""
961 l = struct.unpack(">l", d)[0]
961 l = struct.unpack(">l", d)[0]
962 if l <= 4: return ""
962 if l <= 4: return ""
963 d = source.read(l - 4)
963 d = source.read(l - 4)
964 if len(d) < l - 4:
964 if len(d) < l - 4:
965 raise repo.RepoError("premature EOF reading chunk" +
965 raise repo.RepoError("premature EOF reading chunk" +
966 " (got %d bytes, expected %d)"
966 " (got %d bytes, expected %d)"
967 % (len(d), l - 4))
967 % (len(d), l - 4))
968 return d
968 return d
969
969
970 def getgroup():
970 def getgroup():
971 while 1:
971 while 1:
972 c = getchunk()
972 c = getchunk()
973 if not c: break
973 if not c: break
974 yield c
974 yield c
975
975
976 def csmap(x):
976 def csmap(x):
977 self.ui.debug("add changeset %s\n" % short(x))
977 self.ui.debug("add changeset %s\n" % short(x))
978 return self.changelog.count()
978 return self.changelog.count()
979
979
980 def revmap(x):
980 def revmap(x):
981 return self.changelog.rev(x)
981 return self.changelog.rev(x)
982
982
983 if not source: return
983 if not source: return
984 changesets = files = revisions = 0
984 changesets = files = revisions = 0
985
985
986 tr = self.transaction()
986 tr = self.transaction()
987
987
988 oldheads = len(self.changelog.heads())
988 oldheads = len(self.changelog.heads())
989
989
990 # pull off the changeset group
990 # pull off the changeset group
991 self.ui.status("adding changesets\n")
991 self.ui.status("adding changesets\n")
992 co = self.changelog.tip()
992 co = self.changelog.tip()
993 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
993 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
994 cnr, cor = map(self.changelog.rev, (cn, co))
994 cnr, cor = map(self.changelog.rev, (cn, co))
995 if cn == nullid:
995 if cn == nullid:
996 cnr = cor
996 cnr = cor
997 changesets = cnr - cor
997 changesets = cnr - cor
998
998
999 # pull off the manifest group
999 # pull off the manifest group
1000 self.ui.status("adding manifests\n")
1000 self.ui.status("adding manifests\n")
1001 mm = self.manifest.tip()
1001 mm = self.manifest.tip()
1002 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1002 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1003
1003
1004 # process the files
1004 # process the files
1005 self.ui.status("adding file changes\n")
1005 self.ui.status("adding file changes\n")
1006 while 1:
1006 while 1:
1007 f = getchunk()
1007 f = getchunk()
1008 if not f: break
1008 if not f: break
1009 self.ui.debug("adding %s revisions\n" % f)
1009 self.ui.debug("adding %s revisions\n" % f)
1010 fl = self.file(f)
1010 fl = self.file(f)
1011 o = fl.count()
1011 o = fl.count()
1012 n = fl.addgroup(getgroup(), revmap, tr)
1012 n = fl.addgroup(getgroup(), revmap, tr)
1013 revisions += fl.count() - o
1013 revisions += fl.count() - o
1014 files += 1
1014 files += 1
1015
1015
1016 newheads = len(self.changelog.heads())
1016 newheads = len(self.changelog.heads())
1017 heads = ""
1017 heads = ""
1018 if oldheads and newheads > oldheads:
1018 if oldheads and newheads > oldheads:
1019 heads = " (+%d heads)" % (newheads - oldheads)
1019 heads = " (+%d heads)" % (newheads - oldheads)
1020
1020
1021 self.ui.status(("added %d changesets" +
1021 self.ui.status(("added %d changesets" +
1022 " with %d changes to %d files%s\n")
1022 " with %d changes to %d files%s\n")
1023 % (changesets, revisions, files, heads))
1023 % (changesets, revisions, files, heads))
1024
1024
1025 tr.close()
1025 tr.close()
1026
1026
1027 if changesets > 0:
1027 if changesets > 0:
1028 if not self.hook("changegroup",
1028 if not self.hook("changegroup",
1029 node=hex(self.changelog.node(cor+1))):
1029 node=hex(self.changelog.node(cor+1))):
1030 self.ui.warn("abort: changegroup hook returned failure!\n")
1030 self.ui.warn("abort: changegroup hook returned failure!\n")
1031 return 1
1031 return 1
1032
1032
1033 for i in range(cor + 1, cnr + 1):
1033 for i in range(cor + 1, cnr + 1):
1034 self.hook("commit", node=hex(self.changelog.node(i)))
1034 self.hook("commit", node=hex(self.changelog.node(i)))
1035
1035
1036 return
1036 return
1037
1037
1038 def update(self, node, allow=False, force=False, choose=None,
1038 def update(self, node, allow=False, force=False, choose=None,
1039 moddirstate=True):
1039 moddirstate=True):
1040 pl = self.dirstate.parents()
1040 pl = self.dirstate.parents()
1041 if not force and pl[1] != nullid:
1041 if not force and pl[1] != nullid:
1042 self.ui.warn("aborting: outstanding uncommitted merges\n")
1042 self.ui.warn("aborting: outstanding uncommitted merges\n")
1043 return 1
1043 return 1
1044
1044
1045 p1, p2 = pl[0], node
1045 p1, p2 = pl[0], node
1046 pa = self.changelog.ancestor(p1, p2)
1046 pa = self.changelog.ancestor(p1, p2)
1047 m1n = self.changelog.read(p1)[0]
1047 m1n = self.changelog.read(p1)[0]
1048 m2n = self.changelog.read(p2)[0]
1048 m2n = self.changelog.read(p2)[0]
1049 man = self.manifest.ancestor(m1n, m2n)
1049 man = self.manifest.ancestor(m1n, m2n)
1050 m1 = self.manifest.read(m1n)
1050 m1 = self.manifest.read(m1n)
1051 mf1 = self.manifest.readflags(m1n)
1051 mf1 = self.manifest.readflags(m1n)
1052 m2 = self.manifest.read(m2n)
1052 m2 = self.manifest.read(m2n)
1053 mf2 = self.manifest.readflags(m2n)
1053 mf2 = self.manifest.readflags(m2n)
1054 ma = self.manifest.read(man)
1054 ma = self.manifest.read(man)
1055 mfa = self.manifest.readflags(man)
1055 mfa = self.manifest.readflags(man)
1056
1056
1057 (c, a, d, u) = self.changes()
1057 (c, a, d, u) = self.changes()
1058
1058
1059 # is this a jump, or a merge? i.e. is there a linear path
1059 # is this a jump, or a merge? i.e. is there a linear path
1060 # from p1 to p2?
1060 # from p1 to p2?
1061 linear_path = (pa == p1 or pa == p2)
1061 linear_path = (pa == p1 or pa == p2)
1062
1062
1063 # resolve the manifest to determine which files
1063 # resolve the manifest to determine which files
1064 # we care about merging
1064 # we care about merging
1065 self.ui.note("resolving manifests\n")
1065 self.ui.note("resolving manifests\n")
1066 self.ui.debug(" force %s allow %s moddirstate %s linear %s\n" %
1066 self.ui.debug(" force %s allow %s moddirstate %s linear %s\n" %
1067 (force, allow, moddirstate, linear_path))
1067 (force, allow, moddirstate, linear_path))
1068 self.ui.debug(" ancestor %s local %s remote %s\n" %
1068 self.ui.debug(" ancestor %s local %s remote %s\n" %
1069 (short(man), short(m1n), short(m2n)))
1069 (short(man), short(m1n), short(m2n)))
1070
1070
1071 merge = {}
1071 merge = {}
1072 get = {}
1072 get = {}
1073 remove = []
1073 remove = []
1074
1074
1075 # construct a working dir manifest
1075 # construct a working dir manifest
1076 mw = m1.copy()
1076 mw = m1.copy()
1077 mfw = mf1.copy()
1077 mfw = mf1.copy()
1078 umap = dict.fromkeys(u)
1078 umap = dict.fromkeys(u)
1079
1079
1080 for f in a + c + u:
1080 for f in a + c + u:
1081 mw[f] = ""
1081 mw[f] = ""
1082 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1082 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1083
1083
1084 for f in d:
1084 for f in d:
1085 if f in mw: del mw[f]
1085 if f in mw: del mw[f]
1086
1086
1087 # If we're jumping between revisions (as opposed to merging),
1087 # If we're jumping between revisions (as opposed to merging),
1088 # and if neither the working directory nor the target rev has
1088 # and if neither the working directory nor the target rev has
1089 # the file, then we need to remove it from the dirstate, to
1089 # the file, then we need to remove it from the dirstate, to
1090 # prevent the dirstate from listing the file when it is no
1090 # prevent the dirstate from listing the file when it is no
1091 # longer in the manifest.
1091 # longer in the manifest.
1092 if moddirstate and linear_path and f not in m2:
1092 if moddirstate and linear_path and f not in m2:
1093 self.dirstate.forget((f,))
1093 self.dirstate.forget((f,))
1094
1094
1095 # Compare manifests
1095 # Compare manifests
1096 for f, n in mw.iteritems():
1096 for f, n in mw.iteritems():
1097 if choose and not choose(f): continue
1097 if choose and not choose(f): continue
1098 if f in m2:
1098 if f in m2:
1099 s = 0
1099 s = 0
1100
1100
1101 # is the wfile new since m1, and match m2?
1101 # is the wfile new since m1, and match m2?
1102 if f not in m1:
1102 if f not in m1:
1103 t1 = self.wread(f)
1103 t1 = self.wread(f)
1104 t2 = self.file(f).read(m2[f])
1104 t2 = self.file(f).read(m2[f])
1105 if cmp(t1, t2) == 0:
1105 if cmp(t1, t2) == 0:
1106 n = m2[f]
1106 n = m2[f]
1107 del t1, t2
1107 del t1, t2
1108
1108
1109 # are files different?
1109 # are files different?
1110 if n != m2[f]:
1110 if n != m2[f]:
1111 a = ma.get(f, nullid)
1111 a = ma.get(f, nullid)
1112 # are both different from the ancestor?
1112 # are both different from the ancestor?
1113 if n != a and m2[f] != a:
1113 if n != a and m2[f] != a:
1114 self.ui.debug(" %s versions differ, resolve\n" % f)
1114 self.ui.debug(" %s versions differ, resolve\n" % f)
1115 # merge executable bits
1115 # merge executable bits
1116 # "if we changed or they changed, change in merge"
1116 # "if we changed or they changed, change in merge"
1117 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1117 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1118 mode = ((a^b) | (a^c)) ^ a
1118 mode = ((a^b) | (a^c)) ^ a
1119 merge[f] = (m1.get(f, nullid), m2[f], mode)
1119 merge[f] = (m1.get(f, nullid), m2[f], mode)
1120 s = 1
1120 s = 1
1121 # are we clobbering?
1121 # are we clobbering?
1122 # is remote's version newer?
1122 # is remote's version newer?
1123 # or are we going back in time?
1123 # or are we going back in time?
1124 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1124 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1125 self.ui.debug(" remote %s is newer, get\n" % f)
1125 self.ui.debug(" remote %s is newer, get\n" % f)
1126 get[f] = m2[f]
1126 get[f] = m2[f]
1127 s = 1
1127 s = 1
1128 elif f in umap:
1128 elif f in umap:
1129 # this unknown file is the same as the checkout
1129 # this unknown file is the same as the checkout
1130 get[f] = m2[f]
1130 get[f] = m2[f]
1131
1131
1132 if not s and mfw[f] != mf2[f]:
1132 if not s and mfw[f] != mf2[f]:
1133 if force:
1133 if force:
1134 self.ui.debug(" updating permissions for %s\n" % f)
1134 self.ui.debug(" updating permissions for %s\n" % f)
1135 util.set_exec(self.wjoin(f), mf2[f])
1135 util.set_exec(self.wjoin(f), mf2[f])
1136 else:
1136 else:
1137 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1137 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1138 mode = ((a^b) | (a^c)) ^ a
1138 mode = ((a^b) | (a^c)) ^ a
1139 if mode != b:
1139 if mode != b:
1140 self.ui.debug(" updating permissions for %s\n" % f)
1140 self.ui.debug(" updating permissions for %s\n" % f)
1141 util.set_exec(self.wjoin(f), mode)
1141 util.set_exec(self.wjoin(f), mode)
1142 del m2[f]
1142 del m2[f]
1143 elif f in ma:
1143 elif f in ma:
1144 if n != ma[f]:
1144 if n != ma[f]:
1145 r = "d"
1145 r = "d"
1146 if not force and (linear_path or allow):
1146 if not force and (linear_path or allow):
1147 r = self.ui.prompt(
1147 r = self.ui.prompt(
1148 (" local changed %s which remote deleted\n" % f) +
1148 (" local changed %s which remote deleted\n" % f) +
1149 "(k)eep or (d)elete?", "[kd]", "k")
1149 "(k)eep or (d)elete?", "[kd]", "k")
1150 if r == "d":
1150 if r == "d":
1151 remove.append(f)
1151 remove.append(f)
1152 else:
1152 else:
1153 self.ui.debug("other deleted %s\n" % f)
1153 self.ui.debug("other deleted %s\n" % f)
1154 remove.append(f) # other deleted it
1154 remove.append(f) # other deleted it
1155 else:
1155 else:
1156 # file is created on branch or in working directory
1156 # file is created on branch or in working directory
1157 if force and f not in umap:
1157 if force and f not in umap:
1158 self.ui.debug("remote deleted %s, clobbering\n" % f)
1158 self.ui.debug("remote deleted %s, clobbering\n" % f)
1159 remove.append(f)
1159 remove.append(f)
1160 elif n == m1.get(f, nullid): # same as parent
1160 elif n == m1.get(f, nullid): # same as parent
1161 if p2 == pa: # going backwards?
1161 if p2 == pa: # going backwards?
1162 self.ui.debug("remote deleted %s\n" % f)
1162 self.ui.debug("remote deleted %s\n" % f)
1163 remove.append(f)
1163 remove.append(f)
1164 else:
1164 else:
1165 self.ui.debug("local modified %s, keeping\n" % f)
1165 self.ui.debug("local modified %s, keeping\n" % f)
1166 else:
1166 else:
1167 self.ui.debug("working dir created %s, keeping\n" % f)
1167 self.ui.debug("working dir created %s, keeping\n" % f)
1168
1168
1169 for f, n in m2.iteritems():
1169 for f, n in m2.iteritems():
1170 if choose and not choose(f): continue
1170 if choose and not choose(f): continue
1171 if f[0] == "/": continue
1171 if f[0] == "/": continue
1172 if f in ma and n != ma[f]:
1172 if f in ma and n != ma[f]:
1173 r = "k"
1173 r = "k"
1174 if not force and (linear_path or allow):
1174 if not force and (linear_path or allow):
1175 r = self.ui.prompt(
1175 r = self.ui.prompt(
1176 ("remote changed %s which local deleted\n" % f) +
1176 ("remote changed %s which local deleted\n" % f) +
1177 "(k)eep or (d)elete?", "[kd]", "k")
1177 "(k)eep or (d)elete?", "[kd]", "k")
1178 if r == "k": get[f] = n
1178 if r == "k": get[f] = n
1179 elif f not in ma:
1179 elif f not in ma:
1180 self.ui.debug("remote created %s\n" % f)
1180 self.ui.debug("remote created %s\n" % f)
1181 get[f] = n
1181 get[f] = n
1182 else:
1182 else:
1183 if force or p2 == pa: # going backwards?
1183 if force or p2 == pa: # going backwards?
1184 self.ui.debug("local deleted %s, recreating\n" % f)
1184 self.ui.debug("local deleted %s, recreating\n" % f)
1185 get[f] = n
1185 get[f] = n
1186 else:
1186 else:
1187 self.ui.debug("local deleted %s\n" % f)
1187 self.ui.debug("local deleted %s\n" % f)
1188
1188
1189 del mw, m1, m2, ma
1189 del mw, m1, m2, ma
1190
1190
1191 if force:
1191 if force:
1192 for f in merge:
1192 for f in merge:
1193 get[f] = merge[f][1]
1193 get[f] = merge[f][1]
1194 merge = {}
1194 merge = {}
1195
1195
1196 if linear_path or force:
1196 if linear_path or force:
1197 # we don't need to do any magic, just jump to the new rev
1197 # we don't need to do any magic, just jump to the new rev
1198 branch_merge = False
1198 branch_merge = False
1199 p1, p2 = p2, nullid
1199 p1, p2 = p2, nullid
1200 else:
1200 else:
1201 if not allow:
1201 if not allow:
1202 self.ui.status("this update spans a branch" +
1202 self.ui.status("this update spans a branch" +
1203 " affecting the following files:\n")
1203 " affecting the following files:\n")
1204 fl = merge.keys() + get.keys()
1204 fl = merge.keys() + get.keys()
1205 fl.sort()
1205 fl.sort()
1206 for f in fl:
1206 for f in fl:
1207 cf = ""
1207 cf = ""
1208 if f in merge: cf = " (resolve)"
1208 if f in merge: cf = " (resolve)"
1209 self.ui.status(" %s%s\n" % (f, cf))
1209 self.ui.status(" %s%s\n" % (f, cf))
1210 self.ui.warn("aborting update spanning branches!\n")
1210 self.ui.warn("aborting update spanning branches!\n")
1211 self.ui.status("(use update -m to merge across branches" +
1211 self.ui.status("(use update -m to merge across branches" +
1212 " or -C to lose changes)\n")
1212 " or -C to lose changes)\n")
1213 return 1
1213 return 1
1214 branch_merge = True
1214 branch_merge = True
1215
1215
1216 if moddirstate:
1216 if moddirstate:
1217 self.dirstate.setparents(p1, p2)
1217 self.dirstate.setparents(p1, p2)
1218
1218
1219 # get the files we don't need to change
1219 # get the files we don't need to change
1220 files = get.keys()
1220 files = get.keys()
1221 files.sort()
1221 files.sort()
1222 for f in files:
1222 for f in files:
1223 if f[0] == "/": continue
1223 if f[0] == "/": continue
1224 self.ui.note("getting %s\n" % f)
1224 self.ui.note("getting %s\n" % f)
1225 t = self.file(f).read(get[f])
1225 t = self.file(f).read(get[f])
1226 try:
1226 try:
1227 self.wwrite(f, t)
1227 self.wwrite(f, t)
1228 except IOError, e:
1228 except IOError, e:
1229 if e.errno != errno.ENOENT:
1229 if e.errno != errno.ENOENT:
1230 raise
1230 raise
1231 os.makedirs(os.path.dirname(self.wjoin(f)))
1231 os.makedirs(os.path.dirname(self.wjoin(f)))
1232 self.wwrite(f, t)
1232 self.wwrite(f, t)
1233 util.set_exec(self.wjoin(f), mf2[f])
1233 util.set_exec(self.wjoin(f), mf2[f])
1234 if moddirstate:
1234 if moddirstate:
1235 if branch_merge:
1235 if branch_merge:
1236 self.dirstate.update([f], 'n', st_mtime=-1)
1236 self.dirstate.update([f], 'n', st_mtime=-1)
1237 else:
1237 else:
1238 self.dirstate.update([f], 'n')
1238 self.dirstate.update([f], 'n')
1239
1239
1240 # merge the tricky bits
1240 # merge the tricky bits
1241 files = merge.keys()
1241 files = merge.keys()
1242 files.sort()
1242 files.sort()
1243 for f in files:
1243 for f in files:
1244 self.ui.status("merging %s\n" % f)
1244 self.ui.status("merging %s\n" % f)
1245 my, other, flag = merge[f]
1245 my, other, flag = merge[f]
1246 self.merge3(f, my, other)
1246 self.merge3(f, my, other)
1247 util.set_exec(self.wjoin(f), flag)
1247 util.set_exec(self.wjoin(f), flag)
1248 if moddirstate:
1248 if moddirstate:
1249 if branch_merge:
1249 if branch_merge:
1250 # We've done a branch merge, mark this file as merged
1250 # We've done a branch merge, mark this file as merged
1251 # so that we properly record the merger later
1251 # so that we properly record the merger later
1252 self.dirstate.update([f], 'm')
1252 self.dirstate.update([f], 'm')
1253 else:
1253 else:
1254 # We've update-merged a locally modified file, so
1254 # We've update-merged a locally modified file, so
1255 # we set the dirstate to emulate a normal checkout
1255 # we set the dirstate to emulate a normal checkout
1256 # of that file some time in the past. Thus our
1256 # of that file some time in the past. Thus our
1257 # merge will appear as a normal local file
1257 # merge will appear as a normal local file
1258 # modification.
1258 # modification.
1259 f_len = len(self.file(f).read(other))
1259 f_len = len(self.file(f).read(other))
1260 self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
1260 self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
1261
1261
1262 remove.sort()
1262 remove.sort()
1263 for f in remove:
1263 for f in remove:
1264 self.ui.note("removing %s\n" % f)
1264 self.ui.note("removing %s\n" % f)
1265 try:
1265 try:
1266 os.unlink(self.wjoin(f))
1266 os.unlink(self.wjoin(f))
1267 except OSError, inst:
1267 except OSError, inst:
1268 self.ui.warn("update failed to remove %s: %s!\n" % (f, inst))
1268 self.ui.warn("update failed to remove %s: %s!\n" % (f, inst))
1269 # try removing directories that might now be empty
1269 # try removing directories that might now be empty
1270 try: os.removedirs(os.path.dirname(self.wjoin(f)))
1270 try: os.removedirs(os.path.dirname(self.wjoin(f)))
1271 except: pass
1271 except: pass
1272 if moddirstate:
1272 if moddirstate:
1273 if branch_merge:
1273 if branch_merge:
1274 self.dirstate.update(remove, 'r')
1274 self.dirstate.update(remove, 'r')
1275 else:
1275 else:
1276 self.dirstate.forget(remove)
1276 self.dirstate.forget(remove)
1277
1277
1278 def merge3(self, fn, my, other):
1278 def merge3(self, fn, my, other):
1279 """perform a 3-way merge in the working directory"""
1279 """perform a 3-way merge in the working directory"""
1280
1280
1281 def temp(prefix, node):
1281 def temp(prefix, node):
1282 pre = "%s~%s." % (os.path.basename(fn), prefix)
1282 pre = "%s~%s." % (os.path.basename(fn), prefix)
1283 (fd, name) = tempfile.mkstemp("", pre)
1283 (fd, name) = tempfile.mkstemp("", pre)
1284 f = os.fdopen(fd, "wb")
1284 f = os.fdopen(fd, "wb")
1285 self.wwrite(fn, fl.read(node), f)
1285 self.wwrite(fn, fl.read(node), f)
1286 f.close()
1286 f.close()
1287 return name
1287 return name
1288
1288
1289 fl = self.file(fn)
1289 fl = self.file(fn)
1290 base = fl.ancestor(my, other)
1290 base = fl.ancestor(my, other)
1291 a = self.wjoin(fn)
1291 a = self.wjoin(fn)
1292 b = temp("base", base)
1292 b = temp("base", base)
1293 c = temp("other", other)
1293 c = temp("other", other)
1294
1294
1295 self.ui.note("resolving %s\n" % fn)
1295 self.ui.note("resolving %s\n" % fn)
1296 self.ui.debug("file %s: my %s other %s ancestor %s\n" %
1296 self.ui.debug("file %s: my %s other %s ancestor %s\n" %
1297 (fn, short(my), short(other), short(base)))
1297 (fn, short(my), short(other), short(base)))
1298
1298
1299 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1299 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1300 or "hgmerge")
1300 or "hgmerge")
1301 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1301 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1302 if r:
1302 if r:
1303 self.ui.warn("merging %s failed!\n" % fn)
1303 self.ui.warn("merging %s failed!\n" % fn)
1304
1304
1305 os.unlink(b)
1305 os.unlink(b)
1306 os.unlink(c)
1306 os.unlink(c)
1307
1307
1308 def verify(self):
1308 def verify(self):
1309 filelinkrevs = {}
1309 filelinkrevs = {}
1310 filenodes = {}
1310 filenodes = {}
1311 changesets = revisions = files = 0
1311 changesets = revisions = files = 0
1312 errors = [0]
1312 errors = [0]
1313 neededmanifests = {}
1313 neededmanifests = {}
1314
1314
1315 def err(msg):
1315 def err(msg):
1316 self.ui.warn(msg + "\n")
1316 self.ui.warn(msg + "\n")
1317 errors[0] += 1
1317 errors[0] += 1
1318
1318
1319 seen = {}
1319 seen = {}
1320 self.ui.status("checking changesets\n")
1320 self.ui.status("checking changesets\n")
1321 for i in range(self.changelog.count()):
1321 for i in range(self.changelog.count()):
1322 changesets += 1
1322 changesets += 1
1323 n = self.changelog.node(i)
1323 n = self.changelog.node(i)
1324 l = self.changelog.linkrev(n)
1324 l = self.changelog.linkrev(n)
1325 if l != i:
1325 if l != i:
1326 err("incorrect link (%d) for changeset revision %d" % (l, i))
1326 err("incorrect link (%d) for changeset revision %d" % (l, i))
1327 if n in seen:
1327 if n in seen:
1328 err("duplicate changeset at revision %d" % i)
1328 err("duplicate changeset at revision %d" % i)
1329 seen[n] = 1
1329 seen[n] = 1
1330
1330
1331 for p in self.changelog.parents(n):
1331 for p in self.changelog.parents(n):
1332 if p not in self.changelog.nodemap:
1332 if p not in self.changelog.nodemap:
1333 err("changeset %s has unknown parent %s" %
1333 err("changeset %s has unknown parent %s" %
1334 (short(n), short(p)))
1334 (short(n), short(p)))
1335 try:
1335 try:
1336 changes = self.changelog.read(n)
1336 changes = self.changelog.read(n)
1337 except Exception, inst:
1337 except Exception, inst:
1338 err("unpacking changeset %s: %s" % (short(n), inst))
1338 err("unpacking changeset %s: %s" % (short(n), inst))
1339
1339
1340 neededmanifests[changes[0]] = n
1340 neededmanifests[changes[0]] = n
1341
1341
1342 for f in changes[3]:
1342 for f in changes[3]:
1343 filelinkrevs.setdefault(f, []).append(i)
1343 filelinkrevs.setdefault(f, []).append(i)
1344
1344
1345 seen = {}
1345 seen = {}
1346 self.ui.status("checking manifests\n")
1346 self.ui.status("checking manifests\n")
1347 for i in range(self.manifest.count()):
1347 for i in range(self.manifest.count()):
1348 n = self.manifest.node(i)
1348 n = self.manifest.node(i)
1349 l = self.manifest.linkrev(n)
1349 l = self.manifest.linkrev(n)
1350
1350
1351 if l < 0 or l >= self.changelog.count():
1351 if l < 0 or l >= self.changelog.count():
1352 err("bad manifest link (%d) at revision %d" % (l, i))
1352 err("bad manifest link (%d) at revision %d" % (l, i))
1353
1353
1354 if n in neededmanifests:
1354 if n in neededmanifests:
1355 del neededmanifests[n]
1355 del neededmanifests[n]
1356
1356
1357 if n in seen:
1357 if n in seen:
1358 err("duplicate manifest at revision %d" % i)
1358 err("duplicate manifest at revision %d" % i)
1359
1359
1360 seen[n] = 1
1360 seen[n] = 1
1361
1361
1362 for p in self.manifest.parents(n):
1362 for p in self.manifest.parents(n):
1363 if p not in self.manifest.nodemap:
1363 if p not in self.manifest.nodemap:
1364 err("manifest %s has unknown parent %s" %
1364 err("manifest %s has unknown parent %s" %
1365 (short(n), short(p)))
1365 (short(n), short(p)))
1366
1366
1367 try:
1367 try:
1368 delta = mdiff.patchtext(self.manifest.delta(n))
1368 delta = mdiff.patchtext(self.manifest.delta(n))
1369 except KeyboardInterrupt:
1369 except KeyboardInterrupt:
1370 self.ui.warn("interrupted")
1370 self.ui.warn("interrupted")
1371 raise
1371 raise
1372 except Exception, inst:
1372 except Exception, inst:
1373 err("unpacking manifest %s: %s" % (short(n), inst))
1373 err("unpacking manifest %s: %s" % (short(n), inst))
1374
1374
1375 ff = [ l.split('\0') for l in delta.splitlines() ]
1375 ff = [ l.split('\0') for l in delta.splitlines() ]
1376 for f, fn in ff:
1376 for f, fn in ff:
1377 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1377 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1378
1378
1379 self.ui.status("crosschecking files in changesets and manifests\n")
1379 self.ui.status("crosschecking files in changesets and manifests\n")
1380
1380
1381 for m,c in neededmanifests.items():
1381 for m,c in neededmanifests.items():
1382 err("Changeset %s refers to unknown manifest %s" % (m, c))
1382 err("Changeset %s refers to unknown manifest %s" %
1383 (short(m), short(c)))
1383 del neededmanifests
1384 del neededmanifests
1384
1385
1385 for f in filenodes:
1386 for f in filenodes:
1386 if f not in filelinkrevs:
1387 if f not in filelinkrevs:
1387 err("file %s in manifest but not in changesets" % f)
1388 err("file %s in manifest but not in changesets" % f)
1388
1389
1389 for f in filelinkrevs:
1390 for f in filelinkrevs:
1390 if f not in filenodes:
1391 if f not in filenodes:
1391 err("file %s in changeset but not in manifest" % f)
1392 err("file %s in changeset but not in manifest" % f)
1392
1393
1393 self.ui.status("checking files\n")
1394 self.ui.status("checking files\n")
1394 ff = filenodes.keys()
1395 ff = filenodes.keys()
1395 ff.sort()
1396 ff.sort()
1396 for f in ff:
1397 for f in ff:
1397 if f == "/dev/null": continue
1398 if f == "/dev/null": continue
1398 files += 1
1399 files += 1
1399 fl = self.file(f)
1400 fl = self.file(f)
1400 nodes = { nullid: 1 }
1401 nodes = { nullid: 1 }
1401 seen = {}
1402 seen = {}
1402 for i in range(fl.count()):
1403 for i in range(fl.count()):
1403 revisions += 1
1404 revisions += 1
1404 n = fl.node(i)
1405 n = fl.node(i)
1405
1406
1406 if n in seen:
1407 if n in seen:
1407 err("%s: duplicate revision %d" % (f, i))
1408 err("%s: duplicate revision %d" % (f, i))
1408 if n not in filenodes[f]:
1409 if n not in filenodes[f]:
1409 err("%s: %d:%s not in manifests" % (f, i, short(n)))
1410 err("%s: %d:%s not in manifests" % (f, i, short(n)))
1410 else:
1411 else:
1411 del filenodes[f][n]
1412 del filenodes[f][n]
1412
1413
1413 flr = fl.linkrev(n)
1414 flr = fl.linkrev(n)
1414 if flr not in filelinkrevs[f]:
1415 if flr not in filelinkrevs[f]:
1415 err("%s:%s points to unexpected changeset %d"
1416 err("%s:%s points to unexpected changeset %d"
1416 % (f, short(n), flr))
1417 % (f, short(n), flr))
1417 else:
1418 else:
1418 filelinkrevs[f].remove(flr)
1419 filelinkrevs[f].remove(flr)
1419
1420
1420 # verify contents
1421 # verify contents
1421 try:
1422 try:
1422 t = fl.read(n)
1423 t = fl.read(n)
1423 except Exception, inst:
1424 except Exception, inst:
1424 err("unpacking file %s %s: %s" % (f, short(n), inst))
1425 err("unpacking file %s %s: %s" % (f, short(n), inst))
1425
1426
1426 # verify parents
1427 # verify parents
1427 (p1, p2) = fl.parents(n)
1428 (p1, p2) = fl.parents(n)
1428 if p1 not in nodes:
1429 if p1 not in nodes:
1429 err("file %s:%s unknown parent 1 %s" %
1430 err("file %s:%s unknown parent 1 %s" %
1430 (f, short(n), short(p1)))
1431 (f, short(n), short(p1)))
1431 if p2 not in nodes:
1432 if p2 not in nodes:
1432 err("file %s:%s unknown parent 2 %s" %
1433 err("file %s:%s unknown parent 2 %s" %
1433 (f, short(n), short(p1)))
1434 (f, short(n), short(p1)))
1434 nodes[n] = 1
1435 nodes[n] = 1
1435
1436
1436 # cross-check
1437 # cross-check
1437 for node in filenodes[f]:
1438 for node in filenodes[f]:
1438 err("node %s in manifests not in %s" % (hex(node), f))
1439 err("node %s in manifests not in %s" % (hex(node), f))
1439
1440
1440 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1441 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1441 (files, changesets, revisions))
1442 (files, changesets, revisions))
1442
1443
1443 if errors[0]:
1444 if errors[0]:
1444 self.ui.warn("%d integrity errors encountered!\n" % errors[0])
1445 self.ui.warn("%d integrity errors encountered!\n" % errors[0])
1445 return 1
1446 return 1
General Comments 0
You need to be logged in to leave comments. Login now