comparison mercurial/hg.py @ 146:4a828422247d

Handle merge with deletions If you merge with a repo that has deleted a file after editing it, hg attempted to resolve the file. This (correctly) resulted in hg verify errors because the resolved version didn't show up in the manifests. This moves the manifest resolution before file resolution and decides which files to resolve based on the (partially) resolved manifest. After files are resolved, the final manifest is committed.
author mpm@selenic.com
date Tue, 24 May 2005 20:30:35 -0800
parents ea9188538222
children c32286d0a665
comparison
equal deleted inserted replaced
145:fbce9fc531d2 146:4a828422247d
610 l = struct.unpack(">l", d)[0] 610 l = struct.unpack(">l", d)[0]
611 return source.read(l - 4 + add) 611 return source.read(l - 4 + add)
612 612
613 tr = self.transaction() 613 tr = self.transaction()
614 simple = True 614 simple = True
615 need = {}
615 616
616 self.ui.status("adding changesets\n") 617 self.ui.status("adding changesets\n")
617 # pull off the changeset group 618 # pull off the changeset group
618 def report(x): 619 def report(x):
619 self.ui.debug("add changeset %s\n" % short(x)) 620 self.ui.debug("add changeset %s\n" % short(x))
632 # do we need a resolve? 633 # do we need a resolve?
633 if self.changelog.ancestor(co, cn) != co: 634 if self.changelog.ancestor(co, cn) != co:
634 simple = False 635 simple = False
635 resolverev = self.changelog.count() 636 resolverev = self.changelog.count()
636 637
638 # resolve the manifest to determine which files
639 # we care about merging
640 self.ui.status("resolving manifests\n")
641 ma = self.manifest.ancestor(mm, mo)
642 omap = self.manifest.read(mo) # other
643 amap = self.manifest.read(ma) # ancestor
644 mmap = self.manifest.read(mm) # mine
645 nmap = {}
646
647 self.ui.debug(" ancestor %s local %s remote %s\n" %
648 (short(ma), short(mm), short(mo)))
649
650 for f, mid in mmap.iteritems():
651 if f in omap:
652 if mid != omap[f]:
653 self.ui.debug(" %s versions differ, do resolve\n" % f)
654 need[f] = mid # use merged version or local version
655 else:
656 nmap[f] = mid # keep ours
657 del omap[f]
658 elif f in amap:
659 if mid != amap[f]:
660 r = self.ui.prompt(
661 (" local changed %s which remote deleted\n" % f) +
662 "(k)eep or (d)elete?", "[kd]", "k")
663 if r == "k": nmap[f] = mid
664 else:
665 self.ui.debug("other deleted %s\n" % f)
666 pass # other deleted it
667 else:
668 self.ui.debug("local created %s\n" %f)
669 nmap[f] = mid # we created it
670
671 del mmap
672
673 for f, oid in omap.iteritems():
674 if f in amap:
675 if oid != amap[f]:
676 r = self.ui.prompt(
677 ("remote changed %s which local deleted\n" % f) +
678 "(k)eep or (d)elete?", "[kd]", "k")
679 if r == "k": nmap[f] = oid
680 else:
681 pass # probably safe
682 else:
683 self.ui.debug("remote created %s, do resolve\n" % f)
684 need[f] = oid
685
686 del omap
687 del amap
688
689 new = need.keys()
690 new.sort()
691
637 # process the files 692 # process the files
638 self.ui.status("adding files\n") 693 self.ui.status("adding files\n")
639 new = {}
640 while 1: 694 while 1:
641 f = getchunk(4) 695 f = getchunk(4)
642 if not f: break 696 if not f: break
643 fg = getchunk() 697 fg = getchunk()
644 self.ui.debug("adding %s revisions\n" % f) 698 self.ui.debug("adding %s revisions\n" % f)
645 fl = self.file(f) 699 fl = self.file(f)
646 o = fl.tip() 700 o = fl.tip()
647 n = fl.addgroup(fg, lambda x: self.changelog.rev(x), tr) 701 n = fl.addgroup(fg, lambda x: self.changelog.rev(x), tr)
648 if not simple: 702 if f in need:
649 if o == n: continue 703 del need[f]
650 # this file has changed between branches, so it must be 704 # manifest resolve determined we need to merge the tips
651 # represented in the merge changeset 705 nmap[f] = self.merge3(fl, f, o, n, tr, resolverev)
652 new[f] = self.merge3(fl, f, o, n, tr, resolverev) 706
707 if need:
708 # we need to do trivial merges on local files
709 for f in new:
710 if f not in need: continue
711 fl = self.file(f)
712 nmap[f] = self.merge3(fl, f, need[f], fl.tip(), tr, resolverev)
653 713
654 # For simple merges, we don't need to resolve manifests or changesets 714 # For simple merges, we don't need to resolve manifests or changesets
655 if simple: 715 if simple:
656 self.ui.debug("simple merge, skipping resolve\n") 716 self.ui.debug("simple merge, skipping resolve\n")
657 tr.close() 717 tr.close()
658 return 718 return
659 719
660 # resolve the manifest to point to all the merged files
661 self.ui.status("resolving manifests\n")
662 ma = self.manifest.ancestor(mm, mo)
663 omap = self.manifest.read(mo) # other
664 amap = self.manifest.read(ma) # ancestor
665 mmap = self.manifest.read(mm) # mine
666 self.ui.debug("ancestor %s local %s remote %s\n" %
667 (short(ma), short(mm), short(mo)))
668 nmap = {}
669
670 for f, mid in mmap.iteritems():
671 if f in omap:
672 if mid != omap[f]:
673 self.ui.debug("%s versions differ\n" % f)
674 if f in new: self.ui.debug("%s updated in resolve\n" % f)
675 # use merged version or local version
676 nmap[f] = new.get(f, mid)
677 else:
678 nmap[f] = mid # keep ours
679 del omap[f]
680 elif f in amap:
681 if mid != amap[f]:
682 r = self.ui.prompt(
683 ("local changed %s which remote deleted\n" % f) +
684 "(k)eep or (d)elete?", "[kd]", "k")
685 if r == "k": nmap[f] = mid
686 else:
687 self.ui.debug("other deleted %s\n" % f)
688 pass # other deleted it
689 else:
690 self.ui.debug("local created %s\n" %f)
691 nmap[f] = mid # we created it
692
693 del mmap
694
695 for f, oid in omap.iteritems():
696 if f in amap:
697 if oid != amap[f]:
698 r = self.ui.prompt(
699 ("remote changed %s which local deleted\n" % f) +
700 "(k)eep or (d)elete?", "[kd]", "k")
701 if r == "k": nmap[f] = oid
702 else:
703 pass # probably safe
704 else:
705 self.ui.debug("remote created %s\n" % f)
706 nmap[f] = new.get(f, oid) # remote created it
707
708 del omap
709 del amap
710
711 node = self.manifest.add(nmap, tr, resolverev, mm, mo) 720 node = self.manifest.add(nmap, tr, resolverev, mm, mo)
712 721
713 # Now all files and manifests are merged, we add the changed files 722 # Now all files and manifests are merged, we add the changed files
714 # and manifest id to the changelog 723 # and manifest id to the changelog
715 self.ui.status("committing merge changeset\n") 724 self.ui.status("committing merge changeset\n")
716 new = new.keys()
717 new.sort()
718 if co == cn: cn = -1 725 if co == cn: cn = -1
719 726
720 edittext = "\nHG: merge resolve\n" + \ 727 edittext = "\nHG: merge resolve\n" + \
721 "".join(["HG: changed %s\n" % f for f in new]) 728 "".join(["HG: changed %s\n" % f for f in new])
722 edittext = self.ui.edit(edittext) 729 edittext = self.ui.edit(edittext)