comparison mercurial/localrepo.py @ 1680:c21b54f7f7b8

Merge with crew
author Benoit Boissinot <benoit.boissinot@ens-lyon.org>
date Wed, 01 Feb 2006 19:18:15 +0100
parents 675ca845c2f8 dee55c4a4963
children 20b621154e17
comparison
equal deleted inserted replaced
1679:675ca845c2f8 1680:c21b54f7f7b8
17 if not path: 17 if not path:
18 p = os.getcwd() 18 p = os.getcwd()
19 while not os.path.isdir(os.path.join(p, ".hg")): 19 while not os.path.isdir(os.path.join(p, ".hg")):
20 oldp = p 20 oldp = p
21 p = os.path.dirname(p) 21 p = os.path.dirname(p)
22 if p == oldp: raise repo.RepoError(_("no repo found")) 22 if p == oldp:
23 raise repo.RepoError(_("no repo found"))
23 path = p 24 path = p
24 self.path = os.path.join(path, ".hg") 25 self.path = os.path.join(path, ".hg")
25 26
26 if not create and not os.path.isdir(self.path): 27 if not create and not os.path.isdir(self.path):
27 raise repo.RepoError(_("repository %s not found") % path) 28 raise repo.RepoError(_("repository %s not found") % path)
42 os.mkdir(self.join("data")) 43 os.mkdir(self.join("data"))
43 44
44 self.dirstate = dirstate.dirstate(self.opener, ui, self.root) 45 self.dirstate = dirstate.dirstate(self.opener, ui, self.root)
45 try: 46 try:
46 self.ui.readconfig(self.join("hgrc")) 47 self.ui.readconfig(self.join("hgrc"))
47 except IOError: pass 48 except IOError:
49 pass
48 50
49 def hook(self, name, **args): 51 def hook(self, name, **args):
50 def runhook(name, cmd): 52 def runhook(name, cmd):
51 self.ui.note(_("running hook %s: %s\n") % (name, cmd)) 53 self.ui.note(_("running hook %s: %s\n") % (name, cmd))
52 old = {} 54 old = {}
124 for t, n in self.tags().items(): 126 for t, n in self.tags().items():
125 try: 127 try:
126 r = self.changelog.rev(n) 128 r = self.changelog.rev(n)
127 except: 129 except:
128 r = -2 # sort to the beginning of the list if unknown 130 r = -2 # sort to the beginning of the list if unknown
129 l.append((r,t,n)) 131 l.append((r, t, n))
130 l.sort() 132 l.sort()
131 return [(t,n) for r,t,n in l] 133 return [(t, n) for r, t, n in l]
132 134
133 def nodetags(self, node): 135 def nodetags(self, node):
134 '''return the tags associated with a node''' 136 '''return the tags associated with a node'''
135 if not self.nodetagscache: 137 if not self.nodetagscache:
136 self.nodetagscache = {} 138 self.nodetagscache = {}
137 for t,n in self.tags().items(): 139 for t, n in self.tags().items():
138 self.nodetagscache.setdefault(n,[]).append(t) 140 self.nodetagscache.setdefault(n, []).append(t)
139 return self.nodetagscache.get(node, []) 141 return self.nodetagscache.get(node, [])
140 142
141 def lookup(self, key): 143 def lookup(self, key):
142 try: 144 try:
143 return self.tags()[key] 145 return self.tags()[key]
158 160
159 def wjoin(self, f): 161 def wjoin(self, f):
160 return os.path.join(self.root, f) 162 return os.path.join(self.root, f)
161 163
162 def file(self, f): 164 def file(self, f):
163 if f[0] == '/': f = f[1:] 165 if f[0] == '/':
166 f = f[1:]
164 return filelog.filelog(self.opener, f) 167 return filelog.filelog(self.opener, f)
165 168
166 def getcwd(self): 169 def getcwd(self):
167 return self.dirstate.getcwd() 170 return self.dirstate.getcwd()
168 171
224 def recover(self): 227 def recover(self):
225 lock = self.lock() 228 lock = self.lock()
226 if os.path.exists(self.join("journal")): 229 if os.path.exists(self.join("journal")):
227 self.ui.status(_("rolling back interrupted transaction\n")) 230 self.ui.status(_("rolling back interrupted transaction\n"))
228 transaction.rollback(self.opener, self.join("journal")) 231 transaction.rollback(self.opener, self.join("journal"))
232 self.manifest = manifest.manifest(self.opener)
233 self.changelog = changelog.changelog(self.opener)
229 return True 234 return True
230 else: 235 else:
231 self.ui.warn(_("no interrupted transaction available\n")) 236 self.ui.warn(_("no interrupted transaction available\n"))
232 return False 237 return False
233 238
332 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date) 337 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
333 tr.close() 338 tr.close()
334 if update_dirstate: 339 if update_dirstate:
335 self.dirstate.setparents(n, nullid) 340 self.dirstate.setparents(n, nullid)
336 341
337 def commit(self, files = None, text = "", user = None, date = None, 342 def commit(self, files=None, text="", user=None, date=None,
338 match = util.always, force=False): 343 match=util.always, force=False):
339 commit = [] 344 commit = []
340 remove = [] 345 remove = []
341 changed = [] 346 changed = []
342 347
343 if files: 348 if files:
348 elif s == 'r': 353 elif s == 'r':
349 remove.append(f) 354 remove.append(f)
350 else: 355 else:
351 self.ui.warn(_("%s not tracked!\n") % f) 356 self.ui.warn(_("%s not tracked!\n") % f)
352 else: 357 else:
353 (c, a, d, u) = self.changes(match=match) 358 modified, added, removed, deleted, unknown = self.changes(match=match)
354 commit = c + a 359 commit = modified + added
355 remove = d 360 remove = removed
356 361
357 p1, p2 = self.dirstate.parents() 362 p1, p2 = self.dirstate.parents()
358 c1 = self.changelog.read(p1) 363 c1 = self.changelog.read(p1)
359 c2 = self.changelog.read(p2) 364 c2 = self.changelog.read(p2)
360 m1 = self.manifest.read(c1[0]) 365 m1 = self.manifest.read(c1[0])
396 fp1, fp2 = nullid, nullid 401 fp1, fp2 = nullid, nullid
397 else: 402 else:
398 fp1 = m1.get(f, nullid) 403 fp1 = m1.get(f, nullid)
399 fp2 = m2.get(f, nullid) 404 fp2 = m2.get(f, nullid)
400 405
401 # is the same revision on two branches of a merge?
402 if fp2 == fp1:
403 fp2 = nullid
404
405 if fp2 != nullid: 406 if fp2 != nullid:
406 # is one parent an ancestor of the other? 407 # is one parent an ancestor of the other?
407 fpa = r.ancestor(fp1, fp2) 408 fpa = r.ancestor(fp1, fp2)
408 if fpa == fp1: 409 if fpa == fp1:
409 fp1, fp2 = fp2, nullid 410 fp1, fp2 = fp2, nullid
410 elif fpa == fp2: 411 elif fpa == fp2:
411 fp2 = nullid 412 fp2 = nullid
412 413
413 # is the file unmodified from the parent? 414 # is the file unmodified from the parent?
414 if not meta and t == r.read(fp1): 415 if not meta and t == r.read(fp1) and fp2 == nullid:
415 # record the proper existing parent in manifest 416 # record the proper existing parent in manifest
416 # no need to add a revision 417 # no need to add a revision
417 new[f] = fp1 418 new[f] = fp1
418 continue 419 continue
419 420
421 # remember what we've added so that we can later calculate 422 # remember what we've added so that we can later calculate
422 # the files to pull from a set of changesets 423 # the files to pull from a set of changesets
423 changed.append(f) 424 changed.append(f)
424 425
425 # update manifest 426 # update manifest
427 m1 = m1.copy()
426 m1.update(new) 428 m1.update(new)
427 for f in remove: 429 for f in remove:
428 if f in m1: 430 if f in m1:
429 del m1[f] 431 del m1[f]
430 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0], 432 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
447 if not edittext.rstrip(): 449 if not edittext.rstrip():
448 return None 450 return None
449 text = edittext 451 text = edittext
450 452
451 user = user or self.ui.username() 453 user = user or self.ui.username()
452 n = self.changelog.add(mn, changed, text, tr, p1, p2, user, date) 454 n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, user, date)
453 tr.close() 455 tr.close()
454 456
455 self.dirstate.setparents(n) 457 self.dirstate.setparents(n)
456 self.dirstate.update(new, "n") 458 self.dirstate.update(new, "n")
457 self.dirstate.forget(remove) 459 self.dirstate.forget(remove)
472 util.pathto(self.getcwd(), fn), short(node))) 474 util.pathto(self.getcwd(), fn), short(node)))
473 else: 475 else:
474 for src, fn in self.dirstate.walk(files, match): 476 for src, fn in self.dirstate.walk(files, match):
475 yield src, fn 477 yield src, fn
476 478
477 def changes(self, node1 = None, node2 = None, files = [], 479 def changes(self, node1=None, node2=None, files=[], match=util.always):
478 match = util.always): 480 """return changes between two nodes or node and working directory
479 mf2, u = None, [] 481
482 If node1 is None, use the first dirstate parent instead.
483 If node2 is None, compare node1 with working directory.
484 """
480 485
481 def fcmp(fn, mf): 486 def fcmp(fn, mf):
482 t1 = self.wread(fn) 487 t1 = self.wread(fn)
483 t2 = self.file(fn).read(mf.get(fn, nullid)) 488 t2 = self.file(fn).read(mf.get(fn, nullid))
484 return cmp(t1, t2) 489 return cmp(t1, t2)
485 490
486 def mfmatches(node): 491 def mfmatches(node):
487 mf = dict(self.manifest.read(node)) 492 change = self.changelog.read(node)
493 mf = dict(self.manifest.read(change[0]))
488 for fn in mf.keys(): 494 for fn in mf.keys():
489 if not match(fn): 495 if not match(fn):
490 del mf[fn] 496 del mf[fn]
491 return mf 497 return mf
492 498
494 if not node2: 500 if not node2:
495 try: 501 try:
496 wlock = self.wlock(wait=0) 502 wlock = self.wlock(wait=0)
497 except lock.LockHeld: 503 except lock.LockHeld:
498 wlock = None 504 wlock = None
499 l, c, a, d, u = self.dirstate.changes(files, match) 505 lookup, modified, added, removed, deleted, unknown = (
506 self.dirstate.changes(files, match))
500 507
501 # are we comparing working dir against its parent? 508 # are we comparing working dir against its parent?
502 if not node1: 509 if not node1:
503 if l: 510 if lookup:
504 # do a full compare of any files that might have changed 511 # do a full compare of any files that might have changed
505 change = self.changelog.read(self.dirstate.parents()[0]) 512 mf2 = mfmatches(self.dirstate.parents()[0])
506 mf2 = mfmatches(change[0]) 513 for f in lookup:
507 for f in l:
508 if fcmp(f, mf2): 514 if fcmp(f, mf2):
509 c.append(f) 515 modified.append(f)
510 elif wlock is not None: 516 elif wlock is not None:
511 self.dirstate.update([f], "n") 517 self.dirstate.update([f], "n")
512 518 else:
513 for l in c, a, d, u: 519 # we are comparing working dir against non-parent
514 l.sort() 520 # generate a pseudo-manifest for the working dir
515 521 mf2 = mfmatches(self.dirstate.parents()[0])
516 return (c, a, d, u) 522 for f in lookup + modified + added:
517 523 mf2[f] = ""
518 # are we comparing working dir against non-tip? 524 for f in removed:
519 # generate a pseudo-manifest for the working dir 525 if f in mf2:
520 if not node2: 526 del mf2[f]
521 if not mf2:
522 change = self.changelog.read(self.dirstate.parents()[0])
523 mf2 = mfmatches(change[0])
524 for f in a + c + l:
525 mf2[f] = ""
526 for f in d:
527 if f in mf2: del mf2[f]
528 else: 527 else:
529 change = self.changelog.read(node2) 528 # we are comparing two revisions
530 mf2 = mfmatches(change[0]) 529 deleted, unknown = [], []
531 530 mf2 = mfmatches(node2)
532 # flush lists from dirstate before comparing manifests 531
533 c, a = [], [] 532 if node1:
534 533 # flush lists from dirstate before comparing manifests
535 change = self.changelog.read(node1) 534 modified, added = [], []
536 mf1 = mfmatches(change[0]) 535
537 536 mf1 = mfmatches(node1)
538 for fn in mf2: 537
539 if mf1.has_key(fn): 538 for fn in mf2:
540 if mf1[fn] != mf2[fn]: 539 if mf1.has_key(fn):
541 if mf2[fn] != "" or fcmp(fn, mf1): 540 if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
542 c.append(fn) 541 modified.append(fn)
543 del mf1[fn] 542 del mf1[fn]
544 else: 543 else:
545 a.append(fn) 544 added.append(fn)
546 545
547 d = mf1.keys() 546 removed = mf1.keys()
548 547
549 for l in c, a, d, u: 548 # sort and return results:
549 for l in modified, added, removed, deleted, unknown:
550 l.sort() 550 l.sort()
551 551 return (modified, added, removed, deleted, unknown)
552 return (c, a, d, u)
553 552
554 def add(self, list): 553 def add(self, list):
555 wlock = self.wlock() 554 wlock = self.wlock()
556 for f in list: 555 for f in list:
557 p = self.wjoin(f) 556 p = self.wjoin(f)
558 if not os.path.exists(p): 557 if not os.path.exists(p):
559 self.ui.warn(_("%s does not exist!\n") % f) 558 self.ui.warn(_("%s does not exist!\n") % f)
560 elif not os.path.isfile(p): 559 elif not os.path.isfile(p):
561 self.ui.warn(_("%s not added: only files supported currently\n") % f) 560 self.ui.warn(_("%s not added: only files supported currently\n")
561 % f)
562 elif self.dirstate.state(f) in 'an': 562 elif self.dirstate.state(f) in 'an':
563 self.ui.warn(_("%s already tracked!\n") % f) 563 self.ui.warn(_("%s already tracked!\n") % f)
564 else: 564 else:
565 self.dirstate.update([f], "a") 565 self.dirstate.update([f], "a")
566 566
576 if unlink: 576 if unlink:
577 for f in list: 577 for f in list:
578 try: 578 try:
579 util.unlink(self.wjoin(f)) 579 util.unlink(self.wjoin(f))
580 except OSError, inst: 580 except OSError, inst:
581 if inst.errno != errno.ENOENT: raise 581 if inst.errno != errno.ENOENT:
582 raise
582 wlock = self.wlock() 583 wlock = self.wlock()
583 for f in list: 584 for f in list:
584 p = self.wjoin(f) 585 p = self.wjoin(f)
585 if os.path.exists(p): 586 if os.path.exists(p):
586 self.ui.warn(_("%s still exists!\n") % f) 587 self.ui.warn(_("%s still exists!\n") % f)
731 l = out.setdefault(h, []) 732 l = out.setdefault(h, [])
732 l[len(l):] = self.nodetags(b) 733 l[len(l):] = self.nodetags(b)
733 return out 734 return out
734 735
735 def branches(self, nodes): 736 def branches(self, nodes):
736 if not nodes: nodes = [self.changelog.tip()] 737 if not nodes:
738 nodes = [self.changelog.tip()]
737 b = [] 739 b = []
738 for n in nodes: 740 for n in nodes:
739 t = n 741 t = n
740 while n: 742 while n:
741 p = self.changelog.parents(n) 743 p = self.changelog.parents(n)
803 while unknown: 805 while unknown:
804 n = unknown.pop(0) 806 n = unknown.pop(0)
805 if n[0] in seen: 807 if n[0] in seen:
806 continue 808 continue
807 809
808 self.ui.debug(_("examining %s:%s\n") % (short(n[0]), short(n[1]))) 810 self.ui.debug(_("examining %s:%s\n")
811 % (short(n[0]), short(n[1])))
809 if n[0] == nullid: 812 if n[0] == nullid:
810 break 813 break
811 if n in seenbranch: 814 if n in seenbranch:
812 self.ui.debug(_("branch already found\n")) 815 self.ui.debug(_("branch already found\n"))
813 continue 816 continue
839 for p in range(0, len(r), 10): 842 for p in range(0, len(r), 10):
840 for b in remote.branches(r[p:p+10]): 843 for b in remote.branches(r[p:p+10]):
841 self.ui.debug(_("received %s:%s\n") % 844 self.ui.debug(_("received %s:%s\n") %
842 (short(b[0]), short(b[1]))) 845 (short(b[0]), short(b[1])))
843 if b[0] in m: 846 if b[0] in m:
844 self.ui.debug(_("found base node %s\n") % short(b[0])) 847 self.ui.debug(_("found base node %s\n")
848 % short(b[0]))
845 base[b[0]] = 1 849 base[b[0]] = 1
846 elif b[0] not in seen: 850 elif b[0] not in seen:
847 unknown.append(b) 851 unknown.append(b)
848 852
849 # do binary search on the branches we found 853 # do binary search on the branches we found
912 subset.append(n) 916 subset.append(n)
913 917
914 # this is the set of all roots we have to push 918 # this is the set of all roots we have to push
915 return subset 919 return subset
916 920
917 def pull(self, remote, heads = None): 921 def pull(self, remote, heads=None):
918 lock = self.lock() 922 lock = self.lock()
919 923
920 # if we have an empty repo, fetch everything 924 # if we have an empty repo, fetch everything
921 if self.changelog.tip() == nullid: 925 if self.changelog.tip() == nullid:
922 self.ui.status(_("requesting all changes\n")) 926 self.ui.status(_("requesting all changes\n"))
1197 # Go through all our files in order sorted by name. 1201 # Go through all our files in order sorted by name.
1198 for fname in changedfiles: 1202 for fname in changedfiles:
1199 filerevlog = self.file(fname) 1203 filerevlog = self.file(fname)
1200 # Toss out the filenodes that the recipient isn't really 1204 # Toss out the filenodes that the recipient isn't really
1201 # missing. 1205 # missing.
1202 prune_filenodes(fname, filerevlog) 1206 if msng_filenode_set.has_key(fname):
1203 msng_filenode_lst = msng_filenode_set[fname].keys() 1207 prune_filenodes(fname, filerevlog)
1208 msng_filenode_lst = msng_filenode_set[fname].keys()
1209 else:
1210 msng_filenode_lst = []
1204 # If any filenodes are left, generate the group for them, 1211 # If any filenodes are left, generate the group for them,
1205 # otherwise don't bother. 1212 # otherwise don't bother.
1206 if len(msng_filenode_lst) > 0: 1213 if len(msng_filenode_lst) > 0:
1207 yield struct.pack(">l", len(fname) + 4) + fname 1214 yield struct.pack(">l", len(fname) + 4) + fname
1208 # Sort the filenodes by their revision # 1215 # Sort the filenodes by their revision #
1212 # from filenodes. 1219 # from filenodes.
1213 group = filerevlog.group(msng_filenode_lst, 1220 group = filerevlog.group(msng_filenode_lst,
1214 lookup_filenode_link_func(fname)) 1221 lookup_filenode_link_func(fname))
1215 for chnk in group: 1222 for chnk in group:
1216 yield chnk 1223 yield chnk
1217 # Don't need this anymore, toss it to free memory. 1224 if msng_filenode_set.has_key(fname):
1218 del msng_filenode_set[fname] 1225 # Don't need this anymore, toss it to free memory.
1226 del msng_filenode_set[fname]
1219 # Signal that no more groups are left. 1227 # Signal that no more groups are left.
1220 yield struct.pack(">l", 0) 1228 yield struct.pack(">l", 0)
1221 1229
1222 return util.chunkbuffer(gengroup()) 1230 return util.chunkbuffer(gengroup())
1223 1231
1283 1291
1284 def addchangegroup(self, source): 1292 def addchangegroup(self, source):
1285 1293
1286 def getchunk(): 1294 def getchunk():
1287 d = source.read(4) 1295 d = source.read(4)
1288 if not d: return "" 1296 if not d:
1297 return ""
1289 l = struct.unpack(">l", d)[0] 1298 l = struct.unpack(">l", d)[0]
1290 if l <= 4: return "" 1299 if l <= 4:
1300 return ""
1291 d = source.read(l - 4) 1301 d = source.read(l - 4)
1292 if len(d) < l - 4: 1302 if len(d) < l - 4:
1293 raise repo.RepoError(_("premature EOF reading chunk" 1303 raise repo.RepoError(_("premature EOF reading chunk"
1294 " (got %d bytes, expected %d)") 1304 " (got %d bytes, expected %d)")
1295 % (len(d), l - 4)) 1305 % (len(d), l - 4))
1296 return d 1306 return d
1297 1307
1298 def getgroup(): 1308 def getgroup():
1299 while 1: 1309 while 1:
1300 c = getchunk() 1310 c = getchunk()
1301 if not c: break 1311 if not c:
1312 break
1302 yield c 1313 yield c
1303 1314
1304 def csmap(x): 1315 def csmap(x):
1305 self.ui.debug(_("add changeset %s\n") % short(x)) 1316 self.ui.debug(_("add changeset %s\n") % short(x))
1306 return self.changelog.count() 1317 return self.changelog.count()
1307 1318
1308 def revmap(x): 1319 def revmap(x):
1309 return self.changelog.rev(x) 1320 return self.changelog.rev(x)
1310 1321
1311 if not source: return 1322 if not source:
1323 return
1312 changesets = files = revisions = 0 1324 changesets = files = revisions = 0
1313 1325
1314 tr = self.transaction() 1326 tr = self.transaction()
1315 1327
1316 oldheads = len(self.changelog.heads()) 1328 oldheads = len(self.changelog.heads())
1331 1343
1332 # process the files 1344 # process the files
1333 self.ui.status(_("adding file changes\n")) 1345 self.ui.status(_("adding file changes\n"))
1334 while 1: 1346 while 1:
1335 f = getchunk() 1347 f = getchunk()
1336 if not f: break 1348 if not f:
1349 break
1337 self.ui.debug(_("adding %s revisions\n") % f) 1350 self.ui.debug(_("adding %s revisions\n") % f)
1338 fl = self.file(f) 1351 fl = self.file(f)
1339 o = fl.count() 1352 o = fl.count()
1340 n = fl.addgroup(getgroup(), revmap, tr) 1353 n = fl.addgroup(getgroup(), revmap, tr)
1341 revisions += fl.count() - o 1354 revisions += fl.count() - o
1352 1365
1353 tr.close() 1366 tr.close()
1354 1367
1355 if changesets > 0: 1368 if changesets > 0:
1356 if not self.hook("changegroup", 1369 if not self.hook("changegroup",
1357 node=hex(self.changelog.node(cor+1))): 1370 node=hex(self.changelog.node(cor+1))):
1358 self.ui.warn(_("abort: changegroup hook returned failure!\n")) 1371 self.ui.warn(_("abort: changegroup hook returned failure!\n"))
1359 return 1 1372 return 1
1360 1373
1361 for i in range(cor + 1, cnr + 1): 1374 for i in range(cor + 1, cnr + 1):
1362 self.hook("commit", node=hex(self.changelog.node(i))) 1375 self.hook("commit", node=hex(self.changelog.node(i)))
1367 moddirstate=True, forcemerge=False): 1380 moddirstate=True, forcemerge=False):
1368 pl = self.dirstate.parents() 1381 pl = self.dirstate.parents()
1369 if not force and pl[1] != nullid: 1382 if not force and pl[1] != nullid:
1370 self.ui.warn(_("aborting: outstanding uncommitted merges\n")) 1383 self.ui.warn(_("aborting: outstanding uncommitted merges\n"))
1371 return 1 1384 return 1
1385
1386 err = False
1372 1387
1373 p1, p2 = pl[0], node 1388 p1, p2 = pl[0], node
1374 pa = self.changelog.ancestor(p1, p2) 1389 pa = self.changelog.ancestor(p1, p2)
1375 m1n = self.changelog.read(p1)[0] 1390 m1n = self.changelog.read(p1)[0]
1376 m2n = self.changelog.read(p2)[0] 1391 m2n = self.changelog.read(p2)[0]
1377 man = self.manifest.ancestor(m1n, m2n) 1392 man = self.manifest.ancestor(m1n, m2n)
1378 m1 = self.manifest.read(m1n) 1393 m1 = self.manifest.read(m1n)
1379 mf1 = self.manifest.readflags(m1n) 1394 mf1 = self.manifest.readflags(m1n)
1380 m2 = self.manifest.read(m2n) 1395 m2 = self.manifest.read(m2n).copy()
1381 mf2 = self.manifest.readflags(m2n) 1396 mf2 = self.manifest.readflags(m2n)
1382 ma = self.manifest.read(man) 1397 ma = self.manifest.read(man)
1383 mfa = self.manifest.readflags(man) 1398 mfa = self.manifest.readflags(man)
1384 1399
1385 (c, a, d, u) = self.changes() 1400 modified, added, removed, deleted, unknown = self.changes()
1386
1387 if allow and not forcemerge:
1388 if c or a or d:
1389 raise util.Abort(_("outstanding uncommited changes"))
1390 if not forcemerge and not force:
1391 for f in u:
1392 if f in m2:
1393 t1 = self.wread(f)
1394 t2 = self.file(f).read(m2[f])
1395 if cmp(t1, t2) != 0:
1396 raise util.Abort(_("'%s' already exists in the working"
1397 " dir and differs from remote") % f)
1398 1401
1399 # is this a jump, or a merge? i.e. is there a linear path 1402 # is this a jump, or a merge? i.e. is there a linear path
1400 # from p1 to p2? 1403 # from p1 to p2?
1401 linear_path = (pa == p1 or pa == p2) 1404 linear_path = (pa == p1 or pa == p2)
1405
1406 if allow and linear_path:
1407 raise util.Abort(_("there is nothing to merge, "
1408 "just use 'hg update'"))
1409 if allow and not forcemerge:
1410 if modified or added or removed:
1411 raise util.Abort(_("outstanding uncommited changes"))
1412 if not forcemerge and not force:
1413 for f in unknown:
1414 if f in m2:
1415 t1 = self.wread(f)
1416 t2 = self.file(f).read(m2[f])
1417 if cmp(t1, t2) != 0:
1418 raise util.Abort(_("'%s' already exists in the working"
1419 " dir and differs from remote") % f)
1402 1420
1403 # resolve the manifest to determine which files 1421 # resolve the manifest to determine which files
1404 # we care about merging 1422 # we care about merging
1405 self.ui.note(_("resolving manifests\n")) 1423 self.ui.note(_("resolving manifests\n"))
1406 self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") % 1424 self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") %
1413 remove = [] 1431 remove = []
1414 1432
1415 # construct a working dir manifest 1433 # construct a working dir manifest
1416 mw = m1.copy() 1434 mw = m1.copy()
1417 mfw = mf1.copy() 1435 mfw = mf1.copy()
1418 umap = dict.fromkeys(u) 1436 umap = dict.fromkeys(unknown)
1419 1437
1420 for f in a + c + u: 1438 for f in added + modified + unknown:
1421 mw[f] = "" 1439 mw[f] = ""
1422 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False)) 1440 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1423 1441
1424 if moddirstate: 1442 if moddirstate:
1425 wlock = self.wlock() 1443 wlock = self.wlock()
1426 1444
1427 for f in d: 1445 for f in deleted + removed:
1428 if f in mw: del mw[f] 1446 if f in mw:
1447 del mw[f]
1429 1448
1430 # If we're jumping between revisions (as opposed to merging), 1449 # If we're jumping between revisions (as opposed to merging),
1431 # and if neither the working directory nor the target rev has 1450 # and if neither the working directory nor the target rev has
1432 # the file, then we need to remove it from the dirstate, to 1451 # the file, then we need to remove it from the dirstate, to
1433 # prevent the dirstate from listing the file when it is no 1452 # prevent the dirstate from listing the file when it is no
1435 if moddirstate and linear_path and f not in m2: 1454 if moddirstate and linear_path and f not in m2:
1436 self.dirstate.forget((f,)) 1455 self.dirstate.forget((f,))
1437 1456
1438 # Compare manifests 1457 # Compare manifests
1439 for f, n in mw.iteritems(): 1458 for f, n in mw.iteritems():
1440 if choose and not choose(f): continue 1459 if choose and not choose(f):
1460 continue
1441 if f in m2: 1461 if f in m2:
1442 s = 0 1462 s = 0
1443 1463
1444 # is the wfile new since m1, and match m2? 1464 # is the wfile new since m1, and match m2?
1445 if f not in m1: 1465 if f not in m1:
1478 util.set_exec(self.wjoin(f), mf2[f]) 1498 util.set_exec(self.wjoin(f), mf2[f])
1479 else: 1499 else:
1480 a, b, c = mfa.get(f, 0), mfw[f], mf2[f] 1500 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1481 mode = ((a^b) | (a^c)) ^ a 1501 mode = ((a^b) | (a^c)) ^ a
1482 if mode != b: 1502 if mode != b:
1483 self.ui.debug(_(" updating permissions for %s\n") % f) 1503 self.ui.debug(_(" updating permissions for %s\n")
1504 % f)
1484 util.set_exec(self.wjoin(f), mode) 1505 util.set_exec(self.wjoin(f), mode)
1485 del m2[f] 1506 del m2[f]
1486 elif f in ma: 1507 elif f in ma:
1487 if n != ma[f]: 1508 if n != ma[f]:
1488 r = _("d") 1509 r = _("d")
1508 self.ui.debug(_("local modified %s, keeping\n") % f) 1529 self.ui.debug(_("local modified %s, keeping\n") % f)
1509 else: 1530 else:
1510 self.ui.debug(_("working dir created %s, keeping\n") % f) 1531 self.ui.debug(_("working dir created %s, keeping\n") % f)
1511 1532
1512 for f, n in m2.iteritems(): 1533 for f, n in m2.iteritems():
1513 if choose and not choose(f): continue 1534 if choose and not choose(f):
1514 if f[0] == "/": continue 1535 continue
1536 if f[0] == "/":
1537 continue
1515 if f in ma and n != ma[f]: 1538 if f in ma and n != ma[f]:
1516 r = _("k") 1539 r = _("k")
1517 if not force and (linear_path or allow): 1540 if not force and (linear_path or allow):
1518 r = self.ui.prompt( 1541 r = self.ui.prompt(
1519 (_("remote changed %s which local deleted\n") % f) + 1542 (_("remote changed %s which local deleted\n") % f) +
1520 _("(k)eep or (d)elete?"), _("[kd]"), _("k")) 1543 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1521 if r == _("k"): get[f] = n 1544 if r == _("k"):
1545 get[f] = n
1522 elif f not in ma: 1546 elif f not in ma:
1523 self.ui.debug(_("remote created %s\n") % f) 1547 self.ui.debug(_("remote created %s\n") % f)
1524 get[f] = n 1548 get[f] = n
1525 else: 1549 else:
1526 if force or p2 == pa: # going backwards? 1550 if force or p2 == pa: # going backwards?
1546 " affecting the following files:\n")) 1570 " affecting the following files:\n"))
1547 fl = merge.keys() + get.keys() 1571 fl = merge.keys() + get.keys()
1548 fl.sort() 1572 fl.sort()
1549 for f in fl: 1573 for f in fl:
1550 cf = "" 1574 cf = ""
1551 if f in merge: cf = _(" (resolve)") 1575 if f in merge:
1576 cf = _(" (resolve)")
1552 self.ui.status(" %s%s\n" % (f, cf)) 1577 self.ui.status(" %s%s\n" % (f, cf))
1553 self.ui.warn(_("aborting update spanning branches!\n")) 1578 self.ui.warn(_("aborting update spanning branches!\n"))
1554 self.ui.status(_("(use update -m to merge across branches" 1579 self.ui.status(_("(use update -m to merge across branches"
1555 " or -C to lose changes)\n")) 1580 " or -C to lose changes)\n"))
1556 return 1 1581 return 1
1558 1583
1559 # get the files we don't need to change 1584 # get the files we don't need to change
1560 files = get.keys() 1585 files = get.keys()
1561 files.sort() 1586 files.sort()
1562 for f in files: 1587 for f in files:
1563 if f[0] == "/": continue 1588 if f[0] == "/":
1589 continue
1564 self.ui.note(_("getting %s\n") % f) 1590 self.ui.note(_("getting %s\n") % f)
1565 t = self.file(f).read(get[f]) 1591 t = self.file(f).read(get[f])
1566 self.wwrite(f, t) 1592 self.wwrite(f, t)
1567 util.set_exec(self.wjoin(f), mf2[f]) 1593 util.set_exec(self.wjoin(f), mf2[f])
1568 if moddirstate: 1594 if moddirstate:
1575 files = merge.keys() 1601 files = merge.keys()
1576 files.sort() 1602 files.sort()
1577 for f in files: 1603 for f in files:
1578 self.ui.status(_("merging %s\n") % f) 1604 self.ui.status(_("merging %s\n") % f)
1579 my, other, flag = merge[f] 1605 my, other, flag = merge[f]
1580 self.merge3(f, my, other) 1606 ret = self.merge3(f, my, other)
1607 if ret:
1608 err = True
1581 util.set_exec(self.wjoin(f), flag) 1609 util.set_exec(self.wjoin(f), flag)
1582 if moddirstate: 1610 if moddirstate:
1583 if branch_merge: 1611 if branch_merge:
1584 # We've done a branch merge, mark this file as merged 1612 # We've done a branch merge, mark this file as merged
1585 # so that we properly record the merger later 1613 # so that we properly record the merger later
1608 else: 1636 else:
1609 self.dirstate.forget(remove) 1637 self.dirstate.forget(remove)
1610 1638
1611 if moddirstate: 1639 if moddirstate:
1612 self.dirstate.setparents(p1, p2) 1640 self.dirstate.setparents(p1, p2)
1641 return err
1613 1642
1614 def merge3(self, fn, my, other): 1643 def merge3(self, fn, my, other):
1615 """perform a 3-way merge in the working directory""" 1644 """perform a 3-way merge in the working directory"""
1616 1645
1617 def temp(prefix, node): 1646 def temp(prefix, node):
1638 if r: 1667 if r:
1639 self.ui.warn(_("merging %s failed!\n") % fn) 1668 self.ui.warn(_("merging %s failed!\n") % fn)
1640 1669
1641 os.unlink(b) 1670 os.unlink(b)
1642 os.unlink(c) 1671 os.unlink(c)
1672 return r
1643 1673
1644 def verify(self): 1674 def verify(self):
1645 filelinkrevs = {} 1675 filelinkrevs = {}
1646 filenodes = {} 1676 filenodes = {}
1647 changesets = revisions = files = 0 1677 changesets = revisions = files = 0
1650 1680
1651 def err(msg): 1681 def err(msg):
1652 self.ui.warn(msg + "\n") 1682 self.ui.warn(msg + "\n")
1653 errors[0] += 1 1683 errors[0] += 1
1654 1684
1685 def checksize(obj, name):
1686 d = obj.checksize()
1687 if d[0]:
1688 err(_("%s data length off by %d bytes") % (name, d[0]))
1689 if d[1]:
1690 err(_("%s index contains %d extra bytes") % (name, d[1]))
1691
1655 seen = {} 1692 seen = {}
1656 self.ui.status(_("checking changesets\n")) 1693 self.ui.status(_("checking changesets\n"))
1657 d = self.changelog.checksize() 1694 checksize(self.changelog, "changelog")
1658 if d: 1695
1659 err(_("changeset data short %d bytes") % d)
1660 for i in range(self.changelog.count()): 1696 for i in range(self.changelog.count()):
1661 changesets += 1 1697 changesets += 1
1662 n = self.changelog.node(i) 1698 n = self.changelog.node(i)
1663 l = self.changelog.linkrev(n) 1699 l = self.changelog.linkrev(n)
1664 if l != i: 1700 if l != i:
1684 for f in changes[3]: 1720 for f in changes[3]:
1685 filelinkrevs.setdefault(f, []).append(i) 1721 filelinkrevs.setdefault(f, []).append(i)
1686 1722
1687 seen = {} 1723 seen = {}
1688 self.ui.status(_("checking manifests\n")) 1724 self.ui.status(_("checking manifests\n"))
1689 d = self.manifest.checksize() 1725 checksize(self.manifest, "manifest")
1690 if d: 1726
1691 err(_("manifest data short %d bytes") % d)
1692 for i in range(self.manifest.count()): 1727 for i in range(self.manifest.count()):
1693 n = self.manifest.node(i) 1728 n = self.manifest.node(i)
1694 l = self.manifest.linkrev(n) 1729 l = self.manifest.linkrev(n)
1695 1730
1696 if l < 0 or l >= self.changelog.count(): 1731 if l < 0 or l >= self.changelog.count():
1721 for f, fn in ff: 1756 for f, fn in ff:
1722 filenodes.setdefault(f, {})[bin(fn[:40])] = 1 1757 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1723 1758
1724 self.ui.status(_("crosschecking files in changesets and manifests\n")) 1759 self.ui.status(_("crosschecking files in changesets and manifests\n"))
1725 1760
1726 for m,c in neededmanifests.items(): 1761 for m, c in neededmanifests.items():
1727 err(_("Changeset %s refers to unknown manifest %s") % 1762 err(_("Changeset %s refers to unknown manifest %s") %
1728 (short(m), short(c))) 1763 (short(m), short(c)))
1729 del neededmanifests 1764 del neededmanifests
1730 1765
1731 for f in filenodes: 1766 for f in filenodes:
1738 1773
1739 self.ui.status(_("checking files\n")) 1774 self.ui.status(_("checking files\n"))
1740 ff = filenodes.keys() 1775 ff = filenodes.keys()
1741 ff.sort() 1776 ff.sort()
1742 for f in ff: 1777 for f in ff:
1743 if f == "/dev/null": continue 1778 if f == "/dev/null":
1779 continue
1744 files += 1 1780 files += 1
1745 fl = self.file(f) 1781 fl = self.file(f)
1746 d = fl.checksize() 1782 checksize(fl, f)
1747 if d: 1783
1748 err(_("%s file data short %d bytes") % (f, d)) 1784 nodes = {nullid: 1}
1749
1750 nodes = { nullid: 1 }
1751 seen = {} 1785 seen = {}
1752 for i in range(fl.count()): 1786 for i in range(fl.count()):
1753 revisions += 1 1787 revisions += 1
1754 n = fl.node(i) 1788 n = fl.node(i)
1755 1789