Show More
@@ -851,6 +851,107 b' def driverconclude(repo, ms, wctx, label' | |||
|
851 | 851 | This is currently not implemented -- it's an extension point.""" |
|
852 | 852 | return True |
|
853 | 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")) | |
|
954 | ||
|
854 | 955 | def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher, |
|
855 | 956 | acceptremote, followcopies, forcefulldiff=False): |
|
856 | 957 | """ |
@@ -1026,6 +1127,9 b' def manifestmerge(repo, wctx, p2, pa, br' | |||
|
1026 | 1127 | actions[f] = ('dc', (None, f, f, False, pa.node()), |
|
1027 | 1128 | "prompt deleted/changed") |
|
1028 | 1129 | |
|
1130 | # If we are merging, look for path conflicts. | |
|
1131 | checkpathconflicts(repo, wctx, p2, actions) | |
|
1132 | ||
|
1029 | 1133 | return actions, diverge, renamedelete |
|
1030 | 1134 | |
|
1031 | 1135 | def _resolvetrivial(repo, wctx, mctx, ancestor, actions): |
@@ -103,7 +103,8 b' attack back/test where back symlinks to ' | |||
|
103 | 103 | back/test |
|
104 | 104 | #if symlink |
|
105 | 105 | $ hg update -Cr2 |
|
106 | abort: path 'back/test' traverses symbolic link 'back' | |
|
106 | back: is both a file and a directory | |
|
107 | abort: destination manifest contains path conflicts | |
|
107 | 108 | [255] |
|
108 | 109 | #else |
|
109 | 110 | ('back' will be a file and cause some other system specific error) |
@@ -160,8 +161,12 b' try trivial merge' | |||
|
160 | 161 | |
|
161 | 162 | $ hg up -qC 1 |
|
162 | 163 | $ hg merge 2 |
|
163 | abort: path 'a/poisoned' traverses symbolic link 'a' | |
|
164 | [255] | |
|
164 | a: path conflict - a file or link has the same name as a directory | |
|
165 | the local file has been renamed to a~aa04623eb0c3 | |
|
166 | resolve manually then use 'hg resolve --mark a' | |
|
167 | 1 files updated, 0 files merged, 0 files removed, 1 files unresolved | |
|
168 | use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon | |
|
169 | [1] | |
|
165 | 170 | |
|
166 | 171 | try rebase onto other revision: cache of audited paths should be discarded, |
|
167 | 172 | and the rebase should fail (issue5628) |
@@ -169,8 +174,11 b' and the rebase should fail (issue5628)' | |||
|
169 | 174 | $ hg up -qC 2 |
|
170 | 175 | $ hg rebase -s 2 -d 1 --config extensions.rebase= |
|
171 | 176 | rebasing 2:e73c21d6b244 "file a/poisoned" (tip) |
|
172 | abort: path 'a/poisoned' traverses symbolic link 'a' | |
|
173 | [255] | |
|
177 | a: path conflict - a file or link has the same name as a directory | |
|
178 | the local file has been renamed to a~aa04623eb0c3 | |
|
179 | resolve manually then use 'hg resolve --mark a' | |
|
180 | unresolved conflicts (see hg resolve, then hg rebase --continue) | |
|
181 | [1] | |
|
174 | 182 | $ ls ../merge-symlink-out |
|
175 | 183 | |
|
176 | 184 | $ cd .. |
@@ -202,7 +210,8 b' try linear update where symlink already ' | |||
|
202 | 210 | |
|
203 | 211 | $ hg up -qC 0 |
|
204 | 212 | $ hg up 1 |
|
205 | abort: path 'a/b' traverses symbolic link 'a' | |
|
213 | a: is both a file and a directory | |
|
214 | abort: destination manifest contains path conflicts | |
|
206 | 215 | [255] |
|
207 | 216 | |
|
208 | 217 | try linear update including symlinked directory and its content: paths are |
@@ -211,7 +220,8 b' audited first by calculateupdates(), whe' | |||
|
211 | 220 | |
|
212 | 221 | $ hg up -qC null |
|
213 | 222 | $ hg up 1 |
|
214 | abort: path 'a/b' traverses symbolic link 'a' | |
|
223 | a: is both a file and a directory | |
|
224 | abort: destination manifest contains path conflicts | |
|
215 | 225 | [255] |
|
216 | 226 | $ ls ../update-symlink-out |
|
217 | 227 | |
@@ -222,7 +232,8 b' a symlink.' | |||
|
222 | 232 | $ rm -f a |
|
223 | 233 | $ hg up -qC 2 |
|
224 | 234 | $ hg up 1 |
|
225 | abort: path 'a/b' traverses symbolic link 'a' | |
|
235 | a: is both a file and a directory | |
|
236 | abort: destination manifest contains path conflicts | |
|
226 | 237 | [255] |
|
227 | 238 | $ ls ../update-symlink-out |
|
228 | 239 |
@@ -966,8 +966,12 b' and the merge should fail (issue5628)' | |||
|
966 | 966 | *** runcommand up -qC 2 |
|
967 | 967 | *** runcommand up -qC 1 |
|
968 | 968 | *** runcommand merge 2 |
|
969 | abort: path 'a/poisoned' traverses symbolic link 'a' | |
|
970 | [255] | |
|
969 | a: path conflict - a file or link has the same name as a directory | |
|
970 | the local file has been renamed to a~aa04623eb0c3 | |
|
971 | resolve manually then use 'hg resolve --mark a' | |
|
972 | 1 files updated, 0 files merged, 0 files removed, 1 files unresolved | |
|
973 | use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon | |
|
974 | [1] | |
|
971 | 975 | $ ls ../merge-symlink-out |
|
972 | 976 | |
|
973 | 977 | cache of repo.auditor should be discarded, so matcher would never traverse |
@@ -25,11 +25,16 b' Basic merge - local file conflicts with ' | |||
|
25 | 25 | $ hg bookmark -i |
|
26 | 26 | $ hg merge --verbose dir |
|
27 | 27 | resolving manifests |
|
28 | a: path conflict - a file or link has the same name as a directory | |
|
29 | the local file has been renamed to a~853701544ac3 | |
|
30 | resolve manually then use 'hg resolve --mark a' | |
|
31 | moving a to a~853701544ac3 | |
|
28 | 32 | getting a/b |
|
29 | abort: *: '$TESTTMP/repo/a/b' (glob) | |
|
30 | [255] | |
|
33 | 1 files updated, 0 files merged, 0 files removed, 1 files unresolved | |
|
34 | use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon | |
|
35 | [1] | |
|
31 | 36 |
$ |
|
32 |
|
|
|
37 | 1 files updated, 0 files merged, 1 files removed, 0 files unresolved | |
|
33 | 38 | |
|
34 | 39 | Basic update - local directory conflicts with remote file |
|
35 | 40 |
General Comments 0
You need to be logged in to leave comments.
Login now