Show More
@@ -71,6 +71,23 b" MERGE_RECORD_UNRESOLVED_PATH = b'pu'" | |||
|
71 | 71 | MERGE_RECORD_RESOLVED_PATH = b'pr' |
|
72 | 72 | MERGE_RECORD_DRIVER_RESOLVED = b'd' |
|
73 | 73 | |
|
74 | ACTION_FORGET = b'f' | |
|
75 | ACTION_REMOVE = b'r' | |
|
76 | ACTION_ADD = b'a' | |
|
77 | ACTION_GET = b'g' | |
|
78 | ACTION_PATH_CONFLICT = b'p' | |
|
79 | ACTION_PATH_CONFLICT_RESOLVE = b'pr' | |
|
80 | ACTION_ADD_MODIFIED = b'am' | |
|
81 | ACTION_CREATED = b'c' | |
|
82 | ACTION_DELETED_CHANGED = b'dc' | |
|
83 | ACTION_CHANGED_DELETED = b'cd' | |
|
84 | ACTION_MERGE = b'm' | |
|
85 | ACTION_LOCAL_DIR_RENAME_GET = b'dg' | |
|
86 | ACTION_DIR_RENAME_MOVE_LOCAL = b'dm' | |
|
87 | ACTION_KEEP = b'k' | |
|
88 | ACTION_EXEC = b'e' | |
|
89 | ACTION_CREATED_MERGE = b'cm' | |
|
90 | ||
|
74 | 91 | class mergestate(object): |
|
75 | 92 | '''track 3-way merge state of individual files |
|
76 | 93 | |
@@ -588,18 +605,18 b' class mergestate(object):' | |||
|
588 | 605 | if fcd.isabsent(): |
|
589 | 606 | # dc: local picked. Need to drop if present, which may |
|
590 | 607 | # happen on re-resolves. |
|
591 |
action = |
|
|
608 | action = ACTION_FORGET | |
|
592 | 609 | else: |
|
593 | 610 | # cd: remote picked (or otherwise deleted) |
|
594 |
action = |
|
|
611 | action = ACTION_REMOVE | |
|
595 | 612 | else: |
|
596 | 613 | if fcd.isabsent(): # dc: remote picked |
|
597 |
action = |
|
|
614 | action = ACTION_GET | |
|
598 | 615 | elif fco.isabsent(): # cd: local picked |
|
599 | 616 | if dfile in self.localctx: |
|
600 |
action = |
|
|
617 | action = ACTION_ADD_MODIFIED | |
|
601 | 618 | else: |
|
602 |
action = |
|
|
619 | action = ACTION_ADD | |
|
603 | 620 | # else: regular merges (no action necessary) |
|
604 | 621 | self._results[dfile] = r, action |
|
605 | 622 | |
@@ -631,7 +648,7 b' class mergestate(object):' | |||
|
631 | 648 | if r is None: |
|
632 | 649 | updated += 1 |
|
633 | 650 | elif r == 0: |
|
634 |
if action == |
|
|
651 | if action == ACTION_REMOVE: | |
|
635 | 652 | removed += 1 |
|
636 | 653 | else: |
|
637 | 654 | merged += 1 |
@@ -643,7 +660,13 b' class mergestate(object):' | |||
|
643 | 660 | |
|
644 | 661 | def actions(self): |
|
645 | 662 | """return lists of actions to perform on the dirstate""" |
|
646 | actions = {'r': [], 'f': [], 'a': [], 'am': [], 'g': []} | |
|
663 | actions = { | |
|
664 | ACTION_REMOVE: [], | |
|
665 | ACTION_FORGET: [], | |
|
666 | ACTION_ADD: [], | |
|
667 | ACTION_ADD_MODIFIED: [], | |
|
668 | ACTION_GET: [], | |
|
669 | } | |
|
647 | 670 | for f, (r, action) in self._results.iteritems(): |
|
648 | 671 | if action is not None: |
|
649 | 672 | actions[action].append((f, None, "merge result")) |
@@ -658,19 +681,19 b' class mergestate(object):' | |||
|
658 | 681 | """queues a file to be removed from the dirstate |
|
659 | 682 | |
|
660 | 683 | Meant for use by custom merge drivers.""" |
|
661 |
self._results[f] = 0, |
|
|
684 | self._results[f] = 0, ACTION_REMOVE | |
|
662 | 685 | |
|
663 | 686 | def queueadd(self, f): |
|
664 | 687 | """queues a file to be added to the dirstate |
|
665 | 688 | |
|
666 | 689 | Meant for use by custom merge drivers.""" |
|
667 |
self._results[f] = 0, |
|
|
690 | self._results[f] = 0, ACTION_ADD | |
|
668 | 691 | |
|
669 | 692 | def queueget(self, f): |
|
670 | 693 | """queues a file to be marked modified in the dirstate |
|
671 | 694 | |
|
672 | 695 | Meant for use by custom merge drivers.""" |
|
673 |
self._results[f] = 0, |
|
|
696 | self._results[f] = 0, ACTION_GET | |
|
674 | 697 | |
|
675 | 698 | def _getcheckunknownconfig(repo, section, name): |
|
676 | 699 | config = repo.ui.config(section, name) |
@@ -772,14 +795,14 b' def _checkunknownfiles(repo, wctx, mctx,' | |||
|
772 | 795 | |
|
773 | 796 | checkunknowndirs = _unknowndirschecker() |
|
774 | 797 | for f, (m, args, msg) in actions.iteritems(): |
|
775 | if m in ('c', 'dc'): | |
|
798 | if m in (ACTION_CREATED, ACTION_DELETED_CHANGED): | |
|
776 | 799 | if _checkunknownfile(repo, wctx, mctx, f): |
|
777 | 800 | fileconflicts.add(f) |
|
778 | 801 | elif pathconfig and f not in wctx: |
|
779 | 802 | path = checkunknowndirs(repo, wctx, f) |
|
780 | 803 | if path is not None: |
|
781 | 804 | pathconflicts.add(path) |
|
782 | elif m == 'dg': | |
|
805 | elif m == ACTION_LOCAL_DIR_RENAME_GET: | |
|
783 | 806 | if _checkunknownfile(repo, wctx, mctx, f, args[0]): |
|
784 | 807 | fileconflicts.add(f) |
|
785 | 808 | |
@@ -791,7 +814,7 b' def _checkunknownfiles(repo, wctx, mctx,' | |||
|
791 | 814 | collectconflicts(unknownconflicts, unknownconfig) |
|
792 | 815 | else: |
|
793 | 816 | for f, (m, args, msg) in actions.iteritems(): |
|
794 |
if m == |
|
|
817 | if m == ACTION_CREATED_MERGE: | |
|
795 | 818 | fl2, anc = args |
|
796 | 819 | different = _checkunknownfile(repo, wctx, mctx, f) |
|
797 | 820 | if repo.dirstate._ignore(f): |
@@ -812,16 +835,16 b' def _checkunknownfiles(repo, wctx, mctx,' | |||
|
812 | 835 | # don't like an abort happening in the middle of |
|
813 | 836 | # merge.update. |
|
814 | 837 | if not different: |
|
815 |
actions[f] = ( |
|
|
838 | actions[f] = (ACTION_GET, (fl2, False), 'remote created') | |
|
816 | 839 | elif mergeforce or config == 'abort': |
|
817 |
actions[f] = ( |
|
|
818 |
|
|
|
840 | actions[f] = (ACTION_MERGE, (f, f, None, False, anc), | |
|
841 | 'remote differs from untracked local') | |
|
819 | 842 | elif config == 'abort': |
|
820 | 843 | abortconflicts.add(f) |
|
821 | 844 | else: |
|
822 | 845 | if config == 'warn': |
|
823 | 846 | warnconflicts.add(f) |
|
824 |
actions[f] = ( |
|
|
847 | actions[f] = (ACTION_GET, (fl2, True), 'remote created') | |
|
825 | 848 | |
|
826 | 849 | for f in sorted(abortconflicts): |
|
827 | 850 | warn = repo.ui.warn |
@@ -843,11 +866,11 b' def _checkunknownfiles(repo, wctx, mctx,' | |||
|
843 | 866 | repo.ui.warn(_("%s: replacing untracked files in directory\n") % f) |
|
844 | 867 | |
|
845 | 868 | for f, (m, args, msg) in actions.iteritems(): |
|
846 |
if m == |
|
|
869 | if m == ACTION_CREATED: | |
|
847 | 870 | backup = (f in fileconflicts or f in pathconflicts or |
|
848 | 871 | any(p in pathconflicts for p in util.finddirs(f))) |
|
849 | 872 | flags, = args |
|
850 |
actions[f] = ( |
|
|
873 | actions[f] = (ACTION_GET, (flags, backup), msg) | |
|
851 | 874 | |
|
852 | 875 | def _forgetremoved(wctx, mctx, branchmerge): |
|
853 | 876 | """ |
@@ -865,9 +888,9 b' def _forgetremoved(wctx, mctx, branchmer' | |||
|
865 | 888 | """ |
|
866 | 889 | |
|
867 | 890 | actions = {} |
|
868 | m = 'f' | |
|
891 | m = ACTION_FORGET | |
|
869 | 892 | if branchmerge: |
|
870 |
m = |
|
|
893 | m = ACTION_REMOVE | |
|
871 | 894 | for f in wctx.deleted(): |
|
872 | 895 | if f not in mctx: |
|
873 | 896 | actions[f] = m, None, "forget deleted" |
@@ -875,7 +898,7 b' def _forgetremoved(wctx, mctx, branchmer' | |||
|
875 | 898 | if not branchmerge: |
|
876 | 899 | for f in wctx.removed(): |
|
877 | 900 | if f not in mctx: |
|
878 |
actions[f] = |
|
|
901 | actions[f] = ACTION_FORGET, None, "forget removed" | |
|
879 | 902 | |
|
880 | 903 | return actions |
|
881 | 904 | |
@@ -884,19 +907,20 b' def _checkcollision(repo, wmf, actions):' | |||
|
884 | 907 | pmmf = set(wmf) |
|
885 | 908 | |
|
886 | 909 | if actions: |
|
887 |
# |
|
|
888 | for m in 'a', 'am', 'f', 'g', 'cd', 'dc': | |
|
910 | # KEEP and EXEC are no-op | |
|
911 | for m in (ACTION_ADD, ACTION_ADD_MODIFIED, ACTION_FORGET, ACTION_GET, | |
|
912 | ACTION_CHANGED_DELETED, ACTION_DELETED_CHANGED): | |
|
889 | 913 | for f, args, msg in actions[m]: |
|
890 | 914 | pmmf.add(f) |
|
891 |
for f, args, msg in actions[ |
|
|
915 | for f, args, msg in actions[ACTION_REMOVE]: | |
|
892 | 916 | pmmf.discard(f) |
|
893 |
for f, args, msg in actions[ |
|
|
917 | for f, args, msg in actions[ACTION_DIR_RENAME_MOVE_LOCAL]: | |
|
894 | 918 | f2, flags = args |
|
895 | 919 | pmmf.discard(f2) |
|
896 | 920 | pmmf.add(f) |
|
897 |
for f, args, msg in actions[ |
|
|
921 | for f, args, msg in actions[ACTION_LOCAL_DIR_RENAME_GET]: | |
|
898 | 922 | pmmf.add(f) |
|
899 |
for f, args, msg in actions[ |
|
|
923 | for f, args, msg in actions[ACTION_MERGE]: | |
|
900 | 924 | f1, f2, fa, move, anc = args |
|
901 | 925 | if move: |
|
902 | 926 | pmmf.discard(f1) |
@@ -972,7 +996,8 b' def checkpathconflicts(repo, wctx, mctx,' | |||
|
972 | 996 | deletedfiles = set() |
|
973 | 997 | |
|
974 | 998 | for f, (m, args, msg) in actions.items(): |
|
975 | if m in ('c', 'dc', 'm', 'cm'): | |
|
999 | if m in (ACTION_CREATED, ACTION_DELETED_CHANGED, ACTION_MERGE, | |
|
1000 | ACTION_CREATED_MERGE): | |
|
976 | 1001 | # This action may create a new local file. |
|
977 | 1002 | createdfiledirs.update(util.finddirs(f)) |
|
978 | 1003 | if mf.hasdir(f): |
@@ -981,13 +1006,13 b' def checkpathconflicts(repo, wctx, mctx,' | |||
|
981 | 1006 | # will be checked once we know what all the deleted files are. |
|
982 | 1007 | remoteconflicts.add(f) |
|
983 | 1008 | # Track the names of all deleted files. |
|
984 |
if m == |
|
|
1009 | if m == ACTION_REMOVE: | |
|
985 | 1010 | deletedfiles.add(f) |
|
986 |
if m == |
|
|
1011 | if m == ACTION_MERGE: | |
|
987 | 1012 | f1, f2, fa, move, anc = args |
|
988 | 1013 | if move: |
|
989 | 1014 | deletedfiles.add(f1) |
|
990 | if m == 'dm': | |
|
1015 | if m == ACTION_DIR_RENAME_MOVE_LOCAL: | |
|
991 | 1016 | f2, flags = args |
|
992 | 1017 | deletedfiles.add(f2) |
|
993 | 1018 | |
@@ -1003,7 +1028,10 b' def checkpathconflicts(repo, wctx, mctx,' | |||
|
1003 | 1028 | # A file is in a directory which aliases a local file. |
|
1004 | 1029 | # We will need to rename the local file. |
|
1005 | 1030 | localconflicts.add(p) |
|
1006 |
if p in actions and actions[p][0] in ( |
|
|
1031 | if p in actions and actions[p][0] in (ACTION_CREATED, | |
|
1032 | ACTION_DELETED_CHANGED, | |
|
1033 | ACTION_MERGE, | |
|
1034 | ACTION_CREATED_MERGE): | |
|
1007 | 1035 | # The file is in a directory which aliases a remote file. |
|
1008 | 1036 | # This is an internal inconsistency within the remote |
|
1009 | 1037 | # manifest. |
@@ -1014,8 +1042,10 b' def checkpathconflicts(repo, wctx, mctx,' | |||
|
1014 | 1042 | if p not in deletedfiles: |
|
1015 | 1043 | ctxname = bytes(wctx).rstrip('+') |
|
1016 | 1044 | pnew = util.safename(p, ctxname, wctx, set(actions.keys())) |
|
1017 | actions[pnew] = ('pr', (p,), "local path conflict") | |
|
1018 | actions[p] = ('p', (pnew, 'l'), "path conflict") | |
|
1045 | actions[pnew] = (ACTION_PATH_CONFLICT_RESOLVE, (p,), | |
|
1046 | 'local path conflict') | |
|
1047 | actions[p] = (ACTION_PATH_CONFLICT, (pnew, 'l'), | |
|
1048 | 'path conflict') | |
|
1019 | 1049 | |
|
1020 | 1050 | if remoteconflicts: |
|
1021 | 1051 | # Check if all files in the conflicting directories have been removed. |
@@ -1024,14 +1054,16 b' def checkpathconflicts(repo, wctx, mctx,' | |||
|
1024 | 1054 | if f not in deletedfiles: |
|
1025 | 1055 | m, args, msg = actions[p] |
|
1026 | 1056 | pnew = util.safename(p, ctxname, wctx, set(actions.keys())) |
|
1027 | if m in ('dc', 'm'): | |
|
1057 | if m in (ACTION_DELETED_CHANGED, ACTION_MERGE): | |
|
1028 | 1058 | # Action was merge, just update target. |
|
1029 | 1059 | actions[pnew] = (m, args, msg) |
|
1030 | 1060 | else: |
|
1031 | 1061 | # Action was create, change to renamed get action. |
|
1032 | 1062 | fl = args[0] |
|
1033 |
actions[pnew] = ( |
|
|
1034 | actions[p] = ('p', (pnew, 'r'), "path conflict") | |
|
1063 | actions[pnew] = (ACTION_LOCAL_DIR_RENAME_GET, (p, fl), | |
|
1064 | 'remote path conflict') | |
|
1065 | actions[p] = (ACTION_PATH_CONFLICT, (pnew, ACTION_REMOVE), | |
|
1066 | 'path conflict') | |
|
1035 | 1067 | remoteconflicts.remove(p) |
|
1036 | 1068 | break |
|
1037 | 1069 | |
@@ -1109,77 +1141,80 b' def manifestmerge(repo, wctx, p2, pa, br' | |||
|
1109 | 1141 | if f not in ma: |
|
1110 | 1142 | fa = copy.get(f, None) |
|
1111 | 1143 | if fa is not None: |
|
1112 |
actions[f] = ( |
|
|
1113 |
|
|
|
1144 | actions[f] = (ACTION_MERGE, (f, f, fa, False, pa.node()), | |
|
1145 | 'both renamed from %s' % fa) | |
|
1114 | 1146 | else: |
|
1115 |
actions[f] = ( |
|
|
1116 |
|
|
|
1147 | actions[f] = (ACTION_MERGE, (f, f, None, False, pa.node()), | |
|
1148 | 'both created') | |
|
1117 | 1149 | else: |
|
1118 | 1150 | a = ma[f] |
|
1119 | 1151 | fla = ma.flags(f) |
|
1120 | 1152 | nol = 'l' not in fl1 + fl2 + fla |
|
1121 | 1153 | if n2 == a and fl2 == fla: |
|
1122 |
actions[f] = ( |
|
|
1154 | actions[f] = (ACTION_KEEP, (), 'remote unchanged') | |
|
1123 | 1155 | elif n1 == a and fl1 == fla: # local unchanged - use remote |
|
1124 | 1156 | if n1 == n2: # optimization: keep local content |
|
1125 |
actions[f] = ( |
|
|
1157 | actions[f] = (ACTION_EXEC, (fl2,), 'update permissions') | |
|
1126 | 1158 | else: |
|
1127 |
actions[f] = ( |
|
|
1159 | actions[f] = (ACTION_GET, (fl2, False), | |
|
1160 | 'remote is newer') | |
|
1128 | 1161 | elif nol and n2 == a: # remote only changed 'x' |
|
1129 |
actions[f] = ( |
|
|
1162 | actions[f] = (ACTION_EXEC, (fl2,), 'update permissions') | |
|
1130 | 1163 | elif nol and n1 == a: # local only changed 'x' |
|
1131 |
actions[f] = ( |
|
|
1164 | actions[f] = (ACTION_GET, (fl1, False), 'remote is newer') | |
|
1132 | 1165 | else: # both changed something |
|
1133 |
actions[f] = ( |
|
|
1134 |
|
|
|
1166 | actions[f] = (ACTION_MERGE, (f, f, f, False, pa.node()), | |
|
1167 | 'versions differ') | |
|
1135 | 1168 | elif n1: # file exists only on local side |
|
1136 | 1169 | if f in copied: |
|
1137 | 1170 | pass # we'll deal with it on m2 side |
|
1138 | 1171 | elif f in movewithdir: # directory rename, move local |
|
1139 | 1172 | f2 = movewithdir[f] |
|
1140 | 1173 | if f2 in m2: |
|
1141 |
actions[f2] = ( |
|
|
1142 |
|
|
|
1174 | actions[f2] = (ACTION_MERGE, (f, f2, None, True, pa.node()), | |
|
1175 | 'remote directory rename, both created') | |
|
1143 | 1176 | else: |
|
1144 |
actions[f2] = ( |
|
|
1145 |
|
|
|
1177 | actions[f2] = (ACTION_DIR_RENAME_MOVE_LOCAL, (f, fl1), | |
|
1178 | 'remote directory rename - move from %s' % f) | |
|
1146 | 1179 | elif f in copy: |
|
1147 | 1180 | f2 = copy[f] |
|
1148 |
actions[f] = ( |
|
|
1149 |
|
|
|
1181 | actions[f] = (ACTION_MERGE, (f, f2, f2, False, pa.node()), | |
|
1182 | 'local copied/moved from %s' % f2) | |
|
1150 | 1183 | elif f in ma: # clean, a different, no remote |
|
1151 | 1184 | if n1 != ma[f]: |
|
1152 | 1185 | if acceptremote: |
|
1153 |
actions[f] = ( |
|
|
1186 | actions[f] = (ACTION_REMOVE, None, 'remote delete') | |
|
1154 | 1187 | else: |
|
1155 | actions[f] = ('cd', (f, None, f, False, pa.node()), | |
|
1156 |
|
|
|
1188 | actions[f] = (ACTION_CHANGED_DELETED, | |
|
1189 | (f, None, f, False, pa.node()), | |
|
1190 | 'prompt changed/deleted') | |
|
1157 | 1191 | elif n1 == addednodeid: |
|
1158 | 1192 | # This extra 'a' is added by working copy manifest to mark |
|
1159 | 1193 | # the file as locally added. We should forget it instead of |
|
1160 | 1194 | # deleting it. |
|
1161 |
actions[f] = ( |
|
|
1195 | actions[f] = (ACTION_FORGET, None, 'remote deleted') | |
|
1162 | 1196 | else: |
|
1163 |
actions[f] = ( |
|
|
1197 | actions[f] = (ACTION_REMOVE, None, 'other deleted') | |
|
1164 | 1198 | elif n2: # file exists only on remote side |
|
1165 | 1199 | if f in copied: |
|
1166 | 1200 | pass # we'll deal with it on m1 side |
|
1167 | 1201 | elif f in movewithdir: |
|
1168 | 1202 | f2 = movewithdir[f] |
|
1169 | 1203 | if f2 in m1: |
|
1170 |
actions[f2] = ( |
|
|
1171 |
|
|
|
1204 | actions[f2] = (ACTION_MERGE, | |
|
1205 | (f2, f, None, False, pa.node()), | |
|
1206 | 'local directory rename, both created') | |
|
1172 | 1207 | else: |
|
1173 |
actions[f2] = ( |
|
|
1174 |
|
|
|
1208 | actions[f2] = (ACTION_LOCAL_DIR_RENAME_GET, (f, fl2), | |
|
1209 | 'local directory rename - get from %s' % f) | |
|
1175 | 1210 | elif f in copy: |
|
1176 | 1211 | f2 = copy[f] |
|
1177 | 1212 | if f2 in m2: |
|
1178 |
actions[f] = ( |
|
|
1179 |
|
|
|
1213 | actions[f] = (ACTION_MERGE, (f2, f, f2, False, pa.node()), | |
|
1214 | 'remote copied from %s' % f2) | |
|
1180 | 1215 | else: |
|
1181 |
actions[f] = ( |
|
|
1182 |
|
|
|
1216 | actions[f] = (ACTION_MERGE, (f2, f, f2, True, pa.node()), | |
|
1217 | 'remote moved from %s' % f2) | |
|
1183 | 1218 | elif f not in ma: |
|
1184 | 1219 | # local unknown, remote created: the logic is described by the |
|
1185 | 1220 | # following table: |
@@ -1193,12 +1228,12 b' def manifestmerge(repo, wctx, p2, pa, br' | |||
|
1193 | 1228 | # Checking whether the files are different is expensive, so we |
|
1194 | 1229 | # don't do that when we can avoid it. |
|
1195 | 1230 | if not force: |
|
1196 |
actions[f] = ( |
|
|
1231 | actions[f] = (ACTION_CREATED, (fl2,), 'remote created') | |
|
1197 | 1232 | elif not branchmerge: |
|
1198 |
actions[f] = ( |
|
|
1233 | actions[f] = (ACTION_CREATED, (fl2,), 'remote created') | |
|
1199 | 1234 | else: |
|
1200 |
actions[f] = ( |
|
|
1201 |
|
|
|
1235 | actions[f] = (ACTION_CREATED_MERGE, (fl2, pa.node()), | |
|
1236 | 'remote created, get or merge') | |
|
1202 | 1237 | elif n2 != ma[f]: |
|
1203 | 1238 | df = None |
|
1204 | 1239 | for d in dirmove: |
@@ -1207,13 +1242,15 b' def manifestmerge(repo, wctx, p2, pa, br' | |||
|
1207 | 1242 | df = dirmove[d] + f[len(d):] |
|
1208 | 1243 | break |
|
1209 | 1244 | if df is not None and df in m1: |
|
1210 |
actions[df] = ( |
|
|
1211 |
|
|
|
1245 | actions[df] = (ACTION_MERGE, (df, f, f, False, pa.node()), | |
|
1246 | 'local directory rename - respect move ' | |
|
1247 | 'from %s' % f) | |
|
1212 | 1248 | elif acceptremote: |
|
1213 |
actions[f] = ( |
|
|
1249 | actions[f] = (ACTION_CREATED, (fl2,), 'remote recreating') | |
|
1214 | 1250 | else: |
|
1215 | actions[f] = ('dc', (None, f, f, False, pa.node()), | |
|
1216 |
|
|
|
1251 | actions[f] = (ACTION_DELETED_CHANGED, | |
|
1252 | (None, f, f, False, pa.node()), | |
|
1253 | 'prompt deleted/changed') | |
|
1217 | 1254 | |
|
1218 | 1255 | if repo.ui.configbool('experimental', 'merge.checkpathconflicts'): |
|
1219 | 1256 | # If we are merging, look for path conflicts. |
@@ -1227,10 +1264,12 b' def _resolvetrivial(repo, wctx, mctx, an' | |||
|
1227 | 1264 | # We force a copy of actions.items() because we're going to mutate |
|
1228 | 1265 | # actions as we resolve trivial conflicts. |
|
1229 | 1266 | for f, (m, args, msg) in list(actions.items()): |
|
1230 | if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]): | |
|
1267 | if (m == ACTION_CHANGED_DELETED and f in ancestor | |
|
1268 | and not wctx[f].cmp(ancestor[f])): | |
|
1231 | 1269 | # local did change but ended up with same content |
|
1232 |
actions[f] = |
|
|
1233 | elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]): | |
|
1270 | actions[f] = ACTION_REMOVE, None, 'prompt same' | |
|
1271 | elif (m == ACTION_DELETED_CHANGED and f in ancestor | |
|
1272 | and not mctx[f].cmp(ancestor[f])): | |
|
1234 | 1273 | # remote did change but ended up with same content |
|
1235 | 1274 | del actions[f] # don't get = keep local deleted |
|
1236 | 1275 | |
@@ -1294,18 +1333,18 b' def calculateupdates(repo, wctx, mctx, a' | |||
|
1294 | 1333 | if all(a == l[0] for a in l[1:]): # len(bids) is > 1 |
|
1295 | 1334 | repo.ui.note(_(" %s: consensus for %s\n") % (f, m)) |
|
1296 | 1335 | actions[f] = l[0] |
|
1297 |
if m == |
|
|
1336 | if m == ACTION_DIR_RENAME_MOVE_LOCAL: | |
|
1298 | 1337 | dms.append(f) |
|
1299 | 1338 | continue |
|
1300 | 1339 | # If keep is an option, just do it. |
|
1301 |
if |
|
|
1340 | if ACTION_KEEP in bids: | |
|
1302 | 1341 | repo.ui.note(_(" %s: picking 'keep' action\n") % f) |
|
1303 |
actions[f] = bids[ |
|
|
1342 | actions[f] = bids[ACTION_KEEP][0] | |
|
1304 | 1343 | continue |
|
1305 | 1344 | # If there are gets and they all agree [how could they not?], do it. |
|
1306 |
if |
|
|
1307 |
ga0 = bids[ |
|
|
1308 |
if all(a == ga0 for a in bids[ |
|
|
1345 | if ACTION_GET in bids: | |
|
1346 | ga0 = bids[ACTION_GET][0] | |
|
1347 | if all(a == ga0 for a in bids[ACTION_GET][1:]): | |
|
1309 | 1348 | repo.ui.note(_(" %s: picking 'get' action\n") % f) |
|
1310 | 1349 | actions[f] = ga0 |
|
1311 | 1350 | continue |
@@ -1320,14 +1359,14 b' def calculateupdates(repo, wctx, mctx, a' | |||
|
1320 | 1359 | repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') % |
|
1321 | 1360 | (f, m)) |
|
1322 | 1361 | actions[f] = l[0] |
|
1323 | if m == 'dm': | |
|
1362 | if m == ACTION_DIR_RENAME_MOVE_LOCAL: | |
|
1324 | 1363 | dms.append(f) |
|
1325 | 1364 | continue |
|
1326 | 1365 | # Work around 'dm' that can cause multiple actions for the same file |
|
1327 | 1366 | for f in dms: |
|
1328 | 1367 | dm, (f0, flags), msg = actions[f] |
|
1329 |
assert dm == |
|
|
1330 |
if f0 in actions and actions[f0][0] == |
|
|
1368 | assert dm == ACTION_DIR_RENAME_MOVE_LOCAL, dm | |
|
1369 | if f0 in actions and actions[f0][0] == ACTION_REMOVE: | |
|
1331 | 1370 | # We have one bid for removing a file and another for moving it. |
|
1332 | 1371 | # These two could be merged as first move and then delete ... |
|
1333 | 1372 | # but instead drop moving and just delete. |
@@ -1432,7 +1471,8 b' def _prefetchfiles(repo, ctx, actions):' | |||
|
1432 | 1471 | # Skipping 'a', 'am', 'f', 'r', 'dm', 'e', 'k', 'p' and 'pr', because they |
|
1433 | 1472 | # don't touch the context to be merged in. 'cd' is skipped, because |
|
1434 | 1473 | # changed/deleted never resolves to something from the remote side. |
|
1435 | oplist = [actions[a] for a in 'g dc dg m'.split()] | |
|
1474 | oplist = [actions[a] for a in (ACTION_GET, ACTION_DELETED_CHANGED, | |
|
1475 | ACTION_LOCAL_DIR_RENAME_GET, ACTION_MERGE)] | |
|
1436 | 1476 | prefetch = scmutil.fileprefetchhooks |
|
1437 | 1477 | prefetch(repo, ctx, [f for sublist in oplist for f, args, msg in sublist]) |
|
1438 | 1478 | |
@@ -1479,9 +1519,9 b' def applyupdates(repo, actions, wctx, mc' | |||
|
1479 | 1519 | l.sort() |
|
1480 | 1520 | |
|
1481 | 1521 | # 'cd' and 'dc' actions are treated like other merge conflicts |
|
1482 |
mergeactions = sorted(actions[ |
|
|
1483 |
mergeactions.extend(sorted(actions[ |
|
|
1484 |
mergeactions.extend(actions[ |
|
|
1522 | mergeactions = sorted(actions[ACTION_CHANGED_DELETED]) | |
|
1523 | mergeactions.extend(sorted(actions[ACTION_DELETED_CHANGED])) | |
|
1524 | mergeactions.extend(actions[ACTION_MERGE]) | |
|
1485 | 1525 | for f, args, msg in mergeactions: |
|
1486 | 1526 | f1, f2, fa, move, anc = args |
|
1487 | 1527 | if f == '.hgsubstate': # merged internally |
@@ -1516,14 +1556,15 b' def applyupdates(repo, actions, wctx, mc' | |||
|
1516 | 1556 | wctx[f].audit() |
|
1517 | 1557 | wctx[f].remove() |
|
1518 | 1558 | |
|
1519 |
numupdates = sum(len(l) for m, l in actions.items() |
|
|
1559 | numupdates = sum(len(l) for m, l in actions.items() | |
|
1560 | if m != ACTION_KEEP) | |
|
1520 | 1561 | z = 0 |
|
1521 | 1562 | |
|
1522 |
if [a for a in actions[ |
|
|
1563 | if [a for a in actions[ACTION_REMOVE] if a[0] == '.hgsubstate']: | |
|
1523 | 1564 | subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels) |
|
1524 | 1565 | |
|
1525 | 1566 | # record path conflicts |
|
1526 |
for f, args, msg in actions[ |
|
|
1567 | for f, args, msg in actions[ACTION_PATH_CONFLICT]: | |
|
1527 | 1568 | f1, fo = args |
|
1528 | 1569 | s = repo.ui.status |
|
1529 | 1570 | s(_("%s: path conflict - a file or link has the same name as a " |
@@ -1543,14 +1584,14 b' def applyupdates(repo, actions, wctx, mc' | |||
|
1543 | 1584 | |
|
1544 | 1585 | # remove in parallel (must come before resolving path conflicts and getting) |
|
1545 | 1586 | prog = worker.worker(repo.ui, cost, batchremove, (repo, wctx), |
|
1546 |
actions[ |
|
|
1587 | actions[ACTION_REMOVE]) | |
|
1547 | 1588 | for i, item in prog: |
|
1548 | 1589 | z += i |
|
1549 | 1590 | progress(_updating, z, item=item, total=numupdates, unit=_files) |
|
1550 |
removed = len(actions[ |
|
|
1591 | removed = len(actions[ACTION_REMOVE]) | |
|
1551 | 1592 | |
|
1552 | 1593 | # resolve path conflicts (must come before getting) |
|
1553 |
for f, args, msg in actions[ |
|
|
1594 | for f, args, msg in actions[ACTION_PATH_CONFLICT_RESOLVE]: | |
|
1554 | 1595 | repo.ui.debug(" %s: %s -> pr\n" % (f, msg)) |
|
1555 | 1596 | f0, = args |
|
1556 | 1597 | if wctx[f0].lexists(): |
@@ -1563,40 +1604,40 b' def applyupdates(repo, actions, wctx, mc' | |||
|
1563 | 1604 | |
|
1564 | 1605 | # get in parallel |
|
1565 | 1606 | prog = worker.worker(repo.ui, cost, batchget, (repo, mctx, wctx), |
|
1566 |
actions[ |
|
|
1607 | actions[ACTION_GET]) | |
|
1567 | 1608 | for i, item in prog: |
|
1568 | 1609 | z += i |
|
1569 | 1610 | progress(_updating, z, item=item, total=numupdates, unit=_files) |
|
1570 |
updated = len(actions[ |
|
|
1611 | updated = len(actions[ACTION_GET]) | |
|
1571 | 1612 | |
|
1572 |
if [a for a in actions[ |
|
|
1613 | if [a for a in actions[ACTION_GET] if a[0] == '.hgsubstate']: | |
|
1573 | 1614 | subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels) |
|
1574 | 1615 | |
|
1575 | 1616 | # forget (manifest only, just log it) (must come first) |
|
1576 |
for f, args, msg in actions[ |
|
|
1617 | for f, args, msg in actions[ACTION_FORGET]: | |
|
1577 | 1618 | repo.ui.debug(" %s: %s -> f\n" % (f, msg)) |
|
1578 | 1619 | z += 1 |
|
1579 | 1620 | progress(_updating, z, item=f, total=numupdates, unit=_files) |
|
1580 | 1621 | |
|
1581 | 1622 | # re-add (manifest only, just log it) |
|
1582 |
for f, args, msg in actions[ |
|
|
1623 | for f, args, msg in actions[ACTION_ADD]: | |
|
1583 | 1624 | repo.ui.debug(" %s: %s -> a\n" % (f, msg)) |
|
1584 | 1625 | z += 1 |
|
1585 | 1626 | progress(_updating, z, item=f, total=numupdates, unit=_files) |
|
1586 | 1627 | |
|
1587 | 1628 | # re-add/mark as modified (manifest only, just log it) |
|
1588 |
for f, args, msg in actions[ |
|
|
1629 | for f, args, msg in actions[ACTION_ADD_MODIFIED]: | |
|
1589 | 1630 | repo.ui.debug(" %s: %s -> am\n" % (f, msg)) |
|
1590 | 1631 | z += 1 |
|
1591 | 1632 | progress(_updating, z, item=f, total=numupdates, unit=_files) |
|
1592 | 1633 | |
|
1593 | 1634 | # keep (noop, just log it) |
|
1594 |
for f, args, msg in actions[ |
|
|
1635 | for f, args, msg in actions[ACTION_KEEP]: | |
|
1595 | 1636 | repo.ui.debug(" %s: %s -> k\n" % (f, msg)) |
|
1596 | 1637 | # no progress |
|
1597 | 1638 | |
|
1598 | 1639 | # directory rename, move local |
|
1599 |
for f, args, msg in actions[ |
|
|
1640 | for f, args, msg in actions[ACTION_DIR_RENAME_MOVE_LOCAL]: | |
|
1600 | 1641 | repo.ui.debug(" %s: %s -> dm\n" % (f, msg)) |
|
1601 | 1642 | z += 1 |
|
1602 | 1643 | progress(_updating, z, item=f, total=numupdates, unit=_files) |
@@ -1608,7 +1649,7 b' def applyupdates(repo, actions, wctx, mc' | |||
|
1608 | 1649 | updated += 1 |
|
1609 | 1650 | |
|
1610 | 1651 | # local directory rename, get |
|
1611 |
for f, args, msg in actions[ |
|
|
1652 | for f, args, msg in actions[ACTION_LOCAL_DIR_RENAME_GET]: | |
|
1612 | 1653 | repo.ui.debug(" %s: %s -> dg\n" % (f, msg)) |
|
1613 | 1654 | z += 1 |
|
1614 | 1655 | progress(_updating, z, item=f, total=numupdates, unit=_files) |
@@ -1618,7 +1659,7 b' def applyupdates(repo, actions, wctx, mc' | |||
|
1618 | 1659 | updated += 1 |
|
1619 | 1660 | |
|
1620 | 1661 | # exec |
|
1621 |
for f, args, msg in actions[ |
|
|
1662 | for f, args, msg in actions[ACTION_EXEC]: | |
|
1622 | 1663 | repo.ui.debug(" %s: %s -> e\n" % (f, msg)) |
|
1623 | 1664 | z += 1 |
|
1624 | 1665 | progress(_updating, z, item=f, total=numupdates, unit=_files) |
@@ -1696,17 +1737,17 b' def applyupdates(repo, actions, wctx, mc' | |||
|
1696 | 1737 | |
|
1697 | 1738 | extraactions = ms.actions() |
|
1698 | 1739 | if extraactions: |
|
1699 |
mfiles = set(a[0] for a in actions[ |
|
|
1740 | mfiles = set(a[0] for a in actions[ACTION_MERGE]) | |
|
1700 | 1741 | for k, acts in extraactions.iteritems(): |
|
1701 | 1742 | actions[k].extend(acts) |
|
1702 |
# Remove these files from actions[ |
|
|
1703 |
# because in recordupdates, files in actions[ |
|
|
1704 |
# after files in other actions, and the merge driver |
|
|
1705 |
# files to those actions via extraactions above. This can |
|
|
1706 |
# file being recorded twice, with poor results. This is |
|
|
1707 |
# problematic for actions[ |
|
|
1708 |
# merge driver in the initial merge process; |
|
|
1709 | # don't go through this flow). | |
|
1743 | # Remove these files from actions[ACTION_MERGE] as well. This is | |
|
1744 | # important because in recordupdates, files in actions[ACTION_MERGE] | |
|
1745 | # are processed after files in other actions, and the merge driver | |
|
1746 | # might add files to those actions via extraactions above. This can | |
|
1747 | # lead to a file being recorded twice, with poor results. This is | |
|
1748 | # especially problematic for actions[ACTION_REMOVE] (currently only | |
|
1749 | # possible with the merge driver in the initial merge process; | |
|
1750 | # interrupted merges don't go through this flow). | |
|
1710 | 1751 | # |
|
1711 | 1752 | # The real fix here is to have indexes by both file and action so |
|
1712 | 1753 | # that when the action for a file is changed it is automatically |
@@ -1717,7 +1758,8 b' def applyupdates(repo, actions, wctx, mc' | |||
|
1717 | 1758 | # those lists aren't consulted again. |
|
1718 | 1759 | mfiles.difference_update(a[0] for a in acts) |
|
1719 | 1760 | |
|
1720 |
actions[ |
|
|
1761 | actions[ACTION_MERGE] = [a for a in actions[ACTION_MERGE] | |
|
1762 | if a[0] in mfiles] | |
|
1721 | 1763 | |
|
1722 | 1764 | progress(_updating, None, total=numupdates, unit=_files) |
|
1723 | 1765 | return updateresult(updated, merged, removed, unresolved) |
@@ -1725,18 +1767,18 b' def applyupdates(repo, actions, wctx, mc' | |||
|
1725 | 1767 | def recordupdates(repo, actions, branchmerge): |
|
1726 | 1768 | "record merge actions to the dirstate" |
|
1727 | 1769 | # remove (must come first) |
|
1728 |
for f, args, msg in actions.get( |
|
|
1770 | for f, args, msg in actions.get(ACTION_REMOVE, []): | |
|
1729 | 1771 | if branchmerge: |
|
1730 | 1772 | repo.dirstate.remove(f) |
|
1731 | 1773 | else: |
|
1732 | 1774 | repo.dirstate.drop(f) |
|
1733 | 1775 | |
|
1734 | 1776 | # forget (must come first) |
|
1735 |
for f, args, msg in actions.get( |
|
|
1777 | for f, args, msg in actions.get(ACTION_FORGET, []): | |
|
1736 | 1778 | repo.dirstate.drop(f) |
|
1737 | 1779 | |
|
1738 | 1780 | # resolve path conflicts |
|
1739 |
for f, args, msg in actions.get( |
|
|
1781 | for f, args, msg in actions.get(ACTION_PATH_CONFLICT_RESOLVE, []): | |
|
1740 | 1782 | f0, = args |
|
1741 | 1783 | origf0 = repo.dirstate.copied(f0) or f0 |
|
1742 | 1784 | repo.dirstate.add(f) |
@@ -1747,33 +1789,33 b' def recordupdates(repo, actions, branchm' | |||
|
1747 | 1789 | repo.dirstate.drop(f0) |
|
1748 | 1790 | |
|
1749 | 1791 | # re-add |
|
1750 |
for f, args, msg in actions.get( |
|
|
1792 | for f, args, msg in actions.get(ACTION_ADD, []): | |
|
1751 | 1793 | repo.dirstate.add(f) |
|
1752 | 1794 | |
|
1753 | 1795 | # re-add/mark as modified |
|
1754 |
for f, args, msg in actions.get( |
|
|
1796 | for f, args, msg in actions.get(ACTION_ADD_MODIFIED, []): | |
|
1755 | 1797 | if branchmerge: |
|
1756 | 1798 | repo.dirstate.normallookup(f) |
|
1757 | 1799 | else: |
|
1758 | 1800 | repo.dirstate.add(f) |
|
1759 | 1801 | |
|
1760 | 1802 | # exec change |
|
1761 |
for f, args, msg in actions.get( |
|
|
1803 | for f, args, msg in actions.get(ACTION_EXEC, []): | |
|
1762 | 1804 | repo.dirstate.normallookup(f) |
|
1763 | 1805 | |
|
1764 | 1806 | # keep |
|
1765 |
for f, args, msg in actions.get( |
|
|
1807 | for f, args, msg in actions.get(ACTION_KEEP, []): | |
|
1766 | 1808 | pass |
|
1767 | 1809 | |
|
1768 | 1810 | # get |
|
1769 |
for f, args, msg in actions.get( |
|
|
1811 | for f, args, msg in actions.get(ACTION_GET, []): | |
|
1770 | 1812 | if branchmerge: |
|
1771 | 1813 | repo.dirstate.otherparent(f) |
|
1772 | 1814 | else: |
|
1773 | 1815 | repo.dirstate.normal(f) |
|
1774 | 1816 | |
|
1775 | 1817 | # merge |
|
1776 |
for f, args, msg in actions.get( |
|
|
1818 | for f, args, msg in actions.get(ACTION_MERGE, []): | |
|
1777 | 1819 | f1, f2, fa, move, anc = args |
|
1778 | 1820 | if branchmerge: |
|
1779 | 1821 | # We've done a branch merge, mark this file as merged |
@@ -1798,7 +1840,7 b' def recordupdates(repo, actions, branchm' | |||
|
1798 | 1840 | repo.dirstate.drop(f1) |
|
1799 | 1841 | |
|
1800 | 1842 | # directory rename, move local |
|
1801 |
for f, args, msg in actions.get( |
|
|
1843 | for f, args, msg in actions.get(ACTION_DIR_RENAME_MOVE_LOCAL, []): | |
|
1802 | 1844 | f0, flag = args |
|
1803 | 1845 | if branchmerge: |
|
1804 | 1846 | repo.dirstate.add(f) |
@@ -1809,7 +1851,7 b' def recordupdates(repo, actions, branchm' | |||
|
1809 | 1851 | repo.dirstate.drop(f0) |
|
1810 | 1852 | |
|
1811 | 1853 | # directory rename, get |
|
1812 |
for f, args, msg in actions.get( |
|
|
1854 | for f, args, msg in actions.get(ACTION_LOCAL_DIR_RENAME_GET, []): | |
|
1813 | 1855 | f0, flag = args |
|
1814 | 1856 | if branchmerge: |
|
1815 | 1857 | repo.dirstate.add(f) |
@@ -1982,7 +2024,8 b' def update(repo, node, branchmerge, forc' | |||
|
1982 | 2024 | |
|
1983 | 2025 | if updatecheck == 'noconflict': |
|
1984 | 2026 | for f, (m, args, msg) in actionbyfile.iteritems(): |
|
1985 | if m not in ('g', 'k', 'e', 'r', 'pr'): | |
|
2027 | if m not in (ACTION_GET, ACTION_KEEP, ACTION_EXEC, | |
|
2028 | ACTION_REMOVE, ACTION_PATH_CONFLICT_RESOLVE): | |
|
1986 | 2029 | msg = _("conflicting changes") |
|
1987 | 2030 | hint = _("commit or update --clean to discard changes") |
|
1988 | 2031 | raise error.Abort(msg, hint=hint) |
@@ -1995,30 +2038,45 b' def update(repo, node, branchmerge, forc' | |||
|
1995 | 2038 | m, args, msg = actionbyfile[f] |
|
1996 | 2039 | prompts = filemerge.partextras(labels) |
|
1997 | 2040 | prompts['f'] = f |
|
1998 |
if m == |
|
|
2041 | if m == ACTION_CHANGED_DELETED: | |
|
1999 | 2042 | if repo.ui.promptchoice( |
|
2000 | 2043 | _("local%(l)s changed %(f)s which other%(o)s deleted\n" |
|
2001 | 2044 | "use (c)hanged version or (d)elete?" |
|
2002 | 2045 | "$$ &Changed $$ &Delete") % prompts, 0): |
|
2003 |
actionbyfile[f] = ( |
|
|
2046 | actionbyfile[f] = (ACTION_REMOVE, None, 'prompt delete') | |
|
2004 | 2047 | elif f in p1: |
|
2005 |
actionbyfile[f] = ( |
|
|
2048 | actionbyfile[f] = (ACTION_ADD_MODIFIED, None, 'prompt keep') | |
|
2006 | 2049 | else: |
|
2007 |
actionbyfile[f] = ( |
|
|
2008 |
elif m == |
|
|
2050 | actionbyfile[f] = (ACTION_ADD, None, 'prompt keep') | |
|
2051 | elif m == ACTION_DELETED_CHANGED: | |
|
2009 | 2052 | f1, f2, fa, move, anc = args |
|
2010 | 2053 | flags = p2[f2].flags() |
|
2011 | 2054 | if repo.ui.promptchoice( |
|
2012 | 2055 | _("other%(o)s changed %(f)s which local%(l)s deleted\n" |
|
2013 | 2056 | "use (c)hanged version or leave (d)eleted?" |
|
2014 | 2057 | "$$ &Changed $$ &Deleted") % prompts, 0) == 0: |
|
2015 |
actionbyfile[f] = ( |
|
|
2058 | actionbyfile[f] = (ACTION_GET, (flags, False), | |
|
2059 | 'prompt recreating') | |
|
2016 | 2060 | else: |
|
2017 | 2061 | del actionbyfile[f] |
|
2018 | 2062 | |
|
2019 | 2063 | # Convert to dictionary-of-lists format |
|
2020 | 2064 | actions = dict((m, []) |
|
2021 |
for m in |
|
|
2065 | for m in ( | |
|
2066 | ACTION_ADD, | |
|
2067 | ACTION_ADD_MODIFIED, | |
|
2068 | ACTION_FORGET, | |
|
2069 | ACTION_GET, | |
|
2070 | ACTION_CHANGED_DELETED, | |
|
2071 | ACTION_DELETED_CHANGED, | |
|
2072 | ACTION_REMOVE, | |
|
2073 | ACTION_DIR_RENAME_MOVE_LOCAL, | |
|
2074 | ACTION_LOCAL_DIR_RENAME_GET, | |
|
2075 | ACTION_MERGE, | |
|
2076 | ACTION_EXEC, | |
|
2077 | ACTION_KEEP, | |
|
2078 | ACTION_PATH_CONFLICT, | |
|
2079 | ACTION_PATH_CONFLICT_RESOLVE)) | |
|
2022 | 2080 | for f, (m, args, msg) in actionbyfile.iteritems(): |
|
2023 | 2081 | if m not in actions: |
|
2024 | 2082 | actions[m] = [] |
@@ -2081,7 +2139,7 b' def update(repo, node, branchmerge, forc' | |||
|
2081 | 2139 | if (fsmonitorwarning |
|
2082 | 2140 | and not fsmonitorenabled |
|
2083 | 2141 | and p1.node() == nullid |
|
2084 |
and len(actions[ |
|
|
2142 | and len(actions[ACTION_GET]) >= fsmonitorthreshold | |
|
2085 | 2143 | and pycompat.sysplatform.startswith(('linux', 'darwin'))): |
|
2086 | 2144 | repo.ui.warn( |
|
2087 | 2145 | _('(warning: large working directory being used without ' |
General Comments 0
You need to be logged in to leave comments.
Login now