comparison mercurial/hg.py @ 990:5007e0bdeed2

Fix long-standing excessive file merges Since switching to the multihead approach, we've been creating excessive file-level merges where files are marked as merged with their ancestors. This explicitly checks at commit time whether the two parent versions are linearly related, and if so, reduces the file check-in to a non-merge. Then the file is compared against the remaining parent, and, if equal, skips check-in of that file (as it's not changed). Since we're not checking in all files that were different between versions, we no longer need to mark so many files for merge. This removes most of the 'm' state marking as well. Finally, it is possible to do a tree-level merge with no file-level changes. This will happen if one user changes file A and another changes file B. Thus, if we have have two parents, we allow commit to proceed even if there are no file-level changes.
author mpm@selenic.com
date Sun, 21 Aug 2005 21:59:55 -0700
parents 4f81068ed8cd
children b634b15c020b
comparison
equal deleted inserted replaced
989:1b6eb272b238 990:5007e0bdeed2
838 try: 838 try:
839 t = self.wfile(f).read() 839 t = self.wfile(f).read()
840 tm = util.is_exec(self.wjoin(f), mfm.get(f, False)) 840 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
841 r = self.file(f) 841 r = self.file(f)
842 mfm[f] = tm 842 mfm[f] = tm
843 mm[f] = r.add(t, {}, tr, linkrev, 843
844 m1.get(f, nullid), m2.get(f, nullid)) 844 fp1 = m1.get(f, nullid)
845 fp2 = m2.get(f, nullid)
846
847 # is the same revision on two branches of a merge?
848 if fp2 == fp1:
849 fp2 = nullid
850
851 if fp2 != nullid:
852 # is one parent an ancestor of the other?
853 fpa = r.ancestor(fp1, fp2)
854 if fpa == fp1:
855 fp1, fp2 = fp2, nullid
856 elif fpa == fp2:
857 fp2 = nullid
858
859 # is the file unmodified from the parent?
860 if t == r.read(fp1):
861 # record the proper existing parent in manifest
862 # no need to add a revision
863 mm[f] = fp1
864 continue
865
866 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
845 if update_dirstate: 867 if update_dirstate:
846 self.dirstate.update([f], "n") 868 self.dirstate.update([f], "n")
847 except IOError: 869 except IOError:
848 try: 870 try:
849 del mm[f] 871 del mm[f]
877 else: 899 else:
878 (c, a, d, u) = self.changes(match = match) 900 (c, a, d, u) = self.changes(match = match)
879 commit = c + a 901 commit = c + a
880 remove = d 902 remove = d
881 903
882 if not commit and not remove and not force:
883 self.ui.status("nothing changed\n")
884 return None
885
886 if not self.hook("precommit"):
887 return None
888
889 p1, p2 = self.dirstate.parents() 904 p1, p2 = self.dirstate.parents()
890 c1 = self.changelog.read(p1) 905 c1 = self.changelog.read(p1)
891 c2 = self.changelog.read(p2) 906 c2 = self.changelog.read(p2)
892 m1 = self.manifest.read(c1[0]) 907 m1 = self.manifest.read(c1[0])
893 mf1 = self.manifest.readflags(c1[0]) 908 mf1 = self.manifest.readflags(c1[0])
894 m2 = self.manifest.read(c2[0]) 909 m2 = self.manifest.read(c2[0])
910
911 if not commit and not remove and not force and p2 == nullid:
912 self.ui.status("nothing changed\n")
913 return None
914
915 if not self.hook("precommit"):
916 return None
917
895 lock = self.lock() 918 lock = self.lock()
896 tr = self.transaction() 919 tr = self.transaction()
897 920
898 # check in files 921 # check in files
899 new = {} 922 new = {}
916 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"])) 939 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"]))
917 940
918 r = self.file(f) 941 r = self.file(f)
919 fp1 = m1.get(f, nullid) 942 fp1 = m1.get(f, nullid)
920 fp2 = m2.get(f, nullid) 943 fp2 = m2.get(f, nullid)
944
945 # is the same revision on two branches of a merge?
946 if fp2 == fp1:
947 fp2 = nullid
948
949 if fp2 != nullid:
950 # is one parent an ancestor of the other?
951 fpa = r.ancestor(fp1, fp2)
952 if fpa == fp1:
953 fp1, fp2 = fp2, nullid
954 elif fpa == fp2:
955 fp2 = nullid
956
957 # is the file unmodified from the parent?
958 if not meta and t == r.read(fp1):
959 # record the proper existing parent in manifest
960 # no need to add a revision
961 new[f] = fp1
962 continue
963
921 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2) 964 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
922 965
923 # update manifest 966 # update manifest
924 m1.update(new) 967 m1.update(new)
925 for f in remove: 968 for f in remove:
1713 self.ui.status(" %s%s\n" % (f, cf)) 1756 self.ui.status(" %s%s\n" % (f, cf))
1714 self.ui.warn("aborting update spanning branches!\n") 1757 self.ui.warn("aborting update spanning branches!\n")
1715 self.ui.status("(use update -m to merge across branches" + 1758 self.ui.status("(use update -m to merge across branches" +
1716 " or -C to lose changes)\n") 1759 " or -C to lose changes)\n")
1717 return 1 1760 return 1
1718 # we have to remember what files we needed to get/change
1719 # because any file that's different from either one of its
1720 # parents must be in the changeset
1721 mode = 'm' 1761 mode = 'm'
1722 if moddirstate:
1723 self.dirstate.update(mark.keys(), "m")
1724 1762
1725 if moddirstate: 1763 if moddirstate:
1726 self.dirstate.setparents(p1, p2) 1764 self.dirstate.setparents(p1, p2)
1727 1765
1728 # get the files we don't need to change 1766 # get the files we don't need to change
1737 except IOError: 1775 except IOError:
1738 os.makedirs(os.path.dirname(self.wjoin(f))) 1776 os.makedirs(os.path.dirname(self.wjoin(f)))
1739 self.wfile(f, "w").write(t) 1777 self.wfile(f, "w").write(t)
1740 util.set_exec(self.wjoin(f), mf2[f]) 1778 util.set_exec(self.wjoin(f), mf2[f])
1741 if moddirstate: 1779 if moddirstate:
1742 self.dirstate.update([f], mode) 1780 self.dirstate.update([f], 'n')
1743 1781
1744 # merge the tricky bits 1782 # merge the tricky bits
1745 files = merge.keys() 1783 files = merge.keys()
1746 files.sort() 1784 files.sort()
1747 for f in files: 1785 for f in files: