Git fork
at reftables-rust 118 lines 5.2 kB view raw
1Directory rename detection 2========================== 3 4Rename detection logic in diffcore-rename that checks for renames of 5individual files is also aggregated there and then analyzed in either 6merge-ort or merge-recursive for cases where combinations of renames 7indicate that a full directory has been renamed. 8 9Scope of abilities 10------------------ 11 12It is perhaps easiest to start with an example: 13 14 * When all of x/a, x/b and x/c have moved to z/a, z/b and z/c, it is 15 likely that x/d added in the meantime would also want to move to z/d by 16 taking the hint that the entire directory 'x' moved to 'z'. 17 18More interesting possibilities exist, though, such as: 19 20 * one side of history renames x -> z, and the other renames some file to 21 x/e, causing the need for the merge to do a transitive rename so that 22 the rename ends up at z/e. 23 24 * one side of history renames x -> z, but also renames all files within x. 25 For example, x/a -> z/alpha, x/b -> z/bravo, etc. 26 27 * both 'x' and 'y' being merged into a single directory 'z', with a 28 directory rename being detected for both x->z and y->z. 29 30 * not all files in a directory being renamed to the same location; 31 i.e. perhaps most the files in 'x' are now found under 'z', but a few 32 are found under 'w'. 33 34 * a directory being renamed, which also contained a subdirectory that was 35 renamed to some entirely different location. (And perhaps the inner 36 directory itself contained inner directories that were renamed to yet 37 other locations). 38 39 * combinations of the above; see t/t6423-merge-rename-directories.sh for 40 various interesting cases. 41 42Limitations -- applicability of directory renames 43------------------------------------------------- 44 45In order to prevent edge and corner cases resulting in either conflicts 46that cannot be represented in the index or which might be too complex for 47users to try to understand and resolve, a couple basic rules limit when 48directory rename detection applies: 49 50 1) If a given directory still exists on both sides of a merge, we do 51 not consider it to have been renamed. 52 53 2) If a subset of to-be-renamed files have a file or directory in the 54 way (or would be in the way of each other), "turn off" the directory 55 rename for those specific sub-paths and report the conflict to the 56 user. 57 58 3) If the other side of history did a directory rename to a path that 59 your side of history renamed away, then ignore that particular 60 rename from the other side of history for any implicit directory 61 renames (but warn the user). 62 63Limitations -- detailed rules and testcases 64------------------------------------------- 65 66t/t6423-merge-rename-directories.sh contains extensive tests and commentary 67which generate and explore the rules listed above. It also lists a few 68additional rules: 69 70 a) If renames split a directory into two or more others, the directory 71 with the most renames, "wins". 72 73 b) Only apply implicit directory renames to directories if the other side 74 of history is the one doing the renaming. 75 76 c) Do not perform directory rename detection for directories which had no 77 new paths added to them. 78 79Limitations -- support in different commands 80-------------------------------------------- 81 82Directory rename detection is supported by 'merge' and 'cherry-pick'. 83Other git commands which users might be surprised to see limited or no 84directory rename detection support in: 85 86 * diff 87 88 Folks have requested in the past that `git diff` detect directory 89 renames and somehow simplify its output. It is not clear whether this 90 would be desirable or how the output should be simplified, so this was 91 simply not implemented. Also, while diffcore-rename has most of the 92 logic for detecting directory renames, some of the logic is still found 93 within merge-ort and merge-recursive. Fully supporting directory 94 rename detection in diffs would require copying or moving the remaining 95 bits of logic to the diff machinery. 96 97 * am 98 99 git-am tries to avoid a full three way merge, instead calling 100 git-apply. That prevents us from detecting renames at all, which may 101 defeat the directory rename detection. There is a fallback, though; if 102 the initial git-apply fails and the user has specified the -3 option, 103 git-am will fall back to a three way merge. However, git-am lacks the 104 necessary information to do a "real" three way merge. Instead, it has 105 to use build_fake_ancestor() to get a merge base that is missing files 106 whose rename may have been important to detect for directory rename 107 detection to function. 108 109 * rebase 110 111 Since am-based rebases work by first generating a bunch of patches 112 (which no longer record what the original commits were and thus don't 113 have the necessary info from which we can find a real merge-base), and 114 then calling git-am, this implies that am-based rebases will not always 115 successfully detect directory renames either (see the 'am' section 116 above). merged-based rebases (rebase -m) and cherry-pick-based rebases 117 (rebase -i) are not affected by this shortcoming, and fully support 118 directory rename detection.