848 def driverconclude(repo, ms, wctx, labels=None): |
848 def driverconclude(repo, ms, wctx, labels=None): |
849 """run the conclude step of the merge driver, if any |
849 """run the conclude step of the merge driver, if any |
850 |
850 |
851 This is currently not implemented -- it's an extension point.""" |
851 This is currently not implemented -- it's an extension point.""" |
852 return True |
852 return True |
|
853 |
|
854 def _filesindirs(repo, manifest, dirs): |
|
855 """ |
|
856 Generator that yields pairs of all the files in the manifest that are found |
|
857 inside the directories listed in dirs, and which directory they are found |
|
858 in. |
|
859 """ |
|
860 for f in manifest: |
|
861 for p in util.finddirs(f): |
|
862 if p in dirs: |
|
863 yield f, p |
|
864 break |
|
865 |
|
866 def checkpathconflicts(repo, wctx, mctx, actions): |
|
867 """ |
|
868 Check if any actions introduce path conflicts in the repository, updating |
|
869 actions to record or handle the path conflict accordingly. |
|
870 """ |
|
871 mf = wctx.manifest() |
|
872 |
|
873 # The set of local files that conflict with a remote directory. |
|
874 localconflicts = set() |
|
875 |
|
876 # The set of directories that conflict with a remote file, and so may cause |
|
877 # conflicts if they still contain any files after the merge. |
|
878 remoteconflicts = set() |
|
879 |
|
880 # The set of directories that appear as both a file and a directory in the |
|
881 # remote manifest. These indicate an invalid remote manifest, which |
|
882 # can't be updated to cleanly. |
|
883 invalidconflicts = set() |
|
884 |
|
885 # The set of files deleted by all the actions. |
|
886 deletedfiles = set() |
|
887 |
|
888 for f, (m, args, msg) in actions.items(): |
|
889 if m in ('c', 'dc', 'm', 'cm'): |
|
890 # This action may create a new local file. |
|
891 if mf.hasdir(f): |
|
892 # The file aliases a local directory. This might be ok if all |
|
893 # the files in the local directory are being deleted. This |
|
894 # will be checked once we know what all the deleted files are. |
|
895 remoteconflicts.add(f) |
|
896 for p in util.finddirs(f): |
|
897 if p in mf: |
|
898 if p in mctx: |
|
899 # The file is in a directory which aliases both a local |
|
900 # and a remote file. This is an internal inconsistency |
|
901 # within the remote manifest. |
|
902 invalidconflicts.add(p) |
|
903 else: |
|
904 # The file is in a directory which aliases a local file. |
|
905 # We will need to rename the local file. |
|
906 localconflicts.add(p) |
|
907 if p in actions and actions[p][0] in ('c', 'dc', 'm', 'cm'): |
|
908 # The file is in a directory which aliases a remote file. |
|
909 # This is an internal inconsistency within the remote |
|
910 # manifest. |
|
911 invalidconflicts.add(p) |
|
912 |
|
913 # Track the names of all deleted files. |
|
914 if m == 'r': |
|
915 deletedfiles.add(f) |
|
916 if m == 'm': |
|
917 f1, f2, fa, move, anc = args |
|
918 if move: |
|
919 deletedfiles.add(f1) |
|
920 if m == 'dm': |
|
921 f2, flags = args |
|
922 deletedfiles.add(f2) |
|
923 |
|
924 # Rename all local conflicting files that have not been deleted. |
|
925 for p in localconflicts: |
|
926 if p not in deletedfiles: |
|
927 ctxname = str(wctx).rstrip('+') |
|
928 pnew = util.safename(p, ctxname, wctx, set(actions.keys())) |
|
929 actions[pnew] = ('pr', (p,), "local path conflict") |
|
930 actions[p] = ('p', (pnew, 'l'), "path conflict") |
|
931 |
|
932 if remoteconflicts: |
|
933 # Check if all files in the conflicting directories have been removed. |
|
934 ctxname = str(mctx).rstrip('+') |
|
935 for f, p in _filesindirs(repo, mf, remoteconflicts): |
|
936 if f not in deletedfiles: |
|
937 m, args, msg = actions[p] |
|
938 pnew = util.safename(p, ctxname, wctx, set(actions.keys())) |
|
939 if m in ('dc', 'm'): |
|
940 # Action was merge, just update target. |
|
941 actions[pnew] = (m, args, msg) |
|
942 else: |
|
943 # Action was create, change to renamed get action. |
|
944 fl = args[0] |
|
945 actions[pnew] = ('dg', (p, fl), "remote path conflict") |
|
946 actions[p] = ('p', (pnew, 'r'), "path conflict") |
|
947 remoteconflicts.remove(p) |
|
948 break |
|
949 |
|
950 if invalidconflicts: |
|
951 for p in invalidconflicts: |
|
952 repo.ui.warn(_("%s: is both a file and a directory\n") % p) |
|
953 raise error.Abort(_("destination manifest contains path conflicts")) |
853 |
954 |
854 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher, |
955 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher, |
855 acceptremote, followcopies, forcefulldiff=False): |
956 acceptremote, followcopies, forcefulldiff=False): |
856 """ |
957 """ |
857 Merge wctx and p2 with ancestor pa and generate merge action list |
958 Merge wctx and p2 with ancestor pa and generate merge action list |
1024 actions[f] = ('c', (fl2,), "remote recreating") |
1125 actions[f] = ('c', (fl2,), "remote recreating") |
1025 else: |
1126 else: |
1026 actions[f] = ('dc', (None, f, f, False, pa.node()), |
1127 actions[f] = ('dc', (None, f, f, False, pa.node()), |
1027 "prompt deleted/changed") |
1128 "prompt deleted/changed") |
1028 |
1129 |
|
1130 # If we are merging, look for path conflicts. |
|
1131 checkpathconflicts(repo, wctx, p2, actions) |
|
1132 |
1029 return actions, diverge, renamedelete |
1133 return actions, diverge, renamedelete |
1030 |
1134 |
1031 def _resolvetrivial(repo, wctx, mctx, ancestor, actions): |
1135 def _resolvetrivial(repo, wctx, mctx, ancestor, actions): |
1032 """Resolves false conflicts where the nodeid changed but the content |
1136 """Resolves false conflicts where the nodeid changed but the content |
1033 remained the same.""" |
1137 remained the same.""" |