Show More
@@ -851,6 +851,107 b' def driverconclude(repo, ms, wctx, label' | |||||
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 |
|
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 | 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 | """ | |
@@ -1026,6 +1127,9 b' def manifestmerge(repo, wctx, p2, pa, br' | |||||
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): |
@@ -103,7 +103,8 b' attack back/test where back symlinks to ' | |||||
103 | back/test |
|
103 | back/test | |
104 | #if symlink |
|
104 | #if symlink | |
105 | $ hg update -Cr2 |
|
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 | [255] |
|
108 | [255] | |
108 | #else |
|
109 | #else | |
109 | ('back' will be a file and cause some other system specific error) |
|
110 | ('back' will be a file and cause some other system specific error) | |
@@ -160,8 +161,12 b' try trivial merge' | |||||
160 |
|
161 | |||
161 | $ hg up -qC 1 |
|
162 | $ hg up -qC 1 | |
162 | $ hg merge 2 |
|
163 | $ hg merge 2 | |
163 | abort: path 'a/poisoned' traverses symbolic link 'a' |
|
164 | a: path conflict - a file or link has the same name as a directory | |
164 | [255] |
|
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 | try rebase onto other revision: cache of audited paths should be discarded, |
|
171 | try rebase onto other revision: cache of audited paths should be discarded, | |
167 | and the rebase should fail (issue5628) |
|
172 | and the rebase should fail (issue5628) | |
@@ -169,8 +174,11 b' and the rebase should fail (issue5628)' | |||||
169 | $ hg up -qC 2 |
|
174 | $ hg up -qC 2 | |
170 | $ hg rebase -s 2 -d 1 --config extensions.rebase= |
|
175 | $ hg rebase -s 2 -d 1 --config extensions.rebase= | |
171 | rebasing 2:e73c21d6b244 "file a/poisoned" (tip) |
|
176 | rebasing 2:e73c21d6b244 "file a/poisoned" (tip) | |
172 | abort: path 'a/poisoned' traverses symbolic link 'a' |
|
177 | a: path conflict - a file or link has the same name as a directory | |
173 | [255] |
|
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 | $ ls ../merge-symlink-out |
|
182 | $ ls ../merge-symlink-out | |
175 |
|
183 | |||
176 | $ cd .. |
|
184 | $ cd .. | |
@@ -202,7 +210,8 b' try linear update where symlink already ' | |||||
202 |
|
210 | |||
203 | $ hg up -qC 0 |
|
211 | $ hg up -qC 0 | |
204 | $ hg up 1 |
|
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 | [255] |
|
215 | [255] | |
207 |
|
216 | |||
208 | try linear update including symlinked directory and its content: paths are |
|
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 | $ hg up -qC null |
|
221 | $ hg up -qC null | |
213 | $ hg up 1 |
|
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 | [255] |
|
225 | [255] | |
216 | $ ls ../update-symlink-out |
|
226 | $ ls ../update-symlink-out | |
217 |
|
227 | |||
@@ -222,7 +232,8 b' a symlink.' | |||||
222 | $ rm -f a |
|
232 | $ rm -f a | |
223 | $ hg up -qC 2 |
|
233 | $ hg up -qC 2 | |
224 | $ hg up 1 |
|
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 | [255] |
|
237 | [255] | |
227 | $ ls ../update-symlink-out |
|
238 | $ ls ../update-symlink-out | |
228 |
|
239 |
@@ -966,8 +966,12 b' and the merge should fail (issue5628)' | |||||
966 | *** runcommand up -qC 2 |
|
966 | *** runcommand up -qC 2 | |
967 | *** runcommand up -qC 1 |
|
967 | *** runcommand up -qC 1 | |
968 | *** runcommand merge 2 |
|
968 | *** runcommand merge 2 | |
969 | abort: path 'a/poisoned' traverses symbolic link 'a' |
|
969 | a: path conflict - a file or link has the same name as a directory | |
970 | [255] |
|
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 | $ ls ../merge-symlink-out |
|
975 | $ ls ../merge-symlink-out | |
972 |
|
976 | |||
973 | cache of repo.auditor should be discarded, so matcher would never traverse |
|
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 | $ hg bookmark -i |
|
25 | $ hg bookmark -i | |
26 | $ hg merge --verbose dir |
|
26 | $ hg merge --verbose dir | |
27 | resolving manifests |
|
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 | getting a/b |
|
32 | getting a/b | |
29 | abort: *: '$TESTTMP/repo/a/b' (glob) |
|
33 | 1 files updated, 0 files merged, 0 files removed, 1 files unresolved | |
30 | [255] |
|
34 | use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon | |
|
35 | [1] | |||
31 |
$ |
|
36 | $ hg update --clean . | |
32 |
|
|
37 | 1 files updated, 0 files merged, 1 files removed, 0 files unresolved | |
33 |
|
38 | |||
34 | Basic update - local directory conflicts with remote file |
|
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