comparison mercurial/copies.py @ 46673:ad30b29bc23d

copies: choose target directory based on longest match If one side of a merge renames `dir1/` to `dir2/` and the subdirectory `dir1/subdir1/` to `dir2/subdir2/`, and the other side of the merge adds a file in `dir1/subdir1/`, we should clearly move that into `dir2/subdir2/`. We already detect the directories correctly before this patch, but we iterate over them in arbitrary order. That results in the new file sometimes ending up in `dir2/subdir1/` instead. This patch fixes it by iterating over the source directories by visiting subdirectories first. That's achieved by simply iterating over them in reverse lexicographical order. Without the fix, the test case still passes on Python 2 but fails on Python 3. It depends on the iteration order of the dict. I did not look into how it's built up and why it behaved differently before the fix. I could probably have gotten it to fail on Python 2 as well by choosing different directory names. Differential Revision: https://phab.mercurial-scm.org/D10115
author Martin von Zweigbergk <martinvonz@google.com>
date Thu, 04 Mar 2021 16:06:55 -0800
parents 47557ea79fc7
children eca88f5fbcb2
comparison
equal deleted inserted replaced
46672:7015b0232c5e 46673:ad30b29bc23d
1094 for d in dirmove: 1094 for d in dirmove:
1095 repo.ui.debug( 1095 repo.ui.debug(
1096 b" discovered dir src: '%s' -> dst: '%s'\n" % (d, dirmove[d]) 1096 b" discovered dir src: '%s' -> dst: '%s'\n" % (d, dirmove[d])
1097 ) 1097 )
1098 1098
1099 # Sort the directories in reverse order, so we find children first
1100 # For example, if dir1/ was renamed to dir2/, and dir1/subdir1/
1101 # was renamed to dir2/subdir2/, we want to move dir1/subdir1/file
1102 # to dir2/subdir2/file (not dir2/subdir1/file)
1103 dirmove_children_first = sorted(dirmove, reverse=True)
1104
1099 movewithdir = {} 1105 movewithdir = {}
1100 # check unaccounted nonoverlapping files against directory moves 1106 # check unaccounted nonoverlapping files against directory moves
1101 for f in addedfilesfn(): 1107 for f in addedfilesfn():
1102 if f not in fullcopy: 1108 if f not in fullcopy:
1103 for d in dirmove: 1109 for d in dirmove_children_first:
1104 if f.startswith(d): 1110 if f.startswith(d):
1105 # new file added in a directory that was moved, move it 1111 # new file added in a directory that was moved, move it
1106 df = dirmove[d] + f[len(d) :] 1112 df = dirmove[d] + f[len(d) :]
1107 if df not in copy: 1113 if df not in copy:
1108 movewithdir[f] = df 1114 movewithdir[f] = df